summary refs log tree commit diff
path: root/crypto/src/cms/CMSSignedDataParser.cs
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2013-06-28 15:26:06 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2013-06-28 15:26:06 +0700
commit44288db4414158ac9b98a507b15e81d0d3c66ca6 (patch)
treeaa5ef88948ebb68ed6c8df81eb5da889641a9b50 /crypto/src/cms/CMSSignedDataParser.cs
parentSet up text/binary handling for existing file types (diff)
downloadBouncyCastle.NET-ed25519-44288db4414158ac9b98a507b15e81d0d3c66ca6.tar.xz
Initial import of old CVS repository
Diffstat (limited to 'crypto/src/cms/CMSSignedDataParser.cs')
-rw-r--r--crypto/src/cms/CMSSignedDataParser.cs455
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());
+		}
+	}
+}