CMSSignedDataParser.cs 13 KB

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