Sin descripción
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PKCS7.cs 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using LipingShare.LCLib.Asn1Processor;
  5. using System.Security.Cryptography;
  6. namespace UnityEngine.Purchasing.Security
  7. {
  8. internal class PKCS7
  9. {
  10. private Asn1Node root;
  11. public Asn1Node data { get; private set; }
  12. public List<SignerInfo> sinfos { get; private set; }
  13. public List<X509Cert> certChain { get; private set; }
  14. private bool validStructure;
  15. public static PKCS7 Load(byte[] data)
  16. {
  17. using (var stm = new System.IO.MemoryStream(data))
  18. {
  19. Asn1Parser parser = new Asn1Parser();
  20. parser.LoadData(stm);
  21. return new PKCS7(parser.RootNode);
  22. }
  23. }
  24. public PKCS7(Asn1Node node)
  25. {
  26. this.root = node;
  27. CheckStructure();
  28. }
  29. public bool Verify(X509Cert cert, DateTime certificateCreationTime)
  30. {
  31. if (validStructure)
  32. {
  33. bool ok = true;
  34. foreach (var sinfo in sinfos)
  35. {
  36. X509Cert signCert = FindSignCert(sinfo);
  37. if (signCert != null && signCert.PubKey != null)
  38. {
  39. ok = ok && signCert.CheckCertTime(certificateCreationTime);
  40. if (IsStoreKitSimulatorData())
  41. {
  42. ok = ok && signCert.PubKey.VerifySha256(data.GetChildNode(0).Data, sinfo.EncryptedDigest);
  43. ok = ok && ValidateStoreKitSimulatorCertRoot(cert, signCert);
  44. }
  45. else
  46. {
  47. ok = ok && VerifyPublicKeyWithSha256OrSha1(signCert, sinfo);
  48. ok = ok && ValidateChain(cert, signCert, certificateCreationTime);
  49. }
  50. }
  51. }
  52. return ok && sinfos.Count > 0;
  53. }
  54. return false;
  55. }
  56. X509Cert FindSignCert(SignerInfo sinfo)
  57. {
  58. foreach (var cert in certChain)
  59. {
  60. if (cert.SerialNumber == sinfo.IssuerSerialNumber)
  61. {
  62. return cert;
  63. }
  64. }
  65. return null;
  66. }
  67. bool IsStoreKitSimulatorData()
  68. {
  69. return data.IsIndefiniteLength && data.ChildNodeCount == 1;
  70. }
  71. bool VerifyPublicKeyWithSha256OrSha1(X509Cert signCert, SignerInfo sinfo)
  72. {
  73. if (signCert.PubKey.VerifySha256(data.Data, sinfo.EncryptedDigest))
  74. {
  75. return true;
  76. }
  77. return signCert.PubKey.VerifySha1(data.Data, sinfo.EncryptedDigest);
  78. }
  79. static bool ValidateStoreKitSimulatorCertRoot(X509Cert root, X509Cert cert)
  80. {
  81. return cert.CheckSignatureSha256(root);
  82. }
  83. private bool ValidateChain(X509Cert root, X509Cert cert, DateTime certificateCreationTime)
  84. {
  85. if (cert.Issuer.Equals(root.Subject))
  86. {
  87. return cert.CheckSignature(root);
  88. }
  89. /**
  90. * TODO: improve this logic
  91. */
  92. foreach (var c in certChain)
  93. {
  94. if (c != cert && c.Subject.Equals(cert.Issuer) && c.CheckCertTime(certificateCreationTime))
  95. {
  96. if (c.Issuer.Equals(root.Subject) && c.SerialNumber == root.SerialNumber)
  97. {
  98. return c.CheckSignature(root);
  99. }
  100. else
  101. {
  102. // cert was issued by c
  103. if (cert.CheckSignature(c))
  104. {
  105. return ValidateChain(root, c, certificateCreationTime);
  106. }
  107. }
  108. }
  109. }
  110. return false;
  111. }
  112. private void CheckStructure()
  113. {
  114. validStructure = false;
  115. if ((root.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SEQUENCE &&
  116. root.ChildNodeCount == 2)
  117. {
  118. Asn1Node tt = root.GetChildNode(0);
  119. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.OBJECT_IDENTIFIER ||
  120. tt.GetDataStr(false) != "1.2.840.113549.1.7.2")
  121. {
  122. throw new InvalidPKCS7Data();
  123. }
  124. tt = root.GetChildNode(1); // [0]
  125. if (tt.ChildNodeCount != 1)
  126. throw new InvalidPKCS7Data();
  127. int curChild = 0;
  128. tt = tt.GetChildNode(curChild++); // Seq
  129. if (tt.ChildNodeCount < 4 || (tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE)
  130. throw new InvalidPKCS7Data();
  131. Asn1Node tt2 = tt.GetChildNode(0); // version
  132. if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
  133. throw new InvalidPKCS7Data();
  134. tt2 = tt.GetChildNode(curChild++); // digest algo
  135. // TODO: check algo
  136. if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SET)
  137. throw new InvalidPKCS7Data();
  138. tt2 = tt.GetChildNode(curChild++); // pkcs7 data
  139. if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE && tt2.ChildNodeCount != 2)
  140. throw new InvalidPKCS7Data();
  141. data = tt2.GetChildNode(1).GetChildNode(0);
  142. if (tt.ChildNodeCount == 5)
  143. {
  144. // cert chain, this is optional
  145. certChain = new List<X509Cert>();
  146. tt2 = tt.GetChildNode(curChild++);
  147. if (tt2.ChildNodeCount == 0)
  148. throw new InvalidPKCS7Data();
  149. for (int i = 0; i < tt2.ChildNodeCount; i++)
  150. {
  151. certChain.Add(new X509Cert(tt2.GetChildNode(i)));
  152. }
  153. }
  154. tt2 = tt.GetChildNode(curChild++); // signer's info
  155. if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SET || tt2.ChildNodeCount == 0)
  156. throw new InvalidPKCS7Data();
  157. sinfos = new List<SignerInfo>();
  158. for (int i = 0; i < tt2.ChildNodeCount; i++)
  159. {
  160. sinfos.Add(new SignerInfo(tt2.GetChildNode(i)));
  161. }
  162. validStructure = true;
  163. }
  164. }
  165. }
  166. internal class SignerInfo
  167. {
  168. public int Version { get; private set; }
  169. public string IssuerSerialNumber { get; private set; }
  170. public byte[] EncryptedDigest { get; private set; }
  171. public SignerInfo(Asn1Node n)
  172. {
  173. if (n.ChildNodeCount != 5)
  174. throw new InvalidPKCS7Data();
  175. Asn1Node tt;
  176. // version
  177. tt = n.GetChildNode(0);
  178. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
  179. throw new InvalidPKCS7Data();
  180. Version = tt.Data[0];
  181. if (Version != 1 || tt.Data.Length != 1)
  182. throw new UnsupportedSignerInfoVersion();
  183. // get the issuer SN
  184. tt = n.GetChildNode(1);
  185. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE || tt.ChildNodeCount != 2)
  186. throw new InvalidPKCS7Data();
  187. tt = tt.GetChildNode(1);
  188. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
  189. throw new InvalidPKCS7Data();
  190. IssuerSerialNumber = Asn1Util.ToHexString(tt.Data);
  191. // get the data
  192. tt = n.GetChildNode(4);
  193. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.OCTET_STRING)
  194. throw new InvalidPKCS7Data();
  195. EncryptedDigest = tt.Data;
  196. }
  197. }
  198. /// <summary>
  199. /// An IAP Security exception indicating some invalid data for PKCS7 checks.
  200. /// </summary>
  201. public class InvalidPKCS7Data : IAPSecurityException { }
  202. /// <summary>
  203. /// An IAP Security exception indicating unsupported signer information.
  204. /// </summary>
  205. public class UnsupportedSignerInfoVersion : IAPSecurityException { }
  206. }