123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using LipingShare.LCLib.Asn1Processor;
- using System.Security.Cryptography;
-
- namespace UnityEngine.Purchasing.Security
- {
-
- internal class DistinguishedName
- {
- public string Country { get; set; }
- public string Organization { get; set; }
- public string OrganizationalUnit { get; set; }
- public string Dnq { get; set; }
- public string State { get; set; }
- public string CommonName { get; set; }
- public string SerialNumber { get; set; }
-
- public DistinguishedName(Asn1Node n)
- {
- /* Name:
- * SET
- * SEQ (attr)
- * Object Identifier
- * Printable String || UTF8String
- */
- if (n.MaskedTag == Asn1Tag.SEQUENCE)
- {
- for (int i = 0; i < n.ChildNodeCount; i++)
- {
- Asn1Node tt = n.GetChildNode(i);
- if (tt.MaskedTag != Asn1Tag.SET || tt.ChildNodeCount != 1)
- throw new InvalidX509Data();
-
- tt = tt.GetChildNode(0);
- if (tt.MaskedTag != Asn1Tag.SEQUENCE || tt.ChildNodeCount != 2)
- throw new InvalidX509Data();
-
- Asn1Node oi = tt.GetChildNode(0);
- Asn1Node txt = tt.GetChildNode(1);
-
- if (oi.MaskedTag != Asn1Tag.OBJECT_IDENTIFIER ||
- !(
- (txt.MaskedTag == Asn1Tag.PRINTABLE_STRING) ||
- (txt.MaskedTag == Asn1Tag.UTF8_STRING) ||
- (txt.MaskedTag == Asn1Tag.IA5_STRING)))
- {
- throw new InvalidX509Data();
- }
- var xoid = new LipingShare.LCLib.Asn1Processor.Oid();
- string oiName = xoid.Decode(oi.Data);
- var enc = new System.Text.UTF8Encoding();
-
- switch (oiName)
- {
- case "2.5.4.6": // countryName
- Country = enc.GetString(txt.Data);
- break;
- case "2.5.4.10": // organizationName
- Organization = enc.GetString(txt.Data);
- break;
- case "2.5.4.11": // organizationalUnit
- OrganizationalUnit = enc.GetString(txt.Data);
- break;
- case "2.5.4.3": // commonName
- CommonName = enc.GetString(txt.Data);
- break;
- case "2.5.4.5": // serial number
- SerialNumber = Asn1Util.ToHexString(txt.Data);
- break;
- case "2.5.4.46": // dnq
- Dnq = enc.GetString(txt.Data);
- break;
- case "2.5.4.8": // state
- State = enc.GetString(txt.Data);
- break;
- }
- }
- }
- }
-
- public bool Equals(DistinguishedName n2)
- {
- return this.Organization == n2.Organization &&
- this.OrganizationalUnit == n2.OrganizationalUnit &&
- this.Dnq == n2.Dnq &&
- this.Country == n2.Country &&
- this.State == n2.State &&
- this.CommonName == n2.CommonName;
- }
-
- public override string ToString()
- {
- return "CN: " + CommonName + "\n" +
- "ON: " + Organization + "\n" +
- "Unit Name: " + OrganizationalUnit + "\n" +
- "Country: " + Country;
- }
- }
-
- internal class X509Cert
- {
- public string SerialNumber { get; private set; }
- public DateTime ValidAfter { get; private set; }
- public DateTime ValidBefore { get; private set; }
- public RSAKey PubKey { get; private set; }
- public bool SelfSigned { get; private set; }
- public DistinguishedName Subject { get; private set; }
- public DistinguishedName Issuer { get; private set; }
- private Asn1Node TbsCertificate;
- public Asn1Node Signature { get; private set; }
- public byte[] rawTBSCertificate;
-
- public X509Cert(Asn1Node n)
- {
- ParseNode(n);
- }
-
- public X509Cert(byte[] data)
- {
- using (var stm = new System.IO.MemoryStream(data))
- {
- Asn1Parser parser = new Asn1Parser();
- parser.LoadData(stm);
- ParseNode(parser.RootNode);
- }
- }
-
- public bool CheckCertTime(DateTime time)
- {
- return time.CompareTo(ValidAfter) >= 0 && time.CompareTo(ValidBefore) <= 0;
- }
-
- public bool CheckSignature(X509Cert signer)
- {
- if (Issuer.Equals(signer.Subject))
- {
- return VerifySignatureWithSha256OrSha1(signer);
- }
- return false;
- }
-
- bool VerifySignatureWithSha256OrSha1(X509Cert signer)
- {
- if (signer.PubKey.VerifySha256(rawTBSCertificate, Signature.Data))
- {
- return true;
- }
-
- return signer.PubKey.VerifySha1(rawTBSCertificate, Signature.Data);
- }
-
- public bool CheckSignatureSha256(X509Cert signer)
- {
- if (Issuer.Equals(signer.Subject))
- {
- return signer.PubKey.VerifySha256(rawTBSCertificate, Signature.Data);
- }
-
- return false;
- }
-
- private void ParseNode(Asn1Node root)
- {
- if ((root.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE || root.ChildNodeCount != 3)
- throw new InvalidX509Data();
-
-
-
- // TBS cert
- TbsCertificate = root.GetChildNode(0);
- if (TbsCertificate.ChildNodeCount < 7)
- throw new InvalidX509Data();
-
- rawTBSCertificate = new byte[TbsCertificate.DataLength + 4];
- Array.Copy(root.Data, 0, rawTBSCertificate, 0, rawTBSCertificate.Length);
-
- // get the serial number
- Asn1Node sn = TbsCertificate.GetChildNode(1);
- if ((sn.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
- throw new InvalidX509Data();
- SerialNumber = Asn1Util.ToHexString(sn.Data);
-
- // get the issuer
- Issuer = new DistinguishedName(TbsCertificate.GetChildNode(3));
-
- // get the subject
- Subject = new DistinguishedName(TbsCertificate.GetChildNode(5));
-
- // get the dates
- Asn1Node validTimes = TbsCertificate.GetChildNode(4);
- if ((validTimes.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE || validTimes.ChildNodeCount != 2)
- throw new InvalidX509Data();
- ValidAfter = ParseTime(validTimes.GetChildNode(0));
- ValidBefore = ParseTime(validTimes.GetChildNode(1));
-
- // is this self signed?
- SelfSigned = Subject.Equals(Issuer);
-
- // get the pub key
- PubKey = new RSAKey(TbsCertificate.GetChildNode(6));
-
- // set the tbs cert & signature data for signature verification
- Signature = root.GetChildNode(2);
- }
-
- /**
- * According to rfc5280, time should be specified in GMT:
- * https://tools.ietf.org/html/rfc5280#section-4.1.2.5
- */
- private DateTime ParseTime(Asn1Node n)
- {
- string time = (new System.Text.UTF8Encoding()).GetString(n.Data);
-
- if (!(time.Length == 13 || time.Length == 15))
- throw new InvalidTimeFormat();
-
- // only accept Zulu time
- if (time[time.Length - 1] != 'Z')
- throw new InvalidTimeFormat();
-
- int curIdx = 0;
-
- int year = 0;
- if (time.Length == 13)
- {
- year = Int32.Parse(time.Substring(0, 2));
- if (year >= 50)
- year += 1900;
- else if (year < 50)
- year += 2000;
- curIdx += 2;
- }
- else
- {
- year = Int32.Parse(time.Substring(0, 4));
- curIdx += 4;
- }
-
- int month = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
- int dom = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
- int hour = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
- int min = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
- int secs = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
-
- return new DateTime(year, month, dom, hour, min, secs, DateTimeKind.Utc);
- }
- }
-
- /// <summary>
- /// An IAP Security exception indicating some invalid time format.
- /// </summary>
- public class InvalidTimeFormat : IAPSecurityException { }
-
- /// <summary>
- /// An IAP Security exception indicating some invalid data for X509 certification checks.
- /// </summary>
- public class InvalidX509Data : IAPSecurityException { }
- }
|