PKCS7.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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 = null;
  37. foreach (var c in certChain)
  38. {
  39. if (c.SerialNumber == sinfo.IssuerSerialNumber)
  40. {
  41. signCert = c;
  42. break;
  43. }
  44. }
  45. if (signCert != null && signCert.PubKey != null)
  46. {
  47. ok = ok && signCert.CheckCertTime(certificateCreationTime);
  48. if (IsStoreKitSimulatorData())
  49. {
  50. ok = ok && signCert.PubKey.Verify256(data.GetChildNode(0).Data, sinfo.EncryptedDigest);
  51. ok = ok && ValidateStorekitSimulatorCertRoot(cert, signCert);
  52. }
  53. else
  54. {
  55. ok = ok && signCert.PubKey.Verify(data.Data, sinfo.EncryptedDigest);
  56. ok = ok && ValidateChain(cert, signCert, certificateCreationTime);
  57. }
  58. }
  59. }
  60. return ok && sinfos.Count > 0;
  61. }
  62. return false;
  63. }
  64. bool IsStoreKitSimulatorData()
  65. {
  66. return data.IsIndefiniteLength && data.ChildNodeCount == 1;
  67. }
  68. bool ValidateStorekitSimulatorCertRoot(X509Cert root, X509Cert cert)
  69. {
  70. return cert.CheckSignature256(root);
  71. }
  72. private bool ValidateChain(X509Cert root, X509Cert cert, DateTime certificateCreationTime)
  73. {
  74. if (cert.Issuer.Equals(root.Subject))
  75. return cert.CheckSignature(root);
  76. /**
  77. * TODO: improve this logic
  78. */
  79. foreach (var c in certChain)
  80. {
  81. if (c != cert && c.Subject.Equals(cert.Issuer) && c.CheckCertTime(certificateCreationTime))
  82. {
  83. if (c.Issuer.Equals(root.Subject) && c.SerialNumber == root.SerialNumber)
  84. return c.CheckSignature(root);
  85. else
  86. {
  87. // cert was issued by c
  88. if (cert.CheckSignature(c))
  89. return ValidateChain(root, c, certificateCreationTime);
  90. }
  91. }
  92. }
  93. return false;
  94. }
  95. private void CheckStructure()
  96. {
  97. validStructure = false;
  98. if ((root.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SEQUENCE &&
  99. root.ChildNodeCount == 2)
  100. {
  101. Asn1Node tt = root.GetChildNode(0);
  102. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.OBJECT_IDENTIFIER ||
  103. tt.GetDataStr(false) != "1.2.840.113549.1.7.2")
  104. {
  105. throw new InvalidPKCS7Data();
  106. }
  107. tt = root.GetChildNode(1); // [0]
  108. if (tt.ChildNodeCount != 1)
  109. throw new InvalidPKCS7Data();
  110. int curChild = 0;
  111. tt = tt.GetChildNode(curChild++); // Seq
  112. if (tt.ChildNodeCount < 4 || (tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE)
  113. throw new InvalidPKCS7Data();
  114. Asn1Node tt2 = tt.GetChildNode(0); // version
  115. if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
  116. throw new InvalidPKCS7Data();
  117. tt2 = tt.GetChildNode(curChild++); // digest algo
  118. // TODO: check algo
  119. if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SET)
  120. throw new InvalidPKCS7Data();
  121. tt2 = tt.GetChildNode(curChild++); // pkcs7 data
  122. if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE && tt2.ChildNodeCount != 2)
  123. throw new InvalidPKCS7Data();
  124. data = tt2.GetChildNode(1).GetChildNode(0);
  125. if (tt.ChildNodeCount == 5)
  126. {
  127. // cert chain, this is optional
  128. certChain = new List<X509Cert>();
  129. tt2 = tt.GetChildNode(curChild++);
  130. if (tt2.ChildNodeCount == 0)
  131. throw new InvalidPKCS7Data();
  132. for (int i = 0; i < tt2.ChildNodeCount; i++)
  133. {
  134. certChain.Add(new X509Cert(tt2.GetChildNode(i)));
  135. }
  136. }
  137. tt2 = tt.GetChildNode(curChild++); // signer's info
  138. if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SET || tt2.ChildNodeCount == 0)
  139. throw new InvalidPKCS7Data();
  140. sinfos = new List<SignerInfo>();
  141. for (int i = 0; i < tt2.ChildNodeCount; i++)
  142. {
  143. sinfos.Add(new SignerInfo(tt2.GetChildNode(i)));
  144. }
  145. validStructure = true;
  146. }
  147. }
  148. }
  149. internal class SignerInfo
  150. {
  151. public int Version { get; private set; }
  152. public string IssuerSerialNumber { get; private set; }
  153. public byte[] EncryptedDigest { get; private set; }
  154. public SignerInfo(Asn1Node n)
  155. {
  156. if (n.ChildNodeCount != 5)
  157. throw new InvalidPKCS7Data();
  158. Asn1Node tt;
  159. // version
  160. tt = n.GetChildNode(0);
  161. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
  162. throw new InvalidPKCS7Data();
  163. Version = tt.Data[0];
  164. if (Version != 1 || tt.Data.Length != 1)
  165. throw new UnsupportedSignerInfoVersion();
  166. // get the issuer SN
  167. tt = n.GetChildNode(1);
  168. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE || tt.ChildNodeCount != 2)
  169. throw new InvalidPKCS7Data();
  170. tt = tt.GetChildNode(1);
  171. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
  172. throw new InvalidPKCS7Data();
  173. IssuerSerialNumber = Asn1Util.ToHexString(tt.Data);
  174. // get the data
  175. tt = n.GetChildNode(4);
  176. if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.OCTET_STRING)
  177. throw new InvalidPKCS7Data();
  178. EncryptedDigest = tt.Data;
  179. }
  180. }
  181. /// <summary>
  182. /// An IAP Security exception indicating some invalid data for PKCS7 checks.
  183. /// </summary>
  184. public class InvalidPKCS7Data : IAPSecurityException { }
  185. /// <summary>
  186. /// An IAP Security exception indicating unsupported signer information.
  187. /// </summary>
  188. public class UnsupportedSignerInfoVersion : IAPSecurityException { }
  189. }