CMSSignedDataStreamGenerator.cs 29 KB

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