diff --git a/Crypto/src/cms/CMSSignedDataParser.cs b/Crypto/src/cms/CMSSignedDataParser.cs
new file mode 100644
index 000000000..dd794ee77
--- /dev/null
+++ b/Crypto/src/cms/CMSSignedDataParser.cs
@@ -0,0 +1,455 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * Parsing class for an CMS Signed Data object from an input stream.
+ * <p>
+ * Note: that because we are in a streaming mode only one signer can be tried and it is important
+ * that the methods on the parser are called in the appropriate order.
+ * </p>
+ * <p>
+ * A simple example of usage for an encapsulated signature.
+ * </p>
+ * <p>
+ * Two notes: first, in the example below the validity of
+ * the certificate isn't verified, just the fact that one of the certs
+ * matches the given signer, and, second, because we are in a streaming
+ * mode the order of the operations is important.
+ * </p>
+ * <pre>
+ * CmsSignedDataParser sp = new CmsSignedDataParser(encapSigData);
+ *
+ * sp.GetSignedContent().Drain();
+ *
+ * IX509Store certs = sp.GetCertificates();
+ * SignerInformationStore signers = sp.GetSignerInfos();
+ *
+ * foreach (SignerInformation signer in signers.GetSigners())
+ * {
+ * ArrayList certList = new ArrayList(certs.GetMatches(signer.SignerID));
+ * X509Certificate cert = (X509Certificate) certList[0];
+ *
+ * Console.WriteLine("verify returns: " + signer.Verify(cert));
+ * }
+ * </pre>
+ * Note also: this class does not introduce buffering - if you are processing large files you should create
+ * the parser with:
+ * <pre>
+ * CmsSignedDataParser ep = new CmsSignedDataParser(new BufferedInputStream(encapSigData, bufSize));
+ * </pre>
+ * where bufSize is a suitably large buffer size.
+ */
+ public class CmsSignedDataParser
+ : CmsContentInfoParser
+ {
+ private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
+
+ private SignedDataParser _signedData;
+ private DerObjectIdentifier _signedContentType;
+ private CmsTypedStream _signedContent;
+ private IDictionary _digests;
+ private ISet _digestOids;
+
+ private SignerInformationStore _signerInfoStore;
+ private Asn1Set _certSet, _crlSet;
+ private bool _isCertCrlParsed;
+ private IX509Store _attributeStore;
+ private IX509Store _certificateStore;
+ private IX509Store _crlStore;
+
+ public CmsSignedDataParser(
+ byte[] sigBlock)
+ : this(new MemoryStream(sigBlock, false))
+ {
+ }
+
+ public CmsSignedDataParser(
+ CmsTypedStream signedContent,
+ byte[] sigBlock)
+ : this(signedContent, new MemoryStream(sigBlock, false))
+ {
+ }
+
+ /**
+ * base constructor - with encapsulated content
+ */
+ public CmsSignedDataParser(
+ Stream sigData)
+ : this(null, sigData)
+ {
+ }
+
+ /**
+ * base constructor
+ *
+ * @param signedContent the content that was signed.
+ * @param sigData the signature object.
+ */
+ public CmsSignedDataParser(
+ CmsTypedStream signedContent,
+ Stream sigData)
+ : base(sigData)
+ {
+ try
+ {
+ this._signedContent = signedContent;
+ this._signedData = SignedDataParser.GetInstance(this.contentInfo.GetContent(Asn1Tags.Sequence));
+ this._digests = Platform.CreateHashtable();
+ this._digestOids = new HashSet();
+
+ Asn1SetParser digAlgs = _signedData.GetDigestAlgorithms();
+ IAsn1Convertible o;
+
+ while ((o = digAlgs.ReadObject()) != null)
+ {
+ AlgorithmIdentifier id = AlgorithmIdentifier.GetInstance(o.ToAsn1Object());
+
+ try
+ {
+ string digestOid = id.ObjectID.Id;
+ string digestName = Helper.GetDigestAlgName(digestOid);
+
+ if (!this._digests.Contains(digestName))
+ {
+ this._digests[digestName] = Helper.GetDigestInstance(digestName);
+ this._digestOids.Add(digestOid);
+ }
+ }
+ catch (SecurityUtilityException)
+ {
+ // TODO Should do something other than ignore it
+ }
+ }
+
+ //
+ // If the message is simply a certificate chain message GetContent() may return null.
+ //
+ ContentInfoParser cont = _signedData.GetEncapContentInfo();
+ Asn1OctetStringParser octs = (Asn1OctetStringParser)
+ cont.GetContent(Asn1Tags.OctetString);
+
+ if (octs != null)
+ {
+ CmsTypedStream ctStr = new CmsTypedStream(
+ cont.ContentType.Id, octs.GetOctetStream());
+
+ if (_signedContent == null)
+ {
+ this._signedContent = ctStr;
+ }
+ else
+ {
+ //
+ // content passed in, need to read past empty encapsulated content info object if present
+ //
+ ctStr.Drain();
+ }
+ }
+
+ _signedContentType = _signedContent == null
+ ? cont.ContentType
+ : new DerObjectIdentifier(_signedContent.ContentType);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("io exception: " + e.Message, e);
+ }
+
+ if (_digests.Count < 1)
+ {
+ throw new CmsException("no digests could be created for message.");
+ }
+ }
+
+ /**
+ * Return the version number for the SignedData object
+ *
+ * @return the version number
+ */
+ public int Version
+ {
+ get { return _signedData.Version.Value.IntValue; }
+ }
+
+ public ISet DigestOids
+ {
+ get { return new HashSet(_digestOids); }
+ }
+
+ /**
+ * return the collection of signers that are associated with the
+ * signatures for the message.
+ * @throws CmsException
+ */
+ public SignerInformationStore GetSignerInfos()
+ {
+ if (_signerInfoStore == null)
+ {
+ PopulateCertCrlSets();
+
+ IList signerInfos = Platform.CreateArrayList();
+ IDictionary hashes = Platform.CreateHashtable();
+
+ foreach (object digestKey in _digests.Keys)
+ {
+ hashes[digestKey] = DigestUtilities.DoFinal(
+ (IDigest)_digests[digestKey]);
+ }
+
+ try
+ {
+ Asn1SetParser s = _signedData.GetSignerInfos();
+ IAsn1Convertible o;
+
+ while ((o = s.ReadObject()) != null)
+ {
+ SignerInfo info = SignerInfo.GetInstance(o.ToAsn1Object());
+ string digestName = Helper.GetDigestAlgName(
+ info.DigestAlgorithm.ObjectID.Id);
+
+ byte[] hash = (byte[]) hashes[digestName];
+
+ signerInfos.Add(new SignerInformation(info, _signedContentType, null, new BaseDigestCalculator(hash)));
+ }
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("io exception: " + e.Message, e);
+ }
+
+ _signerInfoStore = new SignerInformationStore(signerInfos);
+ }
+
+ return _signerInfoStore;
+ }
+
+ /**
+ * return a X509Store containing the attribute certificates, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of attribute certificates
+ * @exception org.bouncycastle.x509.NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetAttributeCertificates(
+ string type)
+ {
+ if (_attributeStore == null)
+ {
+ PopulateCertCrlSets();
+
+ _attributeStore = Helper.CreateAttributeStore(type, _certSet);
+ }
+
+ return _attributeStore;
+ }
+
+ /**
+ * return a X509Store containing the public key certificates, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of public key certificates
+ * @exception NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetCertificates(
+ string type)
+ {
+ if (_certificateStore == null)
+ {
+ PopulateCertCrlSets();
+
+ _certificateStore = Helper.CreateCertificateStore(type, _certSet);
+ }
+
+ return _certificateStore;
+ }
+
+ /**
+ * return a X509Store containing CRLs, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of CRLs
+ * @exception NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetCrls(
+ string type)
+ {
+ if (_crlStore == null)
+ {
+ PopulateCertCrlSets();
+
+ _crlStore = Helper.CreateCrlStore(type, _crlSet);
+ }
+
+ return _crlStore;
+ }
+
+ private void PopulateCertCrlSets()
+ {
+ if (_isCertCrlParsed)
+ return;
+
+ _isCertCrlParsed = true;
+
+ try
+ {
+ // care! Streaming - Must process the GetCertificates() result before calling GetCrls()
+ _certSet = GetAsn1Set(_signedData.GetCertificates());
+ _crlSet = GetAsn1Set(_signedData.GetCrls());
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("problem parsing cert/crl sets", e);
+ }
+ }
+
+ /// <summary>
+ /// Return the <c>DerObjectIdentifier</c> associated with the encapsulated
+ /// content info structure carried in the signed data.
+ /// </summary>
+ public DerObjectIdentifier SignedContentType
+ {
+ get { return _signedContentType; }
+ }
+
+ public CmsTypedStream GetSignedContent()
+ {
+ if (_signedContent == null)
+ {
+ return null;
+ }
+
+ Stream digStream = _signedContent.ContentStream;
+
+ foreach (IDigest digest in _digests.Values)
+ {
+ digStream = new DigestStream(digStream, digest, null);
+ }
+
+ return new CmsTypedStream(_signedContent.ContentType, digStream);
+ }
+
+ /**
+ * Replace the signerinformation store associated with the passed
+ * in message contained in the stream original with the new one passed in.
+ * You would probably only want to do this if you wanted to change the unsigned
+ * attributes associated with a signer, or perhaps delete one.
+ * <p>
+ * The output stream is returned unclosed.
+ * </p>
+ * @param original the signed data stream to be used as a base.
+ * @param signerInformationStore the new signer information store to use.
+ * @param out the stream to Write the new signed data object to.
+ * @return out.
+ */
+ public static Stream ReplaceSigners(
+ Stream original,
+ SignerInformationStore signerInformationStore,
+ Stream outStr)
+ {
+ // NB: SecureRandom would be ignored since using existing signatures only
+ CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+ CmsSignedDataParser parser = new CmsSignedDataParser(original);
+
+// gen.AddDigests(parser.DigestOids);
+ gen.AddSigners(signerInformationStore);
+
+ CmsTypedStream signedContent = parser.GetSignedContent();
+ bool encapsulate = (signedContent != null);
+ Stream contentOut = gen.Open(outStr, parser.SignedContentType.Id, encapsulate);
+ if (encapsulate)
+ {
+ Streams.PipeAll(signedContent.ContentStream, contentOut);
+ }
+
+ gen.AddAttributeCertificates(parser.GetAttributeCertificates("Collection"));
+ gen.AddCertificates(parser.GetCertificates("Collection"));
+ gen.AddCrls(parser.GetCrls("Collection"));
+
+// gen.AddSigners(parser.GetSignerInfos());
+
+ contentOut.Dispose();
+
+ return outStr;
+ }
+
+ /**
+ * Replace the certificate and CRL information associated with this
+ * CMSSignedData object with the new one passed in.
+ * <p>
+ * The output stream is returned unclosed.
+ * </p>
+ * @param original the signed data stream to be used as a base.
+ * @param certsAndCrls the new certificates and CRLs to be used.
+ * @param out the stream to Write the new signed data object to.
+ * @return out.
+ * @exception CmsException if there is an error processing the CertStore
+ */
+ public static Stream ReplaceCertificatesAndCrls(
+ Stream original,
+ IX509Store x509Certs,
+ IX509Store x509Crls,
+ IX509Store x509AttrCerts,
+ Stream outStr)
+ {
+ // NB: SecureRandom would be ignored since using existing signatures only
+ CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+ CmsSignedDataParser parser = new CmsSignedDataParser(original);
+
+ gen.AddDigests(parser.DigestOids);
+
+ CmsTypedStream signedContent = parser.GetSignedContent();
+ bool encapsulate = (signedContent != null);
+ Stream contentOut = gen.Open(outStr, parser.SignedContentType.Id, encapsulate);
+ if (encapsulate)
+ {
+ Streams.PipeAll(signedContent.ContentStream, contentOut);
+ }
+
+// gen.AddAttributeCertificates(parser.GetAttributeCertificates("Collection"));
+// gen.AddCertificates(parser.GetCertificates("Collection"));
+// gen.AddCrls(parser.GetCrls("Collection"));
+ if (x509AttrCerts != null)
+ gen.AddAttributeCertificates(x509AttrCerts);
+ if (x509Certs != null)
+ gen.AddCertificates(x509Certs);
+ if (x509Crls != null)
+ gen.AddCrls(x509Crls);
+
+ gen.AddSigners(parser.GetSignerInfos());
+
+ contentOut.Dispose();
+
+ return outStr;
+ }
+
+ private static Asn1Set GetAsn1Set(
+ Asn1SetParser asn1SetParser)
+ {
+ return asn1SetParser == null
+ ? null
+ : Asn1Set.GetInstance(asn1SetParser.ToAsn1Object());
+ }
+ }
+}
|