summary refs log tree commit diff
path: root/crypto/src/cms/SignerInformation.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/SignerInformation.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/SignerInformation.cs')
-rw-r--r--crypto/src/cms/SignerInformation.cs758
1 files changed, 758 insertions, 0 deletions
diff --git a/crypto/src/cms/SignerInformation.cs b/crypto/src/cms/SignerInformation.cs
new file mode 100644
index 000000000..20af29a50
--- /dev/null
+++ b/crypto/src/cms/SignerInformation.cs
@@ -0,0 +1,758 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+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.Engines;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+	/**
+	* an expanded SignerInfo block from a CMS Signed message
+	*/
+	public class SignerInformation
+	{
+		private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
+
+		private SignerID			sid;
+		private SignerInfo			info;
+		private AlgorithmIdentifier	digestAlgorithm;
+		private AlgorithmIdentifier	encryptionAlgorithm;
+		private readonly Asn1Set	signedAttributeSet;
+		private readonly Asn1Set	unsignedAttributeSet;
+		private CmsProcessable		content;
+		private byte[]				signature;
+		private DerObjectIdentifier	contentType;
+		private IDigestCalculator	digestCalculator;
+		private byte[]				resultDigest;
+
+		// Derived
+		private Asn1.Cms.AttributeTable	signedAttributeTable;
+		private Asn1.Cms.AttributeTable	unsignedAttributeTable;
+		private readonly bool isCounterSignature;
+
+		internal SignerInformation(
+			SignerInfo			info,
+			DerObjectIdentifier	contentType,
+			CmsProcessable		content,
+			IDigestCalculator	digestCalculator)
+		{
+			this.info = info;
+			this.sid = new SignerID();
+			this.contentType = contentType;
+			this.isCounterSignature = contentType == null;
+
+			try
+			{
+				SignerIdentifier s = info.SignerID;
+
+				if (s.IsTagged)
+				{
+					Asn1OctetString octs = Asn1OctetString.GetInstance(s.ID);
+
+					sid.SubjectKeyIdentifier = octs.GetEncoded();
+				}
+				else
+				{
+					Asn1.Cms.IssuerAndSerialNumber iAnds =
+						Asn1.Cms.IssuerAndSerialNumber.GetInstance(s.ID);
+
+					sid.Issuer = iAnds.Name;
+					sid.SerialNumber = iAnds.SerialNumber.Value;
+				}
+			}
+			catch (IOException)
+			{
+				throw new ArgumentException("invalid sid in SignerInfo");
+			}
+
+			this.digestAlgorithm = info.DigestAlgorithm;
+			this.signedAttributeSet = info.AuthenticatedAttributes;
+			this.unsignedAttributeSet = info.UnauthenticatedAttributes;
+			this.encryptionAlgorithm = info.DigestEncryptionAlgorithm;
+			this.signature = info.EncryptedDigest.GetOctets();
+
+			this.content = content;
+			this.digestCalculator = digestCalculator;
+		}
+
+		public bool IsCounterSignature
+		{
+			get { return isCounterSignature; }
+		}
+
+		public DerObjectIdentifier ContentType
+		{
+			get { return contentType; }
+		}
+
+		public SignerID SignerID
+		{
+			get { return sid; }
+		}
+
+		/**
+		* return the version number for this objects underlying SignerInfo structure.
+		*/
+		public int Version
+		{
+			get { return info.Version.Value.IntValue; }
+		}
+
+		public AlgorithmIdentifier DigestAlgorithmID
+		{
+			get { return digestAlgorithm; }
+		}
+
+		/**
+		* return the object identifier for the signature.
+		*/
+		public string DigestAlgOid
+		{
+			get { return digestAlgorithm.ObjectID.Id; }
+		}
+
+		/**
+		* return the signature parameters, or null if there aren't any.
+		*/
+		public Asn1Object DigestAlgParams
+		{
+			get
+			{
+				Asn1Encodable ae = digestAlgorithm.Parameters;
+
+				return ae == null ? null : ae.ToAsn1Object();
+			}
+		}
+
+		/**
+		 * return the content digest that was calculated during verification.
+		 */
+		public byte[] GetContentDigest()
+		{
+			if (resultDigest == null)
+			{
+				throw new InvalidOperationException("method can only be called after verify.");
+			}
+
+			return (byte[])resultDigest.Clone();
+		}
+
+		public AlgorithmIdentifier EncryptionAlgorithmID
+		{
+			get { return encryptionAlgorithm; }
+		}
+
+		/**
+		* return the object identifier for the signature.
+		*/
+		public string EncryptionAlgOid
+		{
+			get { return encryptionAlgorithm.ObjectID.Id; }
+		}
+
+		/**
+		* return the signature/encryption algorithm parameters, or null if
+		* there aren't any.
+		*/
+		public Asn1Object EncryptionAlgParams
+		{
+			get
+			{
+				Asn1Encodable ae = encryptionAlgorithm.Parameters;
+
+				return ae == null ? null : ae.ToAsn1Object();
+			}
+		}
+
+		/**
+		* return a table of the signed attributes - indexed by
+		* the OID of the attribute.
+		*/
+		public Asn1.Cms.AttributeTable SignedAttributes
+		{
+			get
+			{
+				if (signedAttributeSet != null && signedAttributeTable == null)
+				{
+					signedAttributeTable = new Asn1.Cms.AttributeTable(signedAttributeSet);
+				}
+				return signedAttributeTable;
+			}
+		}
+
+		/**
+		* return a table of the unsigned attributes indexed by
+		* the OID of the attribute.
+		*/
+		public Asn1.Cms.AttributeTable UnsignedAttributes
+		{
+			get
+			{
+				if (unsignedAttributeSet != null && unsignedAttributeTable == null)
+				{
+					unsignedAttributeTable = new Asn1.Cms.AttributeTable(unsignedAttributeSet);
+				}
+				return unsignedAttributeTable;
+			}
+		}
+
+		/**
+		* return the encoded signature
+		*/
+		public byte[] GetSignature()
+		{
+			return (byte[]) signature.Clone();
+		}
+
+		/**
+		* Return a SignerInformationStore containing the counter signatures attached to this
+		* signer. If no counter signatures are present an empty store is returned.
+		*/
+		public SignerInformationStore GetCounterSignatures()
+		{
+			// TODO There are several checks implied by the RFC3852 comments that are missing
+
+			/*
+			The countersignature attribute MUST be an unsigned attribute; it MUST
+			NOT be a signed attribute, an authenticated attribute, an
+			unauthenticated attribute, or an unprotected attribute.
+			*/
+			Asn1.Cms.AttributeTable unsignedAttributeTable = UnsignedAttributes;
+			if (unsignedAttributeTable == null)
+			{
+                return new SignerInformationStore(Platform.CreateArrayList(0));
+			}
+
+            IList counterSignatures = Platform.CreateArrayList();
+
+			/*
+			The UnsignedAttributes syntax is defined as a SET OF Attributes.  The
+			UnsignedAttributes in a signerInfo may include multiple instances of
+			the countersignature attribute.
+			*/
+			Asn1EncodableVector allCSAttrs = unsignedAttributeTable.GetAll(CmsAttributes.CounterSignature);
+
+			foreach (Asn1.Cms.Attribute counterSignatureAttribute in allCSAttrs)
+			{
+				/*
+				A countersignature attribute can have multiple attribute values.  The
+				syntax is defined as a SET OF AttributeValue, and there MUST be one
+				or more instances of AttributeValue present.
+				*/
+				Asn1Set values = counterSignatureAttribute.AttrValues;
+				if (values.Count < 1)
+				{
+					// TODO Throw an appropriate exception?
+				}
+
+				foreach (Asn1Encodable asn1Obj in values)
+				{
+					/*
+					Countersignature values have the same meaning as SignerInfo values
+					for ordinary signatures, except that:
+
+					   1. The signedAttributes field MUST NOT contain a content-type
+					      attribute; there is no content type for countersignatures.
+
+					   2. The signedAttributes field MUST contain a message-digest
+					      attribute if it contains any other attributes.
+
+					   3. The input to the message-digesting process is the contents
+					      octets of the DER encoding of the signatureValue field of the
+					      SignerInfo value with which the attribute is associated.
+					*/
+					SignerInfo si = SignerInfo.GetInstance(asn1Obj.ToAsn1Object());
+
+					string digestName = CmsSignedHelper.Instance.GetDigestAlgName(si.DigestAlgorithm.ObjectID.Id);
+
+					counterSignatures.Add(new SignerInformation(si, null, null, new CounterSignatureDigestCalculator(digestName, GetSignature())));
+				}
+			}
+
+			return new SignerInformationStore(counterSignatures);
+		}
+
+		/**
+		* return the DER encoding of the signed attributes.
+		* @throws IOException if an encoding error occurs.
+		*/
+		public byte[] GetEncodedSignedAttributes()
+		{
+			return signedAttributeSet == null
+				?	null
+				:	signedAttributeSet.GetEncoded(Asn1Encodable.Der);
+		}
+
+		private bool DoVerify(
+			AsymmetricKeyParameter	key)
+		{
+			string digestName = Helper.GetDigestAlgName(this.DigestAlgOid);
+			IDigest digest = Helper.GetDigestInstance(digestName);
+
+			DerObjectIdentifier sigAlgOid = this.encryptionAlgorithm.ObjectID;
+			Asn1Encodable sigParams = this.encryptionAlgorithm.Parameters;
+			ISigner sig;
+
+			if (sigAlgOid.Equals(Asn1.Pkcs.PkcsObjectIdentifiers.IdRsassaPss))
+			{
+				// RFC 4056 2.2
+				// When the id-RSASSA-PSS algorithm identifier is used for a signature,
+				// the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params.
+				if (sigParams == null)
+					throw new CmsException("RSASSA-PSS signature must specify algorithm parameters");
+
+				try
+				{
+					// TODO Provide abstract configuration mechanism
+					// (via alternate SignerUtilities.GetSigner method taking ASN.1 params)
+
+					Asn1.Pkcs.RsassaPssParameters pss = Asn1.Pkcs.RsassaPssParameters.GetInstance(
+						sigParams.ToAsn1Object());
+
+					if (!pss.HashAlgorithm.ObjectID.Equals(this.digestAlgorithm.ObjectID))
+						throw new CmsException("RSASSA-PSS signature parameters specified incorrect hash algorithm");
+					if (!pss.MaskGenAlgorithm.ObjectID.Equals(Asn1.Pkcs.PkcsObjectIdentifiers.IdMgf1))
+						throw new CmsException("RSASSA-PSS signature parameters specified unknown MGF");
+
+					IDigest pssDigest = DigestUtilities.GetDigest(pss.HashAlgorithm.ObjectID);
+					int saltLength = pss.SaltLength.Value.IntValue;
+					byte trailerField = (byte) pss.TrailerField.Value.IntValue;
+
+					// RFC 4055 3.1
+					// The value MUST be 1, which represents the trailer field with hexadecimal value 0xBC
+					if (trailerField != 1)
+						throw new CmsException("RSASSA-PSS signature parameters must have trailerField of 1");
+
+					sig = new PssSigner(new RsaBlindedEngine(), pssDigest, saltLength);
+				}
+				catch (Exception e)
+				{
+					throw new CmsException("failed to set RSASSA-PSS signature parameters", e);
+				}
+			}
+			else
+			{
+				// TODO Probably too strong a check at the moment
+//				if (sigParams != null)
+//					throw new CmsException("unrecognised signature parameters provided");
+
+				string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(this.EncryptionAlgOid);
+
+				sig = Helper.GetSignatureInstance(signatureName);
+			}
+
+			try
+			{
+				if (digestCalculator != null)
+				{
+					resultDigest = digestCalculator.GetDigest();
+				}
+				else
+				{
+					if (content != null)
+					{
+						content.Write(new DigOutputStream(digest));
+					}
+					else if (signedAttributeSet == null)
+					{
+						// TODO Get rid of this exception and just treat content==null as empty not missing?
+						throw new CmsException("data not encapsulated in signature - use detached constructor.");
+					}
+
+					resultDigest = DigestUtilities.DoFinal(digest);
+				}
+			}
+			catch (IOException e)
+			{
+				throw new CmsException("can't process mime object to create signature.", e);
+			}
+
+			// RFC 3852 11.1 Check the content-type attribute is correct
+			{
+				Asn1Object validContentType = GetSingleValuedSignedAttribute(
+					CmsAttributes.ContentType, "content-type");
+				if (validContentType == null)
+				{
+					if (!isCounterSignature && signedAttributeSet != null)
+						throw new CmsException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
+				}
+				else
+				{
+					if (isCounterSignature)
+						throw new CmsException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
+
+					if (!(validContentType is DerObjectIdentifier))
+						throw new CmsException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
+
+					DerObjectIdentifier signedContentType = (DerObjectIdentifier)validContentType;
+
+					if (!signedContentType.Equals(contentType))
+						throw new CmsException("content-type attribute value does not match eContentType");
+				}
+			}
+
+			// RFC 3852 11.2 Check the message-digest attribute is correct
+			{
+				Asn1Object validMessageDigest = GetSingleValuedSignedAttribute(
+					CmsAttributes.MessageDigest, "message-digest");
+				if (validMessageDigest == null)
+				{
+				    if (signedAttributeSet != null)
+						throw new CmsException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
+				}
+				else
+				{
+					if (!(validMessageDigest is Asn1OctetString))
+					{
+						throw new CmsException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
+					}
+
+					Asn1OctetString signedMessageDigest = (Asn1OctetString)validMessageDigest;
+
+					if (!Arrays.AreEqual(resultDigest, signedMessageDigest.GetOctets()))
+						throw new CmsException("message-digest attribute value does not match calculated value");
+				}
+			}
+
+			// RFC 3852 11.4 Validate countersignature attribute(s)
+			{
+            	Asn1.Cms.AttributeTable signedAttrTable = this.SignedAttributes;
+            	if (signedAttrTable != null
+                	&& signedAttrTable.GetAll(CmsAttributes.CounterSignature).Count > 0)
+            	{
+                	throw new CmsException("A countersignature attribute MUST NOT be a signed attribute");
+            	}
+
+            	Asn1.Cms.AttributeTable unsignedAttrTable = this.UnsignedAttributes;
+            	if (unsignedAttrTable != null)
+            	{
+					foreach (Asn1.Cms.Attribute csAttr in unsignedAttrTable.GetAll(CmsAttributes.CounterSignature))
+	                {
+                    	if (csAttr.AttrValues.Count < 1)
+	                        throw new CmsException("A countersignature attribute MUST contain at least one AttributeValue");
+
+						// Note: We don't recursively validate the countersignature value
+    	            }
+	            }
+			}
+
+			try
+			{
+				sig.Init(false, key);
+
+				if (signedAttributeSet == null)
+				{
+					if (digestCalculator != null)
+					{
+						// need to decrypt signature and check message bytes
+						return VerifyDigest(resultDigest, key, this.GetSignature());
+					}
+					else if (content != null)
+					{
+						// TODO Use raw signature of the hash value instead
+						content.Write(new SigOutputStream(sig));
+					}
+				}
+				else
+				{
+					byte[] tmp = this.GetEncodedSignedAttributes();
+					sig.BlockUpdate(tmp, 0, tmp.Length);
+				}
+
+				return sig.VerifySignature(this.GetSignature());
+			}
+			catch (InvalidKeyException e)
+			{
+				throw new CmsException("key not appropriate to signature in message.", e);
+			}
+			catch (IOException e)
+			{
+				throw new CmsException("can't process mime object to create signature.", e);
+			}
+			catch (SignatureException e)
+			{
+				throw new CmsException("invalid signature format in message: " + e.Message, e);
+			}
+		}
+
+		private bool IsNull(
+			Asn1Encodable o)
+		{
+			return (o is Asn1Null) || (o == null);
+		}
+
+		private DigestInfo DerDecode(
+			byte[] encoding)
+		{
+			if (encoding[0] != (int)(Asn1Tags.Constructed | Asn1Tags.Sequence))
+			{
+				throw new IOException("not a digest info object");
+			}
+
+			DigestInfo digInfo = DigestInfo.GetInstance(Asn1Object.FromByteArray(encoding));
+
+			// length check to avoid Bleichenbacher vulnerability
+
+			if (digInfo.GetEncoded().Length != encoding.Length)
+			{
+				throw new CmsException("malformed RSA signature");
+			}
+
+			return digInfo;
+		}
+
+		private bool VerifyDigest(
+			byte[]					digest,
+			AsymmetricKeyParameter	key,
+			byte[]					signature)
+		{
+			string algorithm = Helper.GetEncryptionAlgName(this.EncryptionAlgOid);
+
+			try
+			{
+				if (algorithm.Equals("RSA"))
+				{
+					IBufferedCipher c = CmsEnvelopedHelper.Instance.CreateAsymmetricCipher("RSA/ECB/PKCS1Padding");
+
+					c.Init(false, key);
+
+					byte[] decrypt = c.DoFinal(signature);
+
+					DigestInfo digInfo = DerDecode(decrypt);
+
+					if (!digInfo.AlgorithmID.ObjectID.Equals(digestAlgorithm.ObjectID))
+					{
+						return false;
+					}
+
+					if (!IsNull(digInfo.AlgorithmID.Parameters))
+					{
+						return false;
+					}
+
+					byte[] sigHash = digInfo.GetDigest();
+
+					return Arrays.ConstantTimeAreEqual(digest, sigHash);
+				}
+				else if (algorithm.Equals("DSA"))
+				{
+					ISigner sig = SignerUtilities.GetSigner("NONEwithDSA");
+
+					sig.Init(false, key);
+
+					sig.BlockUpdate(digest, 0, digest.Length);
+
+					return sig.VerifySignature(signature);
+				}
+				else
+				{
+					throw new CmsException("algorithm: " + algorithm + " not supported in base signatures.");
+				}
+			}
+			catch (SecurityUtilityException e)
+			{
+				throw e;
+			}
+			catch (GeneralSecurityException e)
+			{
+				throw new CmsException("Exception processing signature: " + e, e);
+			}
+			catch (IOException e)
+			{
+				throw new CmsException("Exception decoding signature: " + e, e);
+			}
+		}
+
+		/**
+		* verify that the given public key successfully handles and confirms the
+		* signature associated with this signer.
+		*/
+		public bool Verify(
+			AsymmetricKeyParameter pubKey)
+		{
+			if (pubKey.IsPrivate)
+				throw new ArgumentException("Expected public key", "pubKey");
+
+			// Optional, but still need to validate if present
+			GetSigningTime();
+
+			return DoVerify(pubKey);
+		}
+
+		/**
+		* verify that the given certificate successfully handles and confirms
+		* the signature associated with this signer and, if a signingTime
+		* attribute is available, that the certificate was valid at the time the
+		* signature was generated.
+		*/
+		public bool Verify(
+			X509Certificate cert)
+		{
+			Asn1.Cms.Time signingTime = GetSigningTime();
+			if (signingTime != null)
+			{
+				cert.CheckValidity(signingTime.Date);
+			}
+
+			return DoVerify(cert.GetPublicKey());
+		}
+
+		/**
+		* Return the base ASN.1 CMS structure that this object contains.
+		*
+		* @return an object containing a CMS SignerInfo structure.
+		*/
+		public SignerInfo ToSignerInfo()
+		{
+			return info;
+		}
+
+		private Asn1Object GetSingleValuedSignedAttribute(
+			DerObjectIdentifier attrOID, string printableName)
+		{
+
+			Asn1.Cms.AttributeTable unsignedAttrTable = this.UnsignedAttributes;
+			if (unsignedAttrTable != null
+				&& unsignedAttrTable.GetAll(attrOID).Count > 0)
+			{
+				throw new CmsException("The " + printableName
+					+ " attribute MUST NOT be an unsigned attribute");
+			}
+
+			Asn1.Cms.AttributeTable signedAttrTable = this.SignedAttributes;
+			if (signedAttrTable == null)
+			{
+				return null;
+			}
+
+			Asn1EncodableVector v = signedAttrTable.GetAll(attrOID);
+			switch (v.Count)
+			{
+				case 0:
+					return null;
+				case 1:
+					Asn1.Cms.Attribute t = (Asn1.Cms.Attribute) v[0];
+					Asn1Set attrValues = t.AttrValues;
+
+					if (attrValues.Count != 1)
+						throw new CmsException("A " + printableName
+							+ " attribute MUST have a single attribute value");
+
+					return attrValues[0].ToAsn1Object();
+				default:
+					throw new CmsException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
+						+ printableName + " attribute");
+			}
+		}
+
+		private Asn1.Cms.Time GetSigningTime()
+		{
+			Asn1Object validSigningTime = GetSingleValuedSignedAttribute(
+				CmsAttributes.SigningTime, "signing-time");
+
+			if (validSigningTime == null)
+				return null;
+
+			try
+			{
+				return Asn1.Cms.Time.GetInstance(validSigningTime);
+			}
+			catch (ArgumentException)
+			{
+				throw new CmsException("signing-time attribute value not a valid 'Time' structure");
+			}
+		}
+
+		/**
+		* Return a signer information object with the passed in unsigned
+		* attributes replacing the ones that are current associated with
+		* the object passed in.
+		*
+		* @param signerInformation the signerInfo to be used as the basis.
+		* @param unsignedAttributes the unsigned attributes to add.
+		* @return a copy of the original SignerInformationObject with the changed attributes.
+		*/
+		public static SignerInformation ReplaceUnsignedAttributes(
+			SignerInformation		signerInformation,
+			Asn1.Cms.AttributeTable	unsignedAttributes)
+		{
+			SignerInfo sInfo = signerInformation.info;
+			Asn1Set unsignedAttr = null;
+
+			if (unsignedAttributes != null)
+			{
+				unsignedAttr = new DerSet(unsignedAttributes.ToAsn1EncodableVector());
+			}
+
+			return new SignerInformation(
+				new SignerInfo(
+					sInfo.SignerID,
+					sInfo.DigestAlgorithm,
+					sInfo.AuthenticatedAttributes,
+					sInfo.DigestEncryptionAlgorithm,
+					sInfo.EncryptedDigest,
+					unsignedAttr),
+				signerInformation.contentType,
+				signerInformation.content,
+				null);
+		}
+
+		/**
+		 * Return a signer information object with passed in SignerInformationStore representing counter
+		 * signatures attached as an unsigned attribute.
+		 *
+		 * @param signerInformation the signerInfo to be used as the basis.
+		 * @param counterSigners signer info objects carrying counter signature.
+		 * @return a copy of the original SignerInformationObject with the changed attributes.
+		 */
+		public static SignerInformation AddCounterSigners(
+			SignerInformation		signerInformation,
+			SignerInformationStore	counterSigners)
+		{
+			// TODO Perform checks from RFC 3852 11.4
+
+			SignerInfo sInfo = signerInformation.info;
+			Asn1.Cms.AttributeTable unsignedAttr = signerInformation.UnsignedAttributes;
+			Asn1EncodableVector v;
+
+			if (unsignedAttr != null)
+			{
+				v = unsignedAttr.ToAsn1EncodableVector();
+			}
+			else
+			{
+				v = new Asn1EncodableVector();
+			}
+
+			Asn1EncodableVector sigs = new Asn1EncodableVector();
+
+			foreach (SignerInformation sigInf in counterSigners.GetSigners())
+			{
+				sigs.Add(sigInf.ToSignerInfo());
+			}
+
+			v.Add(new Asn1.Cms.Attribute(CmsAttributes.CounterSignature, new DerSet(sigs)));
+
+			return new SignerInformation(
+				new SignerInfo(
+					sInfo.SignerID,
+					sInfo.DigestAlgorithm,
+					sInfo.AuthenticatedAttributes,
+					sInfo.DigestEncryptionAlgorithm,
+					sInfo.EncryptedDigest,
+					new DerSet(v)),
+				signerInformation.contentType,
+				signerInformation.content,
+				null);
+		}
+	}
+}