Initial Commit
This commit is contained in:
631
iTechSharp/srcbc/cms/SignerInformation.cs
Normal file
631
iTechSharp/srcbc/cms/SignerInformation.cs
Normal file
@@ -0,0 +1,631 @@
|
||||
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.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.GetOctets();
|
||||
}
|
||||
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()
|
||||
{
|
||||
Asn1.Cms.AttributeTable unsignedAttributeTable = UnsignedAttributes;
|
||||
if (unsignedAttributeTable == null)
|
||||
{
|
||||
return new SignerInformationStore(new ArrayList(0));
|
||||
}
|
||||
|
||||
IList counterSignatures = new ArrayList();
|
||||
|
||||
Asn1.Cms.Attribute counterSignatureAttribute = unsignedAttributeTable[CmsAttributes.CounterSignature];
|
||||
if (counterSignatureAttribute != null)
|
||||
{
|
||||
Asn1Set values = counterSignatureAttribute.AttrValues;
|
||||
counterSignatures = new ArrayList(values.Count);
|
||||
|
||||
foreach (Asn1Encodable asn1Obj in values)
|
||||
{
|
||||
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
|
||||
// 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 saltLen = pss.SaltLength.Value.IntValue;
|
||||
byte trailer = (byte) pss.TrailerField.Value.IntValue;
|
||||
|
||||
sig = new Crypto.Signers.PssSigner(new RsaBlindedEngine(), pssDigest, saltLen, trailer);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user