123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using LipingShare.LCLib.Asn1Processor;
- using System.Security.Cryptography;
-
- namespace UnityEngine.Purchasing.Security
- {
- internal class PKCS7
- {
- private Asn1Node root;
- public Asn1Node data { get; private set; }
- public List<SignerInfo> sinfos { get; private set; }
- public List<X509Cert> certChain { get; private set; }
-
- private bool validStructure;
-
- public static PKCS7 Load(byte[] data)
- {
- using (var stm = new System.IO.MemoryStream(data))
- {
- Asn1Parser parser = new Asn1Parser();
- parser.LoadData(stm);
- return new PKCS7(parser.RootNode);
- }
- }
-
- public PKCS7(Asn1Node node)
- {
- this.root = node;
- CheckStructure();
- }
-
- public bool Verify(X509Cert cert, DateTime certificateCreationTime)
- {
- if (validStructure)
- {
- bool ok = true;
- foreach (var sinfo in sinfos)
- {
- X509Cert signCert = FindSignCert(sinfo);
-
- if (signCert != null && signCert.PubKey != null)
- {
- ok = ok && signCert.CheckCertTime(certificateCreationTime);
-
- if (IsStoreKitSimulatorData())
- {
- ok = ok && signCert.PubKey.VerifySha256(data.GetChildNode(0).Data, sinfo.EncryptedDigest);
- ok = ok && ValidateStoreKitSimulatorCertRoot(cert, signCert);
- }
- else
- {
- ok = ok && VerifyPublicKeyWithSha256OrSha1(signCert, sinfo);
- ok = ok && ValidateChain(cert, signCert, certificateCreationTime);
- }
- }
- }
-
- return ok && sinfos.Count > 0;
- }
-
- return false;
- }
-
- X509Cert FindSignCert(SignerInfo sinfo)
- {
- foreach (var cert in certChain)
- {
- if (cert.SerialNumber == sinfo.IssuerSerialNumber)
- {
- return cert;
- }
- }
-
- return null;
- }
-
- bool IsStoreKitSimulatorData()
- {
- return data.IsIndefiniteLength && data.ChildNodeCount == 1;
- }
-
- bool VerifyPublicKeyWithSha256OrSha1(X509Cert signCert, SignerInfo sinfo)
- {
- if (signCert.PubKey.VerifySha256(data.Data, sinfo.EncryptedDigest))
- {
- return true;
- }
-
- return signCert.PubKey.VerifySha1(data.Data, sinfo.EncryptedDigest);
- }
-
- static bool ValidateStoreKitSimulatorCertRoot(X509Cert root, X509Cert cert)
- {
- return cert.CheckSignatureSha256(root);
- }
-
- private bool ValidateChain(X509Cert root, X509Cert cert, DateTime certificateCreationTime)
- {
- if (cert.Issuer.Equals(root.Subject))
- {
- return cert.CheckSignature(root);
- }
-
- /**
- * TODO: improve this logic
- */
- foreach (var c in certChain)
- {
- if (c != cert && c.Subject.Equals(cert.Issuer) && c.CheckCertTime(certificateCreationTime))
- {
- if (c.Issuer.Equals(root.Subject) && c.SerialNumber == root.SerialNumber)
- {
- return c.CheckSignature(root);
- }
- else
- {
- // cert was issued by c
- if (cert.CheckSignature(c))
- {
- return ValidateChain(root, c, certificateCreationTime);
- }
- }
- }
- }
-
- return false;
- }
-
- private void CheckStructure()
- {
- validStructure = false;
- if ((root.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SEQUENCE &&
- root.ChildNodeCount == 2)
- {
- Asn1Node tt = root.GetChildNode(0);
- if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.OBJECT_IDENTIFIER ||
- tt.GetDataStr(false) != "1.2.840.113549.1.7.2")
- {
- throw new InvalidPKCS7Data();
- }
-
- tt = root.GetChildNode(1); // [0]
- if (tt.ChildNodeCount != 1)
- throw new InvalidPKCS7Data();
- int curChild = 0;
-
- tt = tt.GetChildNode(curChild++); // Seq
- if (tt.ChildNodeCount < 4 || (tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE)
- throw new InvalidPKCS7Data();
-
- Asn1Node tt2 = tt.GetChildNode(0); // version
- if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
- throw new InvalidPKCS7Data();
-
- tt2 = tt.GetChildNode(curChild++); // digest algo
- // TODO: check algo
- if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SET)
- throw new InvalidPKCS7Data();
-
- tt2 = tt.GetChildNode(curChild++); // pkcs7 data
- if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE && tt2.ChildNodeCount != 2)
- throw new InvalidPKCS7Data();
- data = tt2.GetChildNode(1).GetChildNode(0);
-
- if (tt.ChildNodeCount == 5)
- {
- // cert chain, this is optional
- certChain = new List<X509Cert>();
- tt2 = tt.GetChildNode(curChild++);
- if (tt2.ChildNodeCount == 0)
- throw new InvalidPKCS7Data();
- for (int i = 0; i < tt2.ChildNodeCount; i++)
- {
- certChain.Add(new X509Cert(tt2.GetChildNode(i)));
- }
- }
-
- tt2 = tt.GetChildNode(curChild++); // signer's info
- if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SET || tt2.ChildNodeCount == 0)
- throw new InvalidPKCS7Data();
-
- sinfos = new List<SignerInfo>();
- for (int i = 0; i < tt2.ChildNodeCount; i++)
- {
- sinfos.Add(new SignerInfo(tt2.GetChildNode(i)));
- }
- validStructure = true;
- }
- }
- }
-
- internal class SignerInfo
- {
- public int Version { get; private set; }
- public string IssuerSerialNumber { get; private set; }
- public byte[] EncryptedDigest { get; private set; }
-
- public SignerInfo(Asn1Node n)
- {
- if (n.ChildNodeCount != 5)
- throw new InvalidPKCS7Data();
- Asn1Node tt;
-
- // version
- tt = n.GetChildNode(0);
- if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
- throw new InvalidPKCS7Data();
- Version = tt.Data[0];
- if (Version != 1 || tt.Data.Length != 1)
- throw new UnsupportedSignerInfoVersion();
-
- // get the issuer SN
- tt = n.GetChildNode(1);
- if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE || tt.ChildNodeCount != 2)
- throw new InvalidPKCS7Data();
- tt = tt.GetChildNode(1);
- if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
- throw new InvalidPKCS7Data();
- IssuerSerialNumber = Asn1Util.ToHexString(tt.Data);
-
- // get the data
- tt = n.GetChildNode(4);
- if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.OCTET_STRING)
- throw new InvalidPKCS7Data();
- EncryptedDigest = tt.Data;
- }
- }
-
- /// <summary>
- /// An IAP Security exception indicating some invalid data for PKCS7 checks.
- /// </summary>
- public class InvalidPKCS7Data : IAPSecurityException { }
-
- /// <summary>
- /// An IAP Security exception indicating unsupported signer information.
- /// </summary>
- public class UnsupportedSignerInfoVersion : IAPSecurityException { }
- }
|