diff options
Diffstat (limited to 'crypto/src/cms/CMSSignedDataParser.cs')
-rw-r--r-- | crypto/src/cms/CMSSignedDataParser.cs | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/crypto/src/cms/CMSSignedDataParser.cs b/crypto/src/cms/CMSSignedDataParser.cs new file mode 100644 index 000000000..35a125196 --- /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.Close(); + + 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.Close(); + + return outStr; + } + + private static Asn1Set GetAsn1Set( + Asn1SetParser asn1SetParser) + { + return asn1SetParser == null + ? null + : Asn1Set.GetInstance(asn1SetParser.ToAsn1Object()); + } + } +} |