CMSSignedDataStreamGenerator.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Cms;
  9. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509;
  10. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  11. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.IO;
  12. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  13. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
  14. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security.Certificates;
  15. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  16. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Collections;
  17. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO;
  18. using BestHTTP.SecureProtocol.Org.BouncyCastle.X509;
  19. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Cms
  20. {
  21. /**
  22. * General class for generating a pkcs7-signature message stream.
  23. * <p>
  24. * A simple example of usage.
  25. * </p>
  26. * <pre>
  27. * IX509Store certs...
  28. * CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
  29. *
  30. * gen.AddSigner(privateKey, cert, CmsSignedDataStreamGenerator.DIGEST_SHA1);
  31. *
  32. * gen.AddCertificates(certs);
  33. *
  34. * Stream sigOut = gen.Open(bOut);
  35. *
  36. * sigOut.Write(Encoding.UTF8.GetBytes("Hello World!"));
  37. *
  38. * sigOut.Close();
  39. * </pre>
  40. */
  41. public class CmsSignedDataStreamGenerator
  42. : CmsSignedGenerator
  43. {
  44. private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
  45. private readonly IList _signerInfs = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList();
  46. private readonly ISet _messageDigestOids = new HashSet();
  47. private readonly IDictionary _messageDigests = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable();
  48. private readonly IDictionary _messageHashes = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable();
  49. private bool _messageDigestsLocked;
  50. private int _bufferSize;
  51. private class DigestAndSignerInfoGeneratorHolder
  52. {
  53. internal readonly ISignerInfoGenerator signerInf;
  54. internal readonly string digestOID;
  55. internal DigestAndSignerInfoGeneratorHolder(ISignerInfoGenerator signerInf, String digestOID)
  56. {
  57. this.signerInf = signerInf;
  58. this.digestOID = digestOID;
  59. }
  60. internal AlgorithmIdentifier DigestAlgorithm
  61. {
  62. get { return new AlgorithmIdentifier(new DerObjectIdentifier(this.digestOID), DerNull.Instance); }
  63. }
  64. }
  65. private class SignerInfoGeneratorImpl : ISignerInfoGenerator
  66. {
  67. private readonly CmsSignedDataStreamGenerator outer;
  68. private readonly SignerIdentifier _signerIdentifier;
  69. private readonly string _digestOID;
  70. private readonly string _encOID;
  71. private readonly CmsAttributeTableGenerator _sAttr;
  72. private readonly CmsAttributeTableGenerator _unsAttr;
  73. private readonly string _encName;
  74. private readonly ISigner _sig;
  75. internal SignerInfoGeneratorImpl(
  76. CmsSignedDataStreamGenerator outer,
  77. AsymmetricKeyParameter key,
  78. SignerIdentifier signerIdentifier,
  79. string digestOID,
  80. string encOID,
  81. CmsAttributeTableGenerator sAttr,
  82. CmsAttributeTableGenerator unsAttr)
  83. {
  84. this.outer = outer;
  85. _signerIdentifier = signerIdentifier;
  86. _digestOID = digestOID;
  87. _encOID = encOID;
  88. _sAttr = sAttr;
  89. _unsAttr = unsAttr;
  90. _encName = Helper.GetEncryptionAlgName(_encOID);
  91. string digestName = Helper.GetDigestAlgName(_digestOID);
  92. string signatureName = digestName + "with" + _encName;
  93. if (_sAttr != null)
  94. {
  95. _sig = Helper.GetSignatureInstance(signatureName);
  96. }
  97. else
  98. {
  99. // Note: Need to use raw signatures here since we have already calculated the digest
  100. if (_encName.Equals("RSA"))
  101. {
  102. _sig = Helper.GetSignatureInstance("RSA");
  103. }
  104. else if (_encName.Equals("DSA"))
  105. {
  106. _sig = Helper.GetSignatureInstance("NONEwithDSA");
  107. }
  108. // TODO Add support for raw PSS
  109. // else if (_encName.equals("RSAandMGF1"))
  110. // {
  111. // _sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEWITHRSAPSS", _sigProvider);
  112. // try
  113. // {
  114. // // Init the params this way to avoid having a 'raw' version of each PSS algorithm
  115. // Signature sig2 = CMSSignedHelper.INSTANCE.getSignatureInstance(signatureName, _sigProvider);
  116. // PSSParameterSpec spec = (PSSParameterSpec)sig2.getParameters().getParameterSpec(PSSParameterSpec.class);
  117. // _sig.setParameter(spec);
  118. // }
  119. // catch (Exception e)
  120. // {
  121. // throw new SignatureException("algorithm: " + _encName + " could not be configured.");
  122. // }
  123. // }
  124. else
  125. {
  126. throw new SignatureException("algorithm: " + _encName + " not supported in base signatures.");
  127. }
  128. }
  129. _sig.Init(true, new ParametersWithRandom(key, outer.rand));
  130. }
  131. public SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm,
  132. byte[] calculatedDigest)
  133. {
  134. try
  135. {
  136. string digestName = Helper.GetDigestAlgName(_digestOID);
  137. string signatureName = digestName + "with" + _encName;
  138. // AlgorithmIdentifier digAlgId = DigestAlgorithmID;
  139. //
  140. // byte[] hash = (byte[])outer._messageHashes[Helper.GetDigestAlgName(this._digestOID)];
  141. // outer._digests[_digestOID] = hash.Clone();
  142. byte[] bytesToSign = calculatedDigest;
  143. /* RFC 3852 5.4
  144. * The result of the message digest calculation process depends on
  145. * whether the signedAttrs field is present. When the field is absent,
  146. * the result is just the message digest of the content as described
  147. *
  148. * above. When the field is present, however, the result is the message
  149. * digest of the complete DER encoding of the SignedAttrs value
  150. * contained in the signedAttrs field.
  151. */
  152. Asn1Set signedAttr = null;
  153. if (_sAttr != null)
  154. {
  155. IDictionary parameters = outer.GetBaseParameters(contentType, digestAlgorithm, calculatedDigest);
  156. // Asn1.Cms.AttributeTable signed = _sAttr.GetAttributes(Collections.unmodifiableMap(parameters));
  157. Asn1.Cms.AttributeTable signed = _sAttr.GetAttributes(parameters);
  158. if (contentType == null) //counter signature
  159. {
  160. if (signed != null && signed[CmsAttributes.ContentType] != null)
  161. {
  162. IDictionary tmpSigned = signed.ToDictionary();
  163. tmpSigned.Remove(CmsAttributes.ContentType);
  164. signed = new Asn1.Cms.AttributeTable(tmpSigned);
  165. }
  166. }
  167. signedAttr = outer.GetAttributeSet(signed);
  168. // sig must be composed from the DER encoding.
  169. bytesToSign = signedAttr.GetEncoded(Asn1Encodable.Der);
  170. }
  171. else
  172. {
  173. // Note: Need to use raw signatures here since we have already calculated the digest
  174. if (_encName.Equals("RSA"))
  175. {
  176. DigestInfo dInfo = new DigestInfo(digestAlgorithm, calculatedDigest);
  177. bytesToSign = dInfo.GetEncoded(Asn1Encodable.Der);
  178. }
  179. }
  180. _sig.BlockUpdate(bytesToSign, 0, bytesToSign.Length);
  181. byte[] sigBytes = _sig.GenerateSignature();
  182. Asn1Set unsignedAttr = null;
  183. if (_unsAttr != null)
  184. {
  185. IDictionary parameters = outer.GetBaseParameters(
  186. contentType, digestAlgorithm, calculatedDigest);
  187. parameters[CmsAttributeTableParameter.Signature] = sigBytes.Clone();
  188. // Asn1.Cms.AttributeTable unsigned = _unsAttr.getAttributes(Collections.unmodifiableMap(parameters));
  189. Asn1.Cms.AttributeTable unsigned = _unsAttr.GetAttributes(parameters);
  190. unsignedAttr = outer.GetAttributeSet(unsigned);
  191. }
  192. // TODO[RSAPSS] Need the ability to specify non-default parameters
  193. Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName);
  194. AlgorithmIdentifier digestEncryptionAlgorithm = Helper.GetEncAlgorithmIdentifier(
  195. new DerObjectIdentifier(_encOID), sigX509Parameters);
  196. return new SignerInfo(_signerIdentifier, digestAlgorithm,
  197. signedAttr, digestEncryptionAlgorithm, new DerOctetString(sigBytes), unsignedAttr);
  198. }
  199. catch (IOException e)
  200. {
  201. throw new CmsStreamException("encoding error.", e);
  202. }
  203. catch (SignatureException e)
  204. {
  205. throw new CmsStreamException("error creating signature.", e);
  206. }
  207. }
  208. }
  209. public CmsSignedDataStreamGenerator()
  210. {
  211. }
  212. /// <summary>Constructor allowing specific source of randomness</summary>
  213. /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
  214. public CmsSignedDataStreamGenerator(
  215. SecureRandom rand)
  216. : base(rand)
  217. {
  218. }
  219. /**
  220. * Set the underlying string size for encapsulated data
  221. *
  222. * @param bufferSize length of octet strings to buffer the data.
  223. */
  224. public void SetBufferSize(
  225. int bufferSize)
  226. {
  227. _bufferSize = bufferSize;
  228. }
  229. public void AddDigests(
  230. params string[] digestOids)
  231. {
  232. AddDigests((IEnumerable) digestOids);
  233. }
  234. public void AddDigests(
  235. IEnumerable digestOids)
  236. {
  237. foreach (string digestOid in digestOids)
  238. {
  239. ConfigureDigest(digestOid);
  240. }
  241. }
  242. /**
  243. * add a signer - no attributes other than the default ones will be
  244. * provided here.
  245. * @throws NoSuchAlgorithmException
  246. * @throws InvalidKeyException
  247. */
  248. public void AddSigner(
  249. AsymmetricKeyParameter privateKey,
  250. X509Certificate cert,
  251. string digestOid)
  252. {
  253. AddSigner(privateKey, cert, digestOid,
  254. new DefaultSignedAttributeTableGenerator(), null);
  255. }
  256. /**
  257. * add a signer, specifying the digest encryption algorithm - no attributes other than the default ones will be
  258. * provided here.
  259. * @throws NoSuchProviderException
  260. * @throws NoSuchAlgorithmException
  261. * @throws InvalidKeyException
  262. */
  263. public void AddSigner(
  264. AsymmetricKeyParameter privateKey,
  265. X509Certificate cert,
  266. string encryptionOid,
  267. string digestOid)
  268. {
  269. AddSigner(privateKey, cert, encryptionOid, digestOid,
  270. new DefaultSignedAttributeTableGenerator(),
  271. (CmsAttributeTableGenerator)null);
  272. }
  273. /**
  274. * add a signer with extra signed/unsigned attributes.
  275. * @throws NoSuchAlgorithmException
  276. * @throws InvalidKeyException
  277. */
  278. public void AddSigner(
  279. AsymmetricKeyParameter privateKey,
  280. X509Certificate cert,
  281. string digestOid,
  282. Asn1.Cms.AttributeTable signedAttr,
  283. Asn1.Cms.AttributeTable unsignedAttr)
  284. {
  285. AddSigner(privateKey, cert, digestOid,
  286. new DefaultSignedAttributeTableGenerator(signedAttr),
  287. new SimpleAttributeTableGenerator(unsignedAttr));
  288. }
  289. /**
  290. * add a signer with extra signed/unsigned attributes - specifying digest
  291. * encryption algorithm.
  292. * @throws NoSuchProviderException
  293. * @throws NoSuchAlgorithmException
  294. * @throws InvalidKeyException
  295. */
  296. public void AddSigner(
  297. AsymmetricKeyParameter privateKey,
  298. X509Certificate cert,
  299. string encryptionOid,
  300. string digestOid,
  301. Asn1.Cms.AttributeTable signedAttr,
  302. Asn1.Cms.AttributeTable unsignedAttr)
  303. {
  304. AddSigner(privateKey, cert, encryptionOid, digestOid,
  305. new DefaultSignedAttributeTableGenerator(signedAttr),
  306. new SimpleAttributeTableGenerator(unsignedAttr));
  307. }
  308. public void AddSigner(
  309. AsymmetricKeyParameter privateKey,
  310. X509Certificate cert,
  311. string digestOid,
  312. CmsAttributeTableGenerator signedAttrGenerator,
  313. CmsAttributeTableGenerator unsignedAttrGenerator)
  314. {
  315. AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOid), digestOid,
  316. signedAttrGenerator, unsignedAttrGenerator);
  317. }
  318. public void AddSigner(
  319. AsymmetricKeyParameter privateKey,
  320. X509Certificate cert,
  321. string encryptionOid,
  322. string digestOid,
  323. CmsAttributeTableGenerator signedAttrGenerator,
  324. CmsAttributeTableGenerator unsignedAttrGenerator)
  325. {
  326. DoAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOid, digestOid,
  327. signedAttrGenerator, unsignedAttrGenerator);
  328. }
  329. /**
  330. * add a signer - no attributes other than the default ones will be
  331. * provided here.
  332. * @throws NoSuchAlgorithmException
  333. * @throws InvalidKeyException
  334. */
  335. public void AddSigner(
  336. AsymmetricKeyParameter privateKey,
  337. byte[] subjectKeyID,
  338. string digestOid)
  339. {
  340. AddSigner(privateKey, subjectKeyID, digestOid, new DefaultSignedAttributeTableGenerator(),
  341. (CmsAttributeTableGenerator)null);
  342. }
  343. /**
  344. * add a signer - no attributes other than the default ones will be
  345. * provided here.
  346. * @throws NoSuchProviderException
  347. * @throws NoSuchAlgorithmException
  348. * @throws InvalidKeyException
  349. */
  350. public void AddSigner(
  351. AsymmetricKeyParameter privateKey,
  352. byte[] subjectKeyID,
  353. string encryptionOid,
  354. string digestOid)
  355. {
  356. AddSigner(privateKey, subjectKeyID, encryptionOid, digestOid,
  357. new DefaultSignedAttributeTableGenerator(),
  358. (CmsAttributeTableGenerator)null);
  359. }
  360. /**
  361. * add a signer with extra signed/unsigned attributes.
  362. * @throws NoSuchAlgorithmException
  363. * @throws InvalidKeyException
  364. */
  365. public void AddSigner(
  366. AsymmetricKeyParameter privateKey,
  367. byte[] subjectKeyID,
  368. string digestOid,
  369. Asn1.Cms.AttributeTable signedAttr,
  370. Asn1.Cms.AttributeTable unsignedAttr)
  371. {
  372. AddSigner(privateKey, subjectKeyID, digestOid,
  373. new DefaultSignedAttributeTableGenerator(signedAttr),
  374. new SimpleAttributeTableGenerator(unsignedAttr));
  375. }
  376. public void AddSigner(
  377. AsymmetricKeyParameter privateKey,
  378. byte[] subjectKeyID,
  379. string digestOid,
  380. CmsAttributeTableGenerator signedAttrGenerator,
  381. CmsAttributeTableGenerator unsignedAttrGenerator)
  382. {
  383. AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOid),
  384. digestOid, signedAttrGenerator, unsignedAttrGenerator);
  385. }
  386. public void AddSigner(
  387. AsymmetricKeyParameter privateKey,
  388. byte[] subjectKeyID,
  389. string encryptionOid,
  390. string digestOid,
  391. CmsAttributeTableGenerator signedAttrGenerator,
  392. CmsAttributeTableGenerator unsignedAttrGenerator)
  393. {
  394. DoAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOid, digestOid,
  395. signedAttrGenerator, unsignedAttrGenerator);
  396. }
  397. private void DoAddSigner(
  398. AsymmetricKeyParameter privateKey,
  399. SignerIdentifier signerIdentifier,
  400. string encryptionOid,
  401. string digestOid,
  402. CmsAttributeTableGenerator signedAttrGenerator,
  403. CmsAttributeTableGenerator unsignedAttrGenerator)
  404. {
  405. ConfigureDigest(digestOid);
  406. SignerInfoGeneratorImpl signerInf = new SignerInfoGeneratorImpl(this, privateKey,
  407. signerIdentifier, digestOid, encryptionOid, signedAttrGenerator, unsignedAttrGenerator);
  408. _signerInfs.Add(new DigestAndSignerInfoGeneratorHolder(signerInf, digestOid));
  409. }
  410. internal override void AddSignerCallback(
  411. SignerInformation si)
  412. {
  413. // FIXME If there were parameters in si.DigestAlgorithmID.Parameters, they are lost
  414. // NB: Would need to call FixAlgID on the DigestAlgorithmID
  415. // For precalculated signers, just need to register the algorithm, not configure a digest
  416. RegisterDigestOid(si.DigestAlgorithmID.Algorithm.Id);
  417. }
  418. /**
  419. * generate a signed object that for a CMS Signed Data object
  420. */
  421. public Stream Open(
  422. Stream outStream)
  423. {
  424. return Open(outStream, false);
  425. }
  426. /**
  427. * generate a signed object that for a CMS Signed Data
  428. * object - if encapsulate is true a copy
  429. * of the message will be included in the signature with the
  430. * default content type "data".
  431. */
  432. public Stream Open(
  433. Stream outStream,
  434. bool encapsulate)
  435. {
  436. return Open(outStream, Data, encapsulate);
  437. }
  438. /**
  439. * generate a signed object that for a CMS Signed Data
  440. * object using the given provider - if encapsulate is true a copy
  441. * of the message will be included in the signature with the
  442. * default content type "data". If dataOutputStream is non null the data
  443. * being signed will be written to the stream as it is processed.
  444. * @param out stream the CMS object is to be written to.
  445. * @param encapsulate true if data should be encapsulated.
  446. * @param dataOutputStream output stream to copy the data being signed to.
  447. */
  448. public Stream Open(
  449. Stream outStream,
  450. bool encapsulate,
  451. Stream dataOutputStream)
  452. {
  453. return Open(outStream, Data, encapsulate, dataOutputStream);
  454. }
  455. /**
  456. * generate a signed object that for a CMS Signed Data
  457. * object - if encapsulate is true a copy
  458. * of the message will be included in the signature. The content type
  459. * is set according to the OID represented by the string signedContentType.
  460. */
  461. public Stream Open(
  462. Stream outStream,
  463. string signedContentType,
  464. bool encapsulate)
  465. {
  466. return Open(outStream, signedContentType, encapsulate, null);
  467. }
  468. /**
  469. * generate a signed object that for a CMS Signed Data
  470. * object using the given provider - if encapsulate is true a copy
  471. * of the message will be included in the signature. The content type
  472. * is set according to the OID represented by the string signedContentType.
  473. * @param out stream the CMS object is to be written to.
  474. * @param signedContentType OID for data to be signed.
  475. * @param encapsulate true if data should be encapsulated.
  476. * @param dataOutputStream output stream to copy the data being signed to.
  477. */
  478. public Stream Open(
  479. Stream outStream,
  480. string signedContentType,
  481. bool encapsulate,
  482. Stream dataOutputStream)
  483. {
  484. if (outStream == null)
  485. throw new ArgumentNullException("outStream");
  486. if (!outStream.CanWrite)
  487. throw new ArgumentException("Expected writeable stream", "outStream");
  488. if (dataOutputStream != null && !dataOutputStream.CanWrite)
  489. throw new ArgumentException("Expected writeable stream", "dataOutputStream");
  490. _messageDigestsLocked = true;
  491. //
  492. // ContentInfo
  493. //
  494. BerSequenceGenerator sGen = new BerSequenceGenerator(outStream);
  495. sGen.AddObject(CmsObjectIdentifiers.SignedData);
  496. //
  497. // Signed Data
  498. //
  499. BerSequenceGenerator sigGen = new BerSequenceGenerator(
  500. sGen.GetRawOutputStream(), 0, true);
  501. bool isCounterSignature = (signedContentType == null);
  502. DerObjectIdentifier contentTypeOid = isCounterSignature
  503. ? null
  504. : new DerObjectIdentifier(signedContentType);
  505. sigGen.AddObject(CalculateVersion(contentTypeOid));
  506. Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
  507. foreach (string digestOid in _messageDigestOids)
  508. {
  509. digestAlgs.Add(
  510. new AlgorithmIdentifier(new DerObjectIdentifier(digestOid), DerNull.Instance));
  511. }
  512. {
  513. byte[] tmp = new DerSet(digestAlgs).GetEncoded();
  514. sigGen.GetRawOutputStream().Write(tmp, 0, tmp.Length);
  515. }
  516. BerSequenceGenerator eiGen = new BerSequenceGenerator(sigGen.GetRawOutputStream());
  517. eiGen.AddObject(contentTypeOid);
  518. // If encapsulating, add the data as an octet string in the sequence
  519. Stream encapStream = encapsulate
  520. ? CmsUtilities.CreateBerOctetOutputStream(eiGen.GetRawOutputStream(), 0, true, _bufferSize)
  521. : null;
  522. // Also send the data to 'dataOutputStream' if necessary
  523. Stream teeStream = GetSafeTeeOutputStream(dataOutputStream, encapStream);
  524. // Let all the digests see the data as it is written
  525. Stream digStream = AttachDigestsToOutputStream(_messageDigests.Values, teeStream);
  526. return new CmsSignedDataOutputStream(this, digStream, signedContentType, sGen, sigGen, eiGen);
  527. }
  528. private void RegisterDigestOid(
  529. string digestOid)
  530. {
  531. if (_messageDigestsLocked)
  532. {
  533. if (!_messageDigestOids.Contains(digestOid))
  534. throw new InvalidOperationException("Cannot register new digest OIDs after the data stream is opened");
  535. }
  536. else
  537. {
  538. _messageDigestOids.Add(digestOid);
  539. }
  540. }
  541. private void ConfigureDigest(
  542. string digestOid)
  543. {
  544. RegisterDigestOid(digestOid);
  545. string digestName = Helper.GetDigestAlgName(digestOid);
  546. IDigest dig = (IDigest)_messageDigests[digestName];
  547. if (dig == null)
  548. {
  549. if (_messageDigestsLocked)
  550. throw new InvalidOperationException("Cannot configure new digests after the data stream is opened");
  551. dig = Helper.GetDigestInstance(digestName);
  552. _messageDigests[digestName] = dig;
  553. }
  554. }
  555. // TODO Make public?
  556. internal void Generate(
  557. Stream outStream,
  558. string eContentType,
  559. bool encapsulate,
  560. Stream dataOutputStream,
  561. CmsProcessable content)
  562. {
  563. Stream signedOut = Open(outStream, eContentType, encapsulate, dataOutputStream);
  564. if (content != null)
  565. {
  566. content.Write(signedOut);
  567. }
  568. BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.Dispose(signedOut);
  569. }
  570. // RFC3852, section 5.1:
  571. // IF ((certificates is present) AND
  572. // (any certificates with a type of other are present)) OR
  573. // ((crls is present) AND
  574. // (any crls with a type of other are present))
  575. // THEN version MUST be 5
  576. // ELSE
  577. // IF (certificates is present) AND
  578. // (any version 2 attribute certificates are present)
  579. // THEN version MUST be 4
  580. // ELSE
  581. // IF ((certificates is present) AND
  582. // (any version 1 attribute certificates are present)) OR
  583. // (any SignerInfo structures are version 3) OR
  584. // (encapContentInfo eContentType is other than id-data)
  585. // THEN version MUST be 3
  586. // ELSE version MUST be 1
  587. //
  588. private DerInteger CalculateVersion(
  589. DerObjectIdentifier contentOid)
  590. {
  591. bool otherCert = false;
  592. bool otherCrl = false;
  593. bool attrCertV1Found = false;
  594. bool attrCertV2Found = false;
  595. if (_certs != null)
  596. {
  597. foreach (object obj in _certs)
  598. {
  599. if (obj is Asn1TaggedObject)
  600. {
  601. Asn1TaggedObject tagged = (Asn1TaggedObject) obj;
  602. if (tagged.TagNo == 1)
  603. {
  604. attrCertV1Found = true;
  605. }
  606. else if (tagged.TagNo == 2)
  607. {
  608. attrCertV2Found = true;
  609. }
  610. else if (tagged.TagNo == 3)
  611. {
  612. otherCert = true;
  613. break;
  614. }
  615. }
  616. }
  617. }
  618. if (otherCert)
  619. {
  620. return new DerInteger(5);
  621. }
  622. if (_crls != null)
  623. {
  624. foreach (object obj in _crls)
  625. {
  626. if (obj is Asn1TaggedObject)
  627. {
  628. otherCrl = true;
  629. break;
  630. }
  631. }
  632. }
  633. if (otherCrl)
  634. {
  635. return new DerInteger(5);
  636. }
  637. if (attrCertV2Found)
  638. {
  639. return new DerInteger(4);
  640. }
  641. if (attrCertV1Found || !CmsObjectIdentifiers.Data.Equals(contentOid) || CheckForVersion3(_signers))
  642. {
  643. return new DerInteger(3);
  644. }
  645. return new DerInteger(1);
  646. }
  647. private bool CheckForVersion3(
  648. IList signerInfos)
  649. {
  650. foreach (SignerInformation si in signerInfos)
  651. {
  652. SignerInfo s = SignerInfo.GetInstance(si.ToSignerInfo());
  653. if (s.Version.IntValueExact == 3)
  654. {
  655. return true;
  656. }
  657. }
  658. return false;
  659. }
  660. private static Stream AttachDigestsToOutputStream(ICollection digests, Stream s)
  661. {
  662. Stream result = s;
  663. foreach (IDigest digest in digests)
  664. {
  665. result = GetSafeTeeOutputStream(result, new DigestSink(digest));
  666. }
  667. return result;
  668. }
  669. private static Stream GetSafeOutputStream(Stream s)
  670. {
  671. if (s == null)
  672. return new NullOutputStream();
  673. return s;
  674. }
  675. private static Stream GetSafeTeeOutputStream(Stream s1, Stream s2)
  676. {
  677. if (s1 == null)
  678. return GetSafeOutputStream(s2);
  679. if (s2 == null)
  680. return GetSafeOutputStream(s1);
  681. return new TeeOutputStream(s1, s2);
  682. }
  683. private class CmsSignedDataOutputStream
  684. : BaseOutputStream
  685. {
  686. private readonly CmsSignedDataStreamGenerator outer;
  687. private Stream _out;
  688. private DerObjectIdentifier _contentOID;
  689. private BerSequenceGenerator _sGen;
  690. private BerSequenceGenerator _sigGen;
  691. private BerSequenceGenerator _eiGen;
  692. public CmsSignedDataOutputStream(
  693. CmsSignedDataStreamGenerator outer,
  694. Stream outStream,
  695. string contentOID,
  696. BerSequenceGenerator sGen,
  697. BerSequenceGenerator sigGen,
  698. BerSequenceGenerator eiGen)
  699. {
  700. this.outer = outer;
  701. _out = outStream;
  702. _contentOID = new DerObjectIdentifier(contentOID);
  703. _sGen = sGen;
  704. _sigGen = sigGen;
  705. _eiGen = eiGen;
  706. }
  707. public override void WriteByte(
  708. byte b)
  709. {
  710. _out.WriteByte(b);
  711. }
  712. public override void Write(
  713. byte[] bytes,
  714. int off,
  715. int len)
  716. {
  717. _out.Write(bytes, off, len);
  718. }
  719. #if PORTABLE || NETFX_CORE
  720. protected override void Dispose(bool disposing)
  721. {
  722. if (disposing)
  723. {
  724. DoClose();
  725. }
  726. base.Dispose(disposing);
  727. }
  728. #else
  729. public override void Close()
  730. {
  731. DoClose();
  732. base.Close();
  733. }
  734. #endif
  735. private void DoClose()
  736. {
  737. BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.Dispose(_out);
  738. // TODO Parent context(s) should really be be closed explicitly
  739. _eiGen.Close();
  740. outer._digests.Clear(); // clear the current preserved digest state
  741. if (outer._certs.Count > 0)
  742. {
  743. Asn1Set certs = outer.UseDerForCerts
  744. ? CmsUtilities.CreateDerSetFromList(outer._certs)
  745. : CmsUtilities.CreateBerSetFromList(outer._certs);
  746. WriteToGenerator(_sigGen, new BerTaggedObject(false, 0, certs));
  747. }
  748. if (outer._crls.Count > 0)
  749. {
  750. Asn1Set crls = outer.UseDerForCrls
  751. ? CmsUtilities.CreateDerSetFromList(outer._crls)
  752. : CmsUtilities.CreateBerSetFromList(outer._crls);
  753. WriteToGenerator(_sigGen, new BerTaggedObject(false, 1, crls));
  754. }
  755. //
  756. // Calculate the digest hashes
  757. //
  758. foreach (DictionaryEntry de in outer._messageDigests)
  759. {
  760. outer._messageHashes.Add(de.Key, DigestUtilities.DoFinal((IDigest)de.Value));
  761. }
  762. // TODO If the digest OIDs for precalculated signers weren't mixed in with
  763. // the others, we could fill in outer._digests here, instead of SignerInfoGenerator.Generate
  764. //
  765. // collect all the SignerInfo objects
  766. //
  767. Asn1EncodableVector signerInfos = new Asn1EncodableVector();
  768. //
  769. // add the generated SignerInfo objects
  770. //
  771. {
  772. foreach (DigestAndSignerInfoGeneratorHolder holder in outer._signerInfs)
  773. {
  774. AlgorithmIdentifier digestAlgorithm = holder.DigestAlgorithm;
  775. byte[] calculatedDigest = (byte[])outer._messageHashes[
  776. Helper.GetDigestAlgName(holder.digestOID)];
  777. outer._digests[holder.digestOID] = calculatedDigest.Clone();
  778. signerInfos.Add(holder.signerInf.Generate(_contentOID, digestAlgorithm, calculatedDigest));
  779. }
  780. }
  781. //
  782. // add the precalculated SignerInfo objects.
  783. //
  784. {
  785. foreach (SignerInformation signer in outer._signers)
  786. {
  787. // TODO Verify the content type and calculated digest match the precalculated SignerInfo
  788. // if (!signer.ContentType.Equals(_contentOID))
  789. // {
  790. // // TODO The precalculated content type did not match - error?
  791. // }
  792. //
  793. // byte[] calculatedDigest = (byte[])outer._digests[signer.DigestAlgOid];
  794. // if (calculatedDigest == null)
  795. // {
  796. // // TODO We can't confirm this digest because we didn't calculate it - error?
  797. // }
  798. // else
  799. // {
  800. // if (!Arrays.AreEqual(signer.GetContentDigest(), calculatedDigest))
  801. // {
  802. // // TODO The precalculated digest did not match - error?
  803. // }
  804. // }
  805. signerInfos.Add(signer.ToSignerInfo());
  806. }
  807. }
  808. WriteToGenerator(_sigGen, new DerSet(signerInfos));
  809. _sigGen.Close();
  810. _sGen.Close();
  811. }
  812. private static void WriteToGenerator(
  813. Asn1Generator ag,
  814. Asn1Encodable ae)
  815. {
  816. byte[] encoded = ae.GetEncoded();
  817. ag.GetRawOutputStream().Write(encoded, 0, encoded.Length);
  818. }
  819. }
  820. }
  821. }
  822. #pragma warning restore
  823. #endif