CMSSignedDataParser.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.Cms;
  8. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509;
  9. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  10. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.IO;
  11. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Security;
  12. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  13. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities.Collections;
  14. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO;
  15. using Best.HTTP.SecureProtocol.Org.BouncyCastle.X509;
  16. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Cms
  17. {
  18. /**
  19. * Parsing class for an CMS Signed Data object from an input stream.
  20. * <p>
  21. * Note: that because we are in a streaming mode only one signer can be tried and it is important
  22. * that the methods on the parser are called in the appropriate order.
  23. * </p>
  24. * <p>
  25. * A simple example of usage for an encapsulated signature.
  26. * </p>
  27. * <p>
  28. * Two notes: first, in the example below the validity of
  29. * the certificate isn't verified, just the fact that one of the certs
  30. * matches the given signer, and, second, because we are in a streaming
  31. * mode the order of the operations is important.
  32. * </p>
  33. * <pre>
  34. * CmsSignedDataParser sp = new CmsSignedDataParser(encapSigData);
  35. *
  36. * sp.GetSignedContent().Drain();
  37. *
  38. * IX509Store certs = sp.GetCertificates();
  39. * SignerInformationStore signers = sp.GetSignerInfos();
  40. *
  41. * foreach (SignerInformation signer in signers.GetSigners())
  42. * {
  43. * ArrayList certList = new ArrayList(certs.GetMatches(signer.SignerID));
  44. * X509Certificate cert = (X509Certificate) certList[0];
  45. *
  46. * Console.WriteLine("verify returns: " + signer.Verify(cert));
  47. * }
  48. * </pre>
  49. * Note also: this class does not introduce buffering - if you are processing large files you should create
  50. * the parser with:
  51. * <pre>
  52. * CmsSignedDataParser ep = new CmsSignedDataParser(new BufferedInputStream(encapSigData, bufSize));
  53. * </pre>
  54. * where bufSize is a suitably large buffer size.
  55. */
  56. public class CmsSignedDataParser
  57. : CmsContentInfoParser
  58. {
  59. private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
  60. private SignedDataParser _signedData;
  61. private DerObjectIdentifier _signedContentType;
  62. private CmsTypedStream _signedContent;
  63. private IDictionary<string, IDigest> m_digests;
  64. private HashSet<string> _digestOids;
  65. private SignerInformationStore _signerInfoStore;
  66. private Asn1Set _certSet, _crlSet;
  67. private bool _isCertCrlParsed;
  68. public CmsSignedDataParser(
  69. byte[] sigBlock)
  70. : this(new MemoryStream(sigBlock, false))
  71. {
  72. }
  73. public CmsSignedDataParser(
  74. CmsTypedStream signedContent,
  75. byte[] sigBlock)
  76. : this(signedContent, new MemoryStream(sigBlock, false))
  77. {
  78. }
  79. /**
  80. * base constructor - with encapsulated content
  81. */
  82. public CmsSignedDataParser(
  83. Stream sigData)
  84. : this(null, sigData)
  85. {
  86. }
  87. /**
  88. * base constructor
  89. *
  90. * @param signedContent the content that was signed.
  91. * @param sigData the signature object.
  92. */
  93. public CmsSignedDataParser(
  94. CmsTypedStream signedContent,
  95. Stream sigData)
  96. : base(sigData)
  97. {
  98. try
  99. {
  100. this._signedContent = signedContent;
  101. this._signedData = SignedDataParser.GetInstance(this.contentInfo.GetContent(Asn1Tags.Sequence));
  102. this.m_digests = new Dictionary<string, IDigest>(StringComparer.OrdinalIgnoreCase);
  103. this._digestOids = new HashSet<string>();
  104. Asn1SetParser digAlgs = _signedData.GetDigestAlgorithms();
  105. IAsn1Convertible o;
  106. while ((o = digAlgs.ReadObject()) != null)
  107. {
  108. AlgorithmIdentifier id = AlgorithmIdentifier.GetInstance(o.ToAsn1Object());
  109. try
  110. {
  111. string digestOid = id.Algorithm.Id;
  112. string digestName = Helper.GetDigestAlgName(digestOid);
  113. if (!this.m_digests.ContainsKey(digestName))
  114. {
  115. this.m_digests[digestName] = Helper.GetDigestInstance(digestName);
  116. this._digestOids.Add(digestOid);
  117. }
  118. }
  119. catch (SecurityUtilityException)
  120. {
  121. // TODO Should do something other than ignore it
  122. }
  123. }
  124. //
  125. // If the message is simply a certificate chain message GetContent() may return null.
  126. //
  127. ContentInfoParser cont = _signedData.GetEncapContentInfo();
  128. Asn1OctetStringParser octs = (Asn1OctetStringParser)
  129. cont.GetContent(Asn1Tags.OctetString);
  130. if (octs != null)
  131. {
  132. CmsTypedStream ctStr = new CmsTypedStream(
  133. cont.ContentType.Id, octs.GetOctetStream());
  134. if (_signedContent == null)
  135. {
  136. this._signedContent = ctStr;
  137. }
  138. else
  139. {
  140. //
  141. // content passed in, need to read past empty encapsulated content info object if present
  142. //
  143. ctStr.Drain();
  144. }
  145. }
  146. _signedContentType = _signedContent == null
  147. ? cont.ContentType
  148. : new DerObjectIdentifier(_signedContent.ContentType);
  149. }
  150. catch (IOException e)
  151. {
  152. throw new CmsException("io exception: " + e.Message, e);
  153. }
  154. }
  155. /**
  156. * Return the version number for the SignedData object
  157. *
  158. * @return the version number
  159. */
  160. public int Version
  161. {
  162. get { return _signedData.Version.IntValueExact; }
  163. }
  164. public ISet<string> DigestOids
  165. {
  166. get { return new HashSet<string>(_digestOids); }
  167. }
  168. /**
  169. * return the collection of signers that are associated with the
  170. * signatures for the message.
  171. * @throws CmsException
  172. */
  173. public SignerInformationStore GetSignerInfos()
  174. {
  175. if (_signerInfoStore == null)
  176. {
  177. PopulateCertCrlSets();
  178. var signerInfos = new List<SignerInformation>();
  179. var hashes = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
  180. foreach (var digest in m_digests)
  181. {
  182. hashes[digest.Key] = DigestUtilities.DoFinal(digest.Value);
  183. }
  184. try
  185. {
  186. Asn1SetParser s = _signedData.GetSignerInfos();
  187. IAsn1Convertible o;
  188. while ((o = s.ReadObject()) != null)
  189. {
  190. SignerInfo info = SignerInfo.GetInstance(o.ToAsn1Object());
  191. string digestName = Helper.GetDigestAlgName(info.DigestAlgorithm.Algorithm.Id);
  192. byte[] hash = hashes[digestName];
  193. signerInfos.Add(new SignerInformation(info, _signedContentType, null, new BaseDigestCalculator(hash)));
  194. }
  195. }
  196. catch (IOException e)
  197. {
  198. throw new CmsException("io exception: " + e.Message, e);
  199. }
  200. _signerInfoStore = new SignerInformationStore(signerInfos);
  201. }
  202. return _signerInfoStore;
  203. }
  204. /**
  205. * return a X509Store containing the attribute certificates, if any, contained
  206. * in this message.
  207. *
  208. * @param type type of store to create
  209. * @return a store of attribute certificates
  210. * @exception org.bouncycastle.x509.NoSuchStoreException if the store type isn't available.
  211. * @exception CmsException if a general exception prevents creation of the X509Store
  212. */
  213. public IStore<X509V2AttributeCertificate> GetAttributeCertificates()
  214. {
  215. PopulateCertCrlSets();
  216. return Helper.GetAttributeCertificates(_certSet);
  217. }
  218. /**
  219. * return a X509Store containing the public key certificates, if any, contained
  220. * in this message.
  221. *
  222. * @param type type of store to create
  223. * @return a store of public key certificates
  224. * @exception NoSuchStoreException if the store type isn't available.
  225. * @exception CmsException if a general exception prevents creation of the X509Store
  226. */
  227. public IStore<X509Certificate> GetCertificates()
  228. {
  229. PopulateCertCrlSets();
  230. return Helper.GetCertificates(_certSet);
  231. }
  232. /**
  233. * return a X509Store containing CRLs, if any, contained
  234. * in this message.
  235. *
  236. * @param type type of store to create
  237. * @return a store of CRLs
  238. * @exception NoSuchStoreException if the store type isn't available.
  239. * @exception CmsException if a general exception prevents creation of the X509Store
  240. */
  241. public IStore<X509Crl> GetCrls()
  242. {
  243. PopulateCertCrlSets();
  244. return Helper.GetCrls(_crlSet);
  245. }
  246. public IStore<Asn1Encodable> GetOtherRevInfos(DerObjectIdentifier otherRevInfoFormat)
  247. {
  248. PopulateCertCrlSets();
  249. return Helper.GetOtherRevInfos(_crlSet, otherRevInfoFormat);
  250. }
  251. private void PopulateCertCrlSets()
  252. {
  253. if (_isCertCrlParsed)
  254. return;
  255. _isCertCrlParsed = true;
  256. try
  257. {
  258. // care! Streaming - Must process the GetCertificates() result before calling GetCrls()
  259. _certSet = GetAsn1Set(_signedData.GetCertificates());
  260. _crlSet = GetAsn1Set(_signedData.GetCrls());
  261. }
  262. catch (IOException e)
  263. {
  264. throw new CmsException("problem parsing cert/crl sets", e);
  265. }
  266. }
  267. /// <summary>
  268. /// Return the <c>DerObjectIdentifier</c> associated with the encapsulated
  269. /// content info structure carried in the signed data.
  270. /// </summary>
  271. public DerObjectIdentifier SignedContentType
  272. {
  273. get { return _signedContentType; }
  274. }
  275. public CmsTypedStream GetSignedContent()
  276. {
  277. if (_signedContent == null)
  278. {
  279. return null;
  280. }
  281. Stream digStream = _signedContent.ContentStream;
  282. foreach (var digest in m_digests.Values)
  283. {
  284. digStream = new DigestStream(digStream, digest, null);
  285. }
  286. return new CmsTypedStream(_signedContent.ContentType, digStream);
  287. }
  288. /**
  289. * Replace the signerinformation store associated with the passed
  290. * in message contained in the stream original with the new one passed in.
  291. * You would probably only want to do this if you wanted to change the unsigned
  292. * attributes associated with a signer, or perhaps delete one.
  293. * <p>
  294. * The output stream is returned unclosed.
  295. * </p>
  296. * @param original the signed data stream to be used as a base.
  297. * @param signerInformationStore the new signer information store to use.
  298. * @param out the stream to Write the new signed data object to.
  299. * @return out.
  300. */
  301. public static Stream ReplaceSigners(
  302. Stream original,
  303. SignerInformationStore signerInformationStore,
  304. Stream outStr)
  305. {
  306. // NB: SecureRandom would be ignored since using existing signatures only
  307. CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
  308. CmsSignedDataParser parser = new CmsSignedDataParser(original);
  309. // gen.AddDigests(parser.DigestOids);
  310. gen.AddSigners(signerInformationStore);
  311. CmsTypedStream signedContent = parser.GetSignedContent();
  312. bool encapsulate = (signedContent != null);
  313. Stream contentOut = gen.Open(outStr, parser.SignedContentType.Id, encapsulate);
  314. if (encapsulate)
  315. {
  316. Streams.PipeAll(signedContent.ContentStream, contentOut);
  317. }
  318. gen.AddAttributeCertificates(parser.GetAttributeCertificates());
  319. gen.AddCertificates(parser.GetCertificates());
  320. gen.AddCrls(parser.GetCrls());
  321. // gen.AddSigners(parser.GetSignerInfos());
  322. contentOut.Dispose();
  323. return outStr;
  324. }
  325. /**
  326. * Replace the certificate and CRL information associated with this
  327. * CMSSignedData object with the new one passed in.
  328. * <p>
  329. * The output stream is returned unclosed.
  330. * </p>
  331. * @param original the signed data stream to be used as a base.
  332. * @param certsAndCrls the new certificates and CRLs to be used.
  333. * @param out the stream to Write the new signed data object to.
  334. * @return out.
  335. * @exception CmsException if there is an error processing the CertStore
  336. */
  337. public static Stream ReplaceCertificatesAndCrls(Stream original, IStore<X509Certificate> x509Certs,
  338. IStore<X509Crl> x509Crls, IStore<X509V2AttributeCertificate> x509AttrCerts, Stream outStr)
  339. {
  340. // NB: SecureRandom would be ignored since using existing signatures only
  341. CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
  342. CmsSignedDataParser parser = new CmsSignedDataParser(original);
  343. gen.AddDigests(parser.DigestOids);
  344. CmsTypedStream signedContent = parser.GetSignedContent();
  345. bool encapsulate = (signedContent != null);
  346. Stream contentOut = gen.Open(outStr, parser.SignedContentType.Id, encapsulate);
  347. if (encapsulate)
  348. {
  349. Streams.PipeAll(signedContent.ContentStream, contentOut);
  350. }
  351. if (x509AttrCerts != null)
  352. {
  353. gen.AddAttributeCertificates(x509AttrCerts);
  354. }
  355. if (x509Certs != null)
  356. {
  357. gen.AddCertificates(x509Certs);
  358. }
  359. if (x509Crls != null)
  360. {
  361. gen.AddCrls(x509Crls);
  362. }
  363. gen.AddSigners(parser.GetSignerInfos());
  364. contentOut.Dispose();
  365. return outStr;
  366. }
  367. private static Asn1Set GetAsn1Set(
  368. Asn1SetParser asn1SetParser)
  369. {
  370. return asn1SetParser == null
  371. ? null
  372. : Asn1Set.GetInstance(asn1SetParser.ToAsn1Object());
  373. }
  374. }
  375. }
  376. #pragma warning restore
  377. #endif