org.bouncycastle.cms.SignerInformation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcmail Show documentation
Show all versions of bcmail Show documentation
A patched bouncycastle-mail
The newest version!
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import javax.crypto.Cipher;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Null;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.DEREncodable;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTags;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.cms.SignerIdentifier;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.asn1.cms.Time;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.RawContentVerifier;
import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
import org.bouncycastle.util.Arrays;
/**
* an expanded SignerInfo block from a CMS Signed message
*/
public class SignerInformation
{
private SignerId sid;
private SignerInfo info;
private AlgorithmIdentifier digestAlgorithm;
private AlgorithmIdentifier encryptionAlgorithm;
private final ASN1Set signedAttributeSet;
private final ASN1Set unsignedAttributeSet;
private CMSProcessable content;
private byte[] signature;
private ASN1ObjectIdentifier contentType;
private IntDigestCalculator digestCalculator;
private byte[] resultDigest;
private SignatureAlgorithmIdentifierFinder sigAlgFinder;
// Derived
private AttributeTable signedAttributeValues;
private AttributeTable unsignedAttributeValues;
private boolean isCounterSignature;
SignerInformation(
SignerInfo info,
ASN1ObjectIdentifier contentType,
CMSProcessable content,
IntDigestCalculator digestCalculator,
SignatureAlgorithmIdentifierFinder sigAlgFinder)
{
this.info = info;
this.contentType = contentType;
this.sigAlgFinder = sigAlgFinder;
this.isCounterSignature = contentType == null;
SignerIdentifier s = info.getSID();
if (s.isTagged())
{
ASN1OctetString octs = ASN1OctetString.getInstance(s.getId());
sid = new SignerId(octs.getOctets());
}
else
{
IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.getInstance(s.getId());
sid = new SignerId(iAnds.getName(), iAnds.getSerialNumber().getValue());
}
this.digestAlgorithm = info.getDigestAlgorithm();
this.signedAttributeSet = info.getAuthenticatedAttributes();
this.unsignedAttributeSet = info.getUnauthenticatedAttributes();
this.encryptionAlgorithm = info.getDigestEncryptionAlgorithm();
this.signature = info.getEncryptedDigest().getOctets();
this.content = content;
this.digestCalculator = digestCalculator;
}
public boolean isCounterSignature()
{
return isCounterSignature;
}
public ASN1ObjectIdentifier getContentType()
{
return this.contentType;
}
private byte[] encodeObj(
DEREncodable obj)
throws IOException
{
if (obj != null)
{
return obj.getDERObject().getEncoded();
}
return null;
}
public SignerId getSID()
{
return sid;
}
/**
* return the version number for this objects underlying SignerInfo structure.
*/
public int getVersion()
{
return info.getVersion().getValue().intValue();
}
public AlgorithmIdentifier getDigestAlgorithmID()
{
return digestAlgorithm;
}
/**
* return the object identifier for the signature.
*/
public String getDigestAlgOID()
{
return digestAlgorithm.getObjectId().getId();
}
/**
* return the signature parameters, or null if there aren't any.
*/
public byte[] getDigestAlgParams()
{
try
{
return encodeObj(digestAlgorithm.getParameters());
}
catch (Exception e)
{
throw new RuntimeException("exception getting digest parameters " + e);
}
}
/**
* return the content digest that was calculated during verification.
*/
public byte[] getContentDigest()
{
if (resultDigest == null)
{
throw new IllegalStateException("method can only be called after verify.");
}
return (byte[])resultDigest.clone();
}
/**
* return the object identifier for the signature.
*/
public String getEncryptionAlgOID()
{
return encryptionAlgorithm.getObjectId().getId();
}
/**
* return the signature/encryption algorithm parameters, or null if
* there aren't any.
*/
public byte[] getEncryptionAlgParams()
{
try
{
return encodeObj(encryptionAlgorithm.getParameters());
}
catch (Exception e)
{
throw new RuntimeException("exception getting encryption parameters " + e);
}
}
/**
* return a table of the signed attributes - indexed by
* the OID of the attribute.
*/
public AttributeTable getSignedAttributes()
{
if (signedAttributeSet != null && signedAttributeValues == null)
{
signedAttributeValues = new AttributeTable(signedAttributeSet);
}
return signedAttributeValues;
}
/**
* return a table of the unsigned attributes indexed by
* the OID of the attribute.
*/
public AttributeTable getUnsignedAttributes()
{
if (unsignedAttributeSet != null && unsignedAttributeValues == null)
{
unsignedAttributeValues = new AttributeTable(unsignedAttributeSet);
}
return unsignedAttributeValues;
}
/**
* 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.
*/
AttributeTable unsignedAttributeTable = getUnsignedAttributes();
if (unsignedAttributeTable == null)
{
return new SignerInformationStore(new ArrayList(0));
}
List 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);
for (int i = 0; i < allCSAttrs.size(); ++i)
{
Attribute counterSignatureAttribute = (Attribute)allCSAttrs.get(i);
/*
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.getAttrValues();
if (values.size() < 1)
{
// TODO Throw an appropriate exception?
}
for (Enumeration en = values.getObjects(); en.hasMoreElements();)
{
/*
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(en.nextElement());
String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(si.getDigestAlgorithm().getObjectId().getId());
counterSignatures.add(new SignerInformation(si, null, null, new CounterSignatureDigestCalculator(digestName, null, getSignature()), new DefaultSignatureAlgorithmIdentifierFinder()));
}
}
return new SignerInformationStore(counterSignatures);
}
/**
* return the DER encoding of the signed attributes.
* @throws IOException if an encoding error occurs.
*/
public byte[] getEncodedSignedAttributes()
throws IOException
{
if (signedAttributeSet != null)
{
return signedAttributeSet.getEncoded(ASN1Encodable.DER);
}
return null;
}
/**
* @deprecated
*/
private boolean doVerify(
PublicKey key,
Provider sigProvider)
throws CMSException, NoSuchAlgorithmException
{
String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(this.getDigestAlgOID());
String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
String signatureName = digestName + "with" + encName;
Signature sig = CMSSignedHelper.INSTANCE.getSignatureInstance(signatureName, sigProvider);
MessageDigest digest = CMSSignedHelper.INSTANCE.getDigestInstance(digestName, sigProvider);
// TODO [BJA-109] Note: PSSParameterSpec requires JDK1.4+
/*
try
{
DERObjectIdentifier sigAlgOID = encryptionAlgorithm.getObjectId();
DEREncodable sigParams = this.encryptionAlgorithm.getParameters();
if (sigAlgOID.equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
{
// 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");
}
AlgorithmParameters params = AlgorithmParameters.getInstance(
sigAlgOID.getId(), sig.getProvider().getName());
params.init(sigParams.getDERObject().getEncoded(), "ASN.1");
PSSParameterSpec spec = (PSSParameterSpec)params.getParameterSpec(PSSParameterSpec.class);
sig.setParameter(spec);
}
else
{
// TODO Are there other signature algorithms that provide parameters?
if (sigParams != null)
{
throw new CMSException("unrecognised signature parameters provided");
}
}
}
catch (IOException e)
{
throw new CMSException("error encoding signature parameters.", e);
}
catch (InvalidAlgorithmParameterException e)
{
throw new CMSException("error setting signature parameters.", e);
}
catch (InvalidParameterSpecException e)
{
throw new CMSException("error processing signature parameters.", e);
}
*/
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 = digest.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
{
DERObject 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 instanceof 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
{
DERObject 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 instanceof ASN1OctetString))
{
throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
}
ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest;
if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets()))
{
throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value");
}
}
}
// RFC 3852 11.4 Validate countersignature attribute(s)
{
AttributeTable signedAttrTable = this.getSignedAttributes();
if (signedAttrTable != null
&& signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
{
throw new CMSException("A countersignature attribute MUST NOT be a signed attribute");
}
AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
if (unsignedAttrTable != null)
{
ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
for (int i = 0; i < csAttrs.size(); ++i)
{
Attribute csAttr = (Attribute)csAttrs.get(i);
if (csAttr.getAttrValues().size() < 1)
{
throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");
}
// Note: We don't recursively validate the countersignature value
}
}
}
try
{
sig.initVerify(key);
if (signedAttributeSet == null)
{
if (digestCalculator != null)
{
// need to decrypt signature and check message bytes
return verifyDigest(resultDigest, key, this.getSignature(), sigProvider);
}
else if (content != null)
{
// TODO Use raw signature of the hash value instead
content.write(new SigOutputStream(sig));
}
}
else
{
sig.update(this.getEncodedSignedAttributes());
}
return sig.verify(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.getMessage(), e);
}
}
private boolean doVerify(
SignerInformationVerifier verifier)
throws CMSException
{
String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(this.getDigestAlgOID());
String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
String signatureName = digestName + "with" + encName;
try
{
if (digestCalculator != null)
{
resultDigest = digestCalculator.getDigest();
}
else
{
DigestCalculator calc = verifier.getDigestCalculator(this.getDigestAlgorithmID());
if (content != null)
{
OutputStream digOut = calc.getOutputStream();
content.write(digOut);
digOut.close();
}
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 = calc.getDigest();
}
}
catch (IOException e)
{
throw new CMSException("can't process mime object to create signature.", e);
}
catch (NoSuchAlgorithmException e)
{
throw new CMSException("can't find algorithm: " + e.getMessage(), e);
}
catch (OperatorCreationException e)
{
throw new CMSException("can't create digest calculator: " + e.getMessage(), e);
}
// RFC 3852 11.1 Check the content-type attribute is correct
{
DERObject 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 instanceof 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
{
DERObject 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 instanceof ASN1OctetString))
{
throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
}
ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest;
if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets()))
{
throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value");
}
}
}
// RFC 3852 11.4 Validate countersignature attribute(s)
{
AttributeTable signedAttrTable = this.getSignedAttributes();
if (signedAttrTable != null
&& signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
{
throw new CMSException("A countersignature attribute MUST NOT be a signed attribute");
}
AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
if (unsignedAttrTable != null)
{
ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
for (int i = 0; i < csAttrs.size(); ++i)
{
Attribute csAttr = (Attribute)csAttrs.get(i);
if (csAttr.getAttrValues().size() < 1)
{
throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");
}
// Note: We don't recursively validate the countersignature value
}
}
}
try
{
ContentVerifier contentVerifier = verifier.getContentVerifier(sigAlgFinder.find(signatureName));
OutputStream sigOut = contentVerifier.getOutputStream();
if (signedAttributeSet == null)
{
if (digestCalculator != null)
{
if (contentVerifier instanceof RawContentVerifier)
{
RawContentVerifier rawVerifier = (RawContentVerifier)contentVerifier;
if (encName.equals("RSA"))
{
DigestInfo digInfo = new DigestInfo(digestAlgorithm, resultDigest);
return rawVerifier.verify(digInfo.getDEREncoded(), this.getSignature());
}
return rawVerifier.verify(resultDigest, this.getSignature());
}
throw new CMSException("verifier unable to process raw signature");
}
else if (content != null)
{
// TODO Use raw signature of the hash value instead
content.write(sigOut);
}
}
else
{
sigOut.write(this.getEncodedSignedAttributes());
}
sigOut.close();
return contentVerifier.verify(this.getSignature());
}
catch (IOException e)
{
throw new CMSException("can't process mime object to create signature.", e);
}
catch (OperatorCreationException e)
{
throw new CMSException("can't create content verifier: " + e.getMessage(), e);
}
}
private boolean isNull(
DEREncodable o)
{
return (o instanceof ASN1Null) || (o == null);
}
private DigestInfo derDecode(
byte[] encoding)
throws IOException, CMSException
{
if (encoding[0] != (DERTags.CONSTRUCTED | DERTags.SEQUENCE))
{
throw new IOException("not a digest info object");
}
ASN1InputStream aIn = new ASN1InputStream(encoding);
DigestInfo digInfo = new DigestInfo((ASN1Sequence)aIn.readObject());
// length check to avoid Bleichenbacher vulnerability
if (digInfo.getEncoded().length != encoding.length)
{
throw new CMSException("malformed RSA signature");
}
return digInfo;
}
/**
* @deprecated
*/
private boolean verifyDigest(
byte[] digest,
PublicKey key,
byte[] signature,
Provider sigProvider)
throws NoSuchAlgorithmException, CMSException
{
String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
try
{
if (encName.equals("RSA"))
{
Cipher c = CMSEnvelopedHelper.INSTANCE.createAsymmetricCipher("RSA/ECB/PKCS1Padding", sigProvider);
c.init(Cipher.DECRYPT_MODE, key);
DigestInfo digInfo = derDecode(c.doFinal(signature));
if (!digInfo.getAlgorithmId().getObjectId().equals(digestAlgorithm.getObjectId()))
{
return false;
}
if (!isNull(digInfo.getAlgorithmId().getParameters()))
{
return false;
}
byte[] sigHash = digInfo.getDigest();
return Arrays.constantTimeAreEqual(digest, sigHash);
}
else if (encName.equals("DSA"))
{
Signature sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEwithDSA", sigProvider);
sig.initVerify(key);
sig.update(digest);
return sig.verify(signature);
}
else
{
throw new CMSException("algorithm: " + encName + " not supported in base signatures.");
}
}
catch (GeneralSecurityException e)
{
throw new CMSException("Exception processing signature: " + e, e);
}
catch (IOException e)
{
throw new CMSException("Exception decoding signature: " + e, e);
}
}
// private boolean verifyDigest(
// byte[] digest,
// PublicKey key,
// byte[] signature,
// Provider sigProvider)
// throws NoSuchAlgorithmException, CMSException
// {
// String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
// String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(this.getDigestAlgOID());
// String signatureName = digestName + "with" + encName;
//
// try
// {
// byte[] bytesToSign = digest;
// Signature sig;
//
// if (encName.equals("RSA"))
// {
// bytesToSign = RSADigestSigner.encodeDERSig(digestAlgorithm.getObjectId(), digest);
// sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEwithRSA", sigProvider);
// }
// else if (encName.equals("DSA"))
// {
// sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEwithDSA", sigProvider);
// }
// else if (encName.equals("RSAandMGF1"))
// {
// sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEWITHRSAPSS", sigProvider);
// try
// {
// // Init the params this way to avoid having a 'raw' version of each PSS algorithm
// Signature sig2 = CMSSignedHelper.INSTANCE.getSignatureInstance(signatureName, sigProvider);
// PSSParameterSpec spec = (PSSParameterSpec)sig2.getParameters().getParameterSpec(PSSParameterSpec.class);
// sig.setParameter(spec);
// }
// catch (Exception e)
// {
// throw new CMSException("algorithm: " + encName + " could not be configured.");
// }
// }
// else
// {
// throw new CMSException("algorithm: " + encName + " not supported in base signatures.");
// }
//
// sig.initVerify(key);
// sig.update(bytesToSign);
// return sig.verify(signature);
// }
// catch (GeneralSecurityException e)
// {
// throw new CMSException("Exception processing signature: " + e, e);
// }
// }
/**
* verify that the given public key successfully handles and confirms the
* signature associated with this signer.
* @deprecated use verify(ContentVerifierProvider)
*/
public boolean verify(
PublicKey key,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
{
return verify(key, CMSUtils.getProvider(sigProvider));
}
/**
* verify that the given public key successfully handles and confirms the
* signature associated with this signer
* @deprecated use verify(ContentVerifierProvider)
*/
public boolean verify(
PublicKey key,
Provider sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
{
// Optional, but still need to validate if present
getSigningTime();
return doVerify(key, sigProvider);
}
/**
* 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.
* @deprecated use verify(ContentVerifierProvider)
*/
public boolean verify(
X509Certificate cert,
String sigProvider)
throws NoSuchAlgorithmException, NoSuchProviderException,
CertificateExpiredException, CertificateNotYetValidException,
CMSException
{
return verify(cert, CMSUtils.getProvider(sigProvider));
}
/**
* 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.
* @deprecated use verify(ContentVerifierProvider)
*/
public boolean verify(
X509Certificate cert,
Provider sigProvider)
throws NoSuchAlgorithmException,
CertificateExpiredException, CertificateNotYetValidException,
CMSException
{
Time signingTime = getSigningTime();
if (signingTime != null)
{
cert.checkValidity(signingTime.getDate());
}
return doVerify(cert.getPublicKey(), sigProvider);
}
/**
* Verify that the given verifier can successfully verify the signature on
* this SignerInformation object.
*
* @param verifier a suitably configured SignerInformationVerifier.
* @return true if the signer information is verified, false otherwise.
* @throws org.bouncycastle.cms.CMSVerifierCertificateNotValidException if the provider has an associated certificate and the certificate is not valid at the time given as the SignerInfo's signing time.
* @throws org.bouncycastle.cms.CMSException if the verifier is unable to create a ContentVerifiers or DigestCalculators.
*/
public boolean verify(SignerInformationVerifier verifier)
throws CMSException
{
Time signingTime = getSigningTime(); // has to be validated if present.
if (verifier.hasAssociatedCertificate())
{
if (signingTime != null)
{
X509CertificateHolder dcv = verifier.getAssociatedCertificate();
if (!dcv.isValidOn(signingTime.getDate()))
{
throw new CMSVerifierCertificateNotValidException("verifier not valid at signingTime");
}
}
}
return doVerify(verifier);
}
/**
* Return the base ASN.1 CMS structure that this object contains.
*
* @return an object containing a CMS SignerInfo structure.
* @deprecated use toASN1Structure()
*/
public SignerInfo toSignerInfo()
{
return info;
}
/**
* Return the underlying ASN.1 object defining this SignerInformation object.
*
* @return a SignerInfo.
*/
public SignerInfo toASN1Structure()
{
return info;
}
private DERObject getSingleValuedSignedAttribute(
DERObjectIdentifier attrOID, String printableName)
throws CMSException
{
AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
if (unsignedAttrTable != null
&& unsignedAttrTable.getAll(attrOID).size() > 0)
{
throw new CMSException("The " + printableName
+ " attribute MUST NOT be an unsigned attribute");
}
AttributeTable signedAttrTable = this.getSignedAttributes();
if (signedAttrTable == null)
{
return null;
}
ASN1EncodableVector v = signedAttrTable.getAll(attrOID);
switch (v.size())
{
case 0:
return null;
case 1:
{
Attribute t = (Attribute)v.get(0);
ASN1Set attrValues = t.getAttrValues();
if (attrValues.size() != 1)
{
throw new CMSException("A " + printableName
+ " attribute MUST have a single attribute value");
}
return attrValues.getObjectAt(0).getDERObject();
}
default:
throw new CMSException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
+ printableName + " attribute");
}
}
private Time getSigningTime() throws CMSException
{
DERObject validSigningTime = getSingleValuedSignedAttribute(
CMSAttributes.signingTime, "signing-time");
if (validSigningTime == null)
{
return null;
}
try
{
return Time.getInstance(validSigningTime);
}
catch (IllegalArgumentException e)
{
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,
AttributeTable unsignedAttributes)
{
SignerInfo sInfo = signerInformation.info;
ASN1Set unsignedAttr = null;
if (unsignedAttributes != null)
{
unsignedAttr = new DERSet(unsignedAttributes.toASN1EncodableVector());
}
return new SignerInformation(
new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), unsignedAttr),
signerInformation.contentType, signerInformation.content, null, new DefaultSignatureAlgorithmIdentifierFinder());
}
/**
* 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;
AttributeTable unsignedAttr = signerInformation.getUnsignedAttributes();
ASN1EncodableVector v;
if (unsignedAttr != null)
{
v = unsignedAttr.toASN1EncodableVector();
}
else
{
v = new ASN1EncodableVector();
}
ASN1EncodableVector sigs = new ASN1EncodableVector();
for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext();)
{
sigs.add(((SignerInformation)it.next()).toSignerInfo());
}
v.add(new Attribute(CMSAttributes.counterSignature, new DERSet(sigs)));
return new SignerInformation(
new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), new DERSet(v)),
signerInformation.contentType, signerInformation.content, null, new DefaultSignatureAlgorithmIdentifierFinder());
}
}