iTextSharp-LGPL/src/core/srcbc/cms/SignerInformation.cs

673 lines
18 KiB
C#

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 Asn1Set signedAttributes;
private Asn1Set unsignedAttributes;
private CmsProcessable content;
private byte[] signature;
private DerObjectIdentifier contentType;
private IDigestCalculator digestCalculator;
private byte[] resultDigest;
internal SignerInformation(
SignerInfo info,
DerObjectIdentifier contentType,
CmsProcessable content,
IDigestCalculator digestCalculator)
{
this.info = info;
this.sid = new SignerID();
this.contentType = contentType;
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.signedAttributes = info.AuthenticatedAttributes;
this.unsignedAttributes = info.UnauthenticatedAttributes;
this.encryptionAlgorithm = info.DigestEncryptionAlgorithm;
this.signature = info.EncryptedDigest.GetOctets();
this.content = content;
this.digestCalculator = digestCalculator;
}
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
{
return signedAttributes == null
? null
: new Asn1.Cms.AttributeTable(signedAttributes);
}
}
/**
* return a table of the unsigned attributes indexed by
* the OID of the attribute.
*/
public Asn1.Cms.AttributeTable UnsignedAttributes
{
get
{
return unsignedAttributes == null
? null
: new Asn1.Cms.AttributeTable(unsignedAttributes);
}
}
/**
* 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(new ArrayList(0));
}
IList counterSignatures = new ArrayList();
/*
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, CmsAttributes.CounterSignature, 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 signedAttributes == null
? null
: signedAttributes.GetEncoded(Asn1Encodable.Der);
}
private bool DoVerify(
AsymmetricKeyParameter key,
Asn1.Cms.AttributeTable signedAttrTable)
{
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
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
{
sig.Init(false, key);
if (signedAttributes == null)
{
if (content != null)
{
content.Write(new CmsSignedDataGenerator.SigOutputStream(sig));
content.Write(new CmsSignedDataGenerator.DigOutputStream(digest));
resultDigest = DigestUtilities.DoFinal(digest);
}
else
{
resultDigest = digestCalculator.GetDigest();
// need to decrypt signature and check message bytes
return VerifyDigest(resultDigest, key, this.GetSignature());
}
}
else
{
byte[] hash;
if (content != null)
{
content.Write(
new CmsSignedDataGenerator.DigOutputStream(digest));
hash = DigestUtilities.DoFinal(digest);
}
else if (digestCalculator != null)
{
hash = digestCalculator.GetDigest();
}
else
{
hash = null;
}
resultDigest = hash;
Asn1.Cms.Attribute dig = signedAttrTable[Asn1.Cms.CmsAttributes.MessageDigest];
Asn1.Cms.Attribute type = signedAttrTable[Asn1.Cms.CmsAttributes.ContentType];
if (dig == null)
{
throw new SignatureException("no hash for content found in signed attributes");
}
if (type == null && !contentType.Equals(CmsAttributes.CounterSignature))
{
throw new SignatureException("no content type id found in signed attributes");
}
Asn1Object hashObj = dig.AttrValues[0].ToAsn1Object();
if (hashObj is Asn1OctetString)
{
byte[] signedHash = ((Asn1OctetString)hashObj).GetOctets();
if (!Arrays.AreEqual(hash, signedHash))
{
throw new SignatureException("content hash found in signed attributes different");
}
}
else if (hashObj is DerNull)
{
if (hash != null)
{
throw new SignatureException("NULL hash found in signed attributes when one expected");
}
}
if (type != null)
{
DerObjectIdentifier typeOID = (DerObjectIdentifier)type.AttrValues[0];
if (!typeOID.Equals(contentType))
{
throw new SignatureException("contentType in signed attributes different");
}
}
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 = CipherUtilities.GetCipher("RSA//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.AreEqual(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 succesfully handles and confirms the
* signature associated with this signer.
*/
public bool Verify(
AsymmetricKeyParameter pubKey)
{
if (pubKey.IsPrivate)
throw new ArgumentException("Expected public key", "pubKey");
return DoVerify(pubKey, this.SignedAttributes);
}
/**
* 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.AttributeTable attr = this.SignedAttributes;
if (attr != null)
{
Asn1EncodableVector v = attr.GetAll(CmsAttributes.SigningTime);
switch (v.Count)
{
case 0:
break;
case 1:
{
Asn1.Cms.Attribute t = (Asn1.Cms.Attribute) v[0];
Debug.Assert(t != null);
Asn1Set attrValues = t.AttrValues;
if (attrValues.Count != 1)
throw new CmsException("A signing-time attribute MUST have a single attribute value");
Asn1.Cms.Time time = Asn1.Cms.Time.GetInstance(attrValues[0].ToAsn1Object());
cert.CheckValidity(time.Date);
break;
}
default:
throw new CmsException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the signing-time attribute");
}
}
return DoVerify(cert.GetPublicKey(), attr);
}
/**
* Return the base ASN.1 CMS structure that this object contains.
*
* @return an object containing a CMS SignerInfo structure.
*/
public SignerInfo ToSignerInfo()
{
return info;
}
/**
* 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)
{
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);
}
}
}