org.bouncycastle.mail.smime.validator.SignedMailValidator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcmail-jdk16 Show documentation
Show all versions of bcmail-jdk16 Show documentation
The Bouncy Castle Java CMS and S/MIME APIs for handling the CMS and S/MIME protocols. This jar contains CMS and S/MIME APIs for JDK 1.6. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs. If the S/MIME API is used, the JavaMail API and the Java activation framework will also be needed.
package org.bouncycastle.mail.smime.validator;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.Time;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.i18n.ErrorBundle;
import org.bouncycastle.i18n.filter.TrustedInput;
import org.bouncycastle.i18n.filter.UntrustedInput;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.x509.CertPathReviewerException;
import org.bouncycastle.x509.PKIXCertPathReviewer;
import javax.mail.Address;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.cert.CertPath;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
public class SignedMailValidator
{
private static final String RESOURCE_NAME = "org.bouncycastle.mail.smime.validator.SignedMailValidatorMessages";
private static final Class DEFAULT_CERT_PATH_REVIEWER = PKIXCertPathReviewer.class;
private static final String EXT_KEY_USAGE = X509Extensions.ExtendedKeyUsage
.getId();
private static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName
.getId();
private static final int shortKeyLength = 512;
// (365.25*30)*24*3600*1000
private static final long THIRTY_YEARS_IN_MILLI_SEC = 21915l*12l*3600l*1000l;
private CertStore certs;
private SignerInformationStore signers;
private Map results;
private String[] fromAddresses;
private Class certPathReviewerClass;
/**
* Validates the signed {@link MimeMessage} message. The
* {@link PKIXParameters} from param are used for the certificate path
* validation. The actual PKIXParameters used for the certificate path
* validation is a copy of param with the followin changes:
- The
* validation date is changed to the signature time
- A CertStore with
* certificates and crls from the mail message is added to the CertStores.
*
* In param
it's also possible to add additional CertStores
* with intermediate Certificates and/or CRLs which then are also used for
* the validation.
*
* @param message
* the signed MimeMessage
* @param param
* the parameters for the certificate path validation
* @throws SignedMailValidatorException
* if the message is no signed message or if an exception occurs
* reading the message
*/
public SignedMailValidator(MimeMessage message, PKIXParameters param)
throws SignedMailValidatorException
{
this(message, param, DEFAULT_CERT_PATH_REVIEWER);
}
/**
* Validates the signed {@link MimeMessage} message. The
* {@link PKIXParameters} from param are used for the certificate path
* validation. The actual PKIXParameters used for the certificate path
* validation is a copy of param with the followin changes:
- The
* validation date is changed to the signature time
- A CertStore with
* certificates and crls from the mail message is added to the CertStores.
*
* In param
it's also possible to add additional CertStores
* with intermediate Certificates and/or CRLs which then are also used for
* the validation.
*
* @param message
* the signed MimeMessage
* @param param
* the parameters for the certificate path validation
* @param certPathReviewerClass
* a subclass of {@link PKIXCertPathReviewer}. The SignedMailValidator
* uses objects of this type for the cert path vailidation. The class must
* have an empty constructor.
* @throws SignedMailValidatorException
* if the message is no signed message or if an exception occurs
* reading the message
* @throws IllegalArgumentException if the certPathReviewerClass is not a
* subclass of {@link PKIXCertPathReviewer} or objects of
* certPathReviewerClass can not be instantiated
*/
public SignedMailValidator(MimeMessage message, PKIXParameters param, Class certPathReviewerClass)
throws SignedMailValidatorException
{
this.certPathReviewerClass = certPathReviewerClass;
try
{
certPathReviewerClass.asSubclass(DEFAULT_CERT_PATH_REVIEWER);
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("certPathReviewerClass is not a subclass of " + DEFAULT_CERT_PATH_REVIEWER.getName());
}
SMIMESigned s;
try
{
// check if message is multipart signed
if (message.isMimeType("multipart/signed"))
{
MimeMultipart mimemp = (MimeMultipart) message.getContent();
s = new SMIMESigned(mimemp);
}
else if (message.isMimeType("application/pkcs7-mime")
|| message.isMimeType("application/x-pkcs7-mime"))
{
s = new SMIMESigned(message);
}
else
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.noSignedMessage");
throw new SignedMailValidatorException(msg);
}
// save certstore and signerInformationStore
certs = s.getCertificatesAndCRLs("Collection", "BC");
signers = s.getSignerInfos();
// save "from" addresses from message
Address[] froms = message.getFrom();
fromAddresses = new String[froms.length];
for (int i = 0; i < froms.length; i++)
{
InternetAddress inetAddr = (InternetAddress) froms[i];
fromAddresses[i] = inetAddr.getAddress();
}
// initialize results
results = new HashMap();
}
catch (Exception e)
{
if (e instanceof SignedMailValidatorException)
{
throw (SignedMailValidatorException) e;
}
// exception reading message
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.exceptionReadingMessage",
new Object[] { e.getMessage(), e , e.getClass().getName()});
throw new SignedMailValidatorException(msg, e);
}
// validate signatues
validateSignatures(param);
}
protected void validateSignatures(PKIXParameters pkixParam)
{
PKIXParameters usedParameters = (PKIXParameters) pkixParam.clone();
// add crls and certs from mail
usedParameters.addCertStore(certs);
Collection c = signers.getSigners();
Iterator it = c.iterator();
// check each signer
while (it.hasNext())
{
List errors = new ArrayList();
List notifications = new ArrayList();
SignerInformation signer = (SignerInformation) it.next();
// signer certificate
X509Certificate cert = null;
try
{
Collection certCollection = findCerts(usedParameters
.getCertStores(), signer.getSID());
Iterator certIt = certCollection.iterator();
if (certIt.hasNext())
{
cert = (X509Certificate) certIt.next();
}
}
catch (CertStoreException cse)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.exceptionRetrievingSignerCert",
new Object[] { cse.getMessage(), cse , cse.getClass().getName()});
errors.add(msg);
}
if (cert != null)
{
// check signature
boolean validSignature = false;
try
{
validSignature = signer.verify(cert.getPublicKey(), "BC");
if (!validSignature)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.signatureNotVerified");
errors.add(msg);
}
}
catch (Exception e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.exceptionVerifyingSignature",
new Object[] { e.getMessage(), e, e.getClass().getName() });
errors.add(msg);
}
// check signer certificate (mail address, key usage, etc)
checkSignerCert(cert, errors, notifications);
// notify if a signed receip request is in the message
AttributeTable atab = signer.getSignedAttributes();
if (atab != null)
{
Attribute attr = atab.get(PKCSObjectIdentifiers.id_aa_receiptRequest);
if (attr != null)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.signedReceiptRequest");
notifications.add(msg);
}
}
// check certificate path
// get signing time if possible, otherwise use current time as
// signing time
Date signTime = getSignatureTime(signer);
if (signTime == null) // no signing time was found
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.noSigningTime");
errors.add(msg);
signTime = new Date();
}
else
{
// check if certificate was valid at signing time
try
{
cert.checkValidity(signTime);
}
catch (CertificateExpiredException e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.certExpired",
new Object[] { new TrustedInput(signTime), new TrustedInput(cert.getNotAfter()) });
errors.add(msg);
}
catch (CertificateNotYetValidException e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.certNotYetValid",
new Object[] { new TrustedInput(signTime), new TrustedInput(cert.getNotBefore()) });
errors.add(msg);
}
}
usedParameters.setDate(signTime);
try
{
// construct cert chain
CertPath certPath;
List userProvidedList;
List userCertStores = new ArrayList();
userCertStores.add(certs);
Object[] cpres = createCertPath(cert, usedParameters.getTrustAnchors(), pkixParam.getCertStores(), userCertStores);
certPath = (CertPath) cpres[0];
userProvidedList = (List) cpres[1];
// validate cert chain
PKIXCertPathReviewer review;
try
{
review = (PKIXCertPathReviewer) certPathReviewerClass.newInstance();
}
catch (IllegalAccessException e)
{
throw new IllegalArgumentException("Cannot instantiate object of type " +
certPathReviewerClass.getName() + ": " + e.getMessage(), e);
}
catch (InstantiationException e)
{
throw new IllegalArgumentException("Cannot instantiate object of type " +
certPathReviewerClass.getName() + ": " + e.getMessage(), e);
}
review.init(certPath, usedParameters);
if (!review.isValidCertPath())
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.certPathInvalid");
errors.add(msg);
}
results.put(signer, new ValidationResult(review,
validSignature, errors, notifications, userProvidedList));
}
catch (GeneralSecurityException gse)
{
// cannot create cert path
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.exceptionCreateCertPath",
new Object[] { gse.getMessage(), gse, gse.getClass().getName() });
errors.add(msg);
results.put(signer, new ValidationResult(null,
validSignature, errors, notifications, null));
}
catch (CertPathReviewerException cpre)
{
// cannot initialize certpathreviewer - wrong parameters
errors.add(cpre.getErrorMessage());
results.put(signer, new ValidationResult(null,
validSignature, errors, notifications, null));
}
}
else
// no signer certificate found
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.noSignerCert");
errors.add(msg);
results.put(signer, new ValidationResult(null, false, errors,
notifications, null));
}
}
}
public static Set getEmailAddresses(X509Certificate cert) throws IOException, CertificateEncodingException
{
Set addresses = new HashSet();
X509Principal name = PrincipalUtil.getSubjectX509Principal(cert);
Vector oids = name.getOIDs();
Vector names = name.getValues();
for (int i = 0; i < oids.size(); i++)
{
if (oids.get(i).equals(X509Principal.EmailAddress))
{
String email = ((String) names.get(i)).toLowerCase();
addresses.add(email);
break;
}
}
byte[] ext = cert.getExtensionValue(SUBJECT_ALTERNATIVE_NAME);
if (ext != null)
{
DERSequence altNames = (DERSequence) getObject(ext);
for (int j = 0; j < altNames.size(); j++)
{
ASN1TaggedObject o = (ASN1TaggedObject) altNames
.getObjectAt(j);
if (o.getTagNo() == 1)
{
String email = DERIA5String.getInstance(o, true)
.getString().toLowerCase();
addresses.add(email);
}
}
}
return addresses;
}
private static DERObject getObject(byte[] ext) throws IOException
{
ASN1InputStream aIn = new ASN1InputStream(ext);
ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
aIn = new ASN1InputStream(octs.getOctets());
return aIn.readObject();
}
protected void checkSignerCert(X509Certificate cert, List errors,
List notifications)
{
// get key length
PublicKey key = cert.getPublicKey();
int keyLenght = -1;
if (key instanceof RSAPublicKey)
{
keyLenght = ((RSAPublicKey) key).getModulus().bitLength();
}
else if (key instanceof DSAPublicKey)
{
keyLenght = ((DSAPublicKey) key).getParams().getP().bitLength();
}
if (keyLenght != -1 && keyLenght <= shortKeyLength)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.shortSigningKey",
new Object[] { new Integer(keyLenght) });
notifications.add(msg);
}
// warn if certificate has very long validity period
long validityPeriod = cert.getNotAfter().getTime() - cert.getNotBefore().getTime();
if (validityPeriod > THIRTY_YEARS_IN_MILLI_SEC)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.longValidity",
new Object[] {new TrustedInput(cert.getNotBefore()), new TrustedInput(cert.getNotAfter())});
notifications.add(msg);
}
// check key usage if digitalSignature or nonRepudiation is set
boolean[] keyUsage = cert.getKeyUsage();
if (keyUsage != null && !keyUsage[0] && !keyUsage[1])
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.signingNotPermitted");
errors.add(msg);
}
// check extended key usage
try
{
byte[] ext = cert.getExtensionValue(EXT_KEY_USAGE);
if (ext != null)
{
ExtendedKeyUsage extKeyUsage = ExtendedKeyUsage
.getInstance(getObject(ext));
if (!extKeyUsage
.hasKeyPurposeId(KeyPurposeId.anyExtendedKeyUsage)
&& !extKeyUsage
.hasKeyPurposeId(KeyPurposeId.id_kp_emailProtection))
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.extKeyUsageNotPermitted");
errors.add(msg);
}
}
}
catch (Exception e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.extKeyUsageError", new Object[] {
e.getMessage(), e, e.getClass().getName() });
errors.add(msg);
}
// cert has an email address
try
{
Set certEmails = getEmailAddresses(cert);
if (certEmails.isEmpty())
{
// error no email address in signing certificate
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.noEmailInCert");
errors.add(msg);
}
else
{
// check if email in cert is equal to the from address in the
// message
boolean equalsFrom = false;
for (int i = 0; i < fromAddresses.length; i++)
{
if (certEmails.contains(fromAddresses[i].toLowerCase()))
{
equalsFrom = true;
break;
}
}
if (!equalsFrom)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.emailFromCertMismatch",
new Object[] {
new UntrustedInput(Arrays
.toString(fromAddresses)),
new UntrustedInput(certEmails) });
errors.add(msg);
}
}
}
catch (Exception e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.certGetEmailError", new Object[] {
e.getMessage(), e, e.getClass().getName() });
errors.add(msg);
}
}
public static Date getSignatureTime(SignerInformation signer)
{
AttributeTable atab = signer.getSignedAttributes();
Date result = null;
if (atab != null)
{
Attribute attr = atab.get(CMSAttributes.signingTime);
if (attr != null)
{
Time t = Time.getInstance(attr.getAttrValues().getObjectAt(0)
.getDERObject());
result = t.getDate();
}
}
return result;
}
private static List findCerts(List certStores, X509CertSelector selector)
throws CertStoreException
{
List result = new ArrayList();
Iterator it = certStores.iterator();
while (it.hasNext())
{
CertStore store = (CertStore) it.next();
Collection coll = store.getCertificates(selector);
result.addAll(coll);
}
return result;
}
private static X509Certificate findNextCert(List certStores, X509CertSelector selector, Set certSet)
throws CertStoreException
{
Iterator certIt = findCerts(certStores, selector).iterator();
boolean certFound = false;
X509Certificate nextCert = null;
while (certIt.hasNext())
{
nextCert = (X509Certificate) certIt.next();
if (!certSet.contains(nextCert))
{
certFound = true;
break;
}
}
return certFound ? nextCert : null;
}
/**
*
* @param signerCert the end of the path
* @param trustanchors trust anchors for the path
* @param certStores
* @return the resulting certificate path.
* @throws GeneralSecurityException
*/
public static CertPath createCertPath(X509Certificate signerCert,
Set trustanchors, List certStores) throws GeneralSecurityException
{
Object[] results = createCertPath(signerCert, trustanchors, certStores, null);
return (CertPath) results[0];
}
/**
* Returns an Object array containing a CertPath and a List of Booleans. The list contains the value true
* if the corresponding certificate in the CertPath was taken from the user provided CertStores.
* @param signerCert the end of the path
* @param trustanchors trust anchors for the path
* @param systemCertStores list of {@link CertStore} provided by the system
* @param userCertStores list of {@link CertStore} provided by the user
* @return a CertPath and a List of booleans.
* @throws GeneralSecurityException
*/
public static Object[] createCertPath(X509Certificate signerCert,
Set trustanchors, List systemCertStores, List userCertStores) throws GeneralSecurityException
{
Set certSet = new LinkedHashSet();
List userProvidedList = new ArrayList();
// add signer certificate
X509Certificate cert = signerCert;
certSet.add(cert);
userProvidedList.add(new Boolean(true));
boolean trustAnchorFound = false;
X509Certificate taCert = null;
// add other certs to the cert path
while (cert != null && !trustAnchorFound)
{
// check if cert Issuer is Trustanchor
Iterator trustIt = trustanchors.iterator();
while (trustIt.hasNext())
{
TrustAnchor anchor = (TrustAnchor) trustIt.next();
X509Certificate anchorCert = anchor.getTrustedCert();
if (anchorCert != null)
{
if (anchorCert.getSubjectX500Principal().equals(
cert.getIssuerX500Principal()))
{
try
{
cert.verify(anchorCert.getPublicKey(), "BC");
trustAnchorFound = true;
taCert = anchorCert;
break;
}
catch (Exception e)
{
// trustanchor not found
}
}
}
else
{
if (anchor.getCAName().equals(
cert.getIssuerX500Principal().getName()))
{
try
{
cert.verify(anchor.getCAPublicKey(), "BC");
trustAnchorFound = true;
break;
}
catch (Exception e)
{
// trustanchor not found
}
}
}
}
if (!trustAnchorFound)
{
// add next cert to path
X509CertSelector select = new X509CertSelector();
select.setSubject(cert.getIssuerX500Principal());
byte[] authKeyIdentBytes = cert.getExtensionValue(X509Extensions.AuthorityKeyIdentifier.getId());
if (authKeyIdentBytes != null)
{
try
{
AuthorityKeyIdentifier kid = AuthorityKeyIdentifier.getInstance(getObject(authKeyIdentBytes));
if (kid.getKeyIdentifier() != null)
{
select.setSubjectKeyIdentifier(new DEROctetString(kid.getKeyIdentifier()).getDEREncoded());
}
}
catch (IOException ioe)
{
// ignore
}
}
boolean userProvided = false;
cert = findNextCert(systemCertStores, select, certSet);
if (cert == null && userCertStores != null)
{
userProvided = true;
cert = findNextCert(userCertStores, select, certSet);
}
if (cert != null)
{
// cert found
certSet.add(cert);
userProvidedList.add(new Boolean(userProvided));
}
}
}
// if a trustanchor was found - try to find a selfsigned certificate of
// the trustanchor
if (trustAnchorFound)
{
if (taCert != null && taCert.getSubjectX500Principal().equals(taCert.getIssuerX500Principal()))
{
certSet.add(taCert);
userProvidedList.add(new Boolean(false));
}
else
{
X509CertSelector select = new X509CertSelector();
select.setSubject(cert.getIssuerX500Principal());
select.setIssuer(cert.getIssuerX500Principal());
boolean userProvided = false;
taCert = findNextCert(systemCertStores, select, certSet);
if (taCert == null && userCertStores != null)
{
userProvided = true;
taCert = findNextCert(userCertStores, select, certSet);
}
if (taCert != null)
{
try
{
cert.verify(taCert.getPublicKey(), "BC");
certSet.add(taCert);
userProvidedList.add(new Boolean(userProvided));
}
catch (GeneralSecurityException gse)
{
// wrong cert
}
}
}
}
CertPath certPath = CertificateFactory.getInstance("X.509", "BC").generateCertPath(new ArrayList(certSet));
return new Object[] {certPath, userProvidedList};
}
public CertStore getCertsAndCRLs()
{
return certs;
}
public SignerInformationStore getSignerInformationStore()
{
return signers;
}
public ValidationResult getValidationResult(SignerInformation signer)
throws SignedMailValidatorException
{
if (signers.getSigners(signer.getSID()).isEmpty())
{
// the signer is not part of the SignerInformationStore
// he has not signed the message
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.wrongSigner");
throw new SignedMailValidatorException(msg);
}
else
{
return (ValidationResult) results.get(signer);
}
}
public class ValidationResult
{
private PKIXCertPathReviewer review;
private List errors;
private List notifications;
private List userProvidedCerts;
private boolean signVerified;
ValidationResult(PKIXCertPathReviewer review, boolean verified,
List errors, List notifications, List userProvidedCerts)
{
this.review = review;
this.errors = errors;
this.notifications = notifications;
signVerified = verified;
this.userProvidedCerts = userProvidedCerts;
}
/**
* Returns a list of error messages of type {@link ErrorBundle}.
*
* @return List of error messages
*/
public List getErrors()
{
return errors;
}
/**
* Returns a list of notification messages of type {@link ErrorBundle}.
*
* @return List of notification messages
*/
public List getNotifications()
{
return notifications;
}
/**
*
* @return the PKIXCertPathReviewer for the CertPath of this signature
* or null if an Exception occured.
*/
public PKIXCertPathReviewer getCertPathReview()
{
return review;
}
/**
*
* @return the CertPath for this signature
* or null if an Exception occured.
*/
public CertPath getCertPath()
{
return review != null ? review.getCertPath() : null;
}
/**
*
* @return a List of Booleans that are true if the corresponding certificate in the CertPath was taken from
* the CertStore of the SMIME message
*/
public List getUserProvidedCerts()
{
return userProvidedCerts;
}
/**
*
* @return true if the signature corresponds to the public key of the
* signer
*/
public boolean isVerifiedSignature()
{
return signVerified;
}
/**
*
* @return true if the signature is valid (ie. if it corresponds to the
* public key of the signer and the cert path for the signers
* certificate is also valid)
*/
public boolean isValidSignature()
{
if (review != null)
{
return signVerified && review.isValidCertPath()
&& errors.isEmpty();
}
else
{
return false;
}
}
}
}