diff options
Diffstat (limited to 'libraries/spongycastle/mail/src/main/java/org/spongycastle/mail/smime/validator/SignedMailValidator.java')
-rw-r--r-- | libraries/spongycastle/mail/src/main/java/org/spongycastle/mail/smime/validator/SignedMailValidator.java | 960 |
1 files changed, 960 insertions, 0 deletions
diff --git a/libraries/spongycastle/mail/src/main/java/org/spongycastle/mail/smime/validator/SignedMailValidator.java b/libraries/spongycastle/mail/src/main/java/org/spongycastle/mail/smime/validator/SignedMailValidator.java new file mode 100644 index 000000000..21132852c --- /dev/null +++ b/libraries/spongycastle/mail/src/main/java/org/spongycastle/mail/smime/validator/SignedMailValidator.java @@ -0,0 +1,960 @@ +package org.bouncycastle.mail.smime.validator; + +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.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; + +import javax.mail.Address; +import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DEROctetString; +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.cert.jcajce.JcaCertStoreBuilder; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.cms.SignerInformationStore; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter; +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.util.Integers; +import org.bouncycastle.x509.CertPathReviewerException; +import org.bouncycastle.x509.PKIXCertPathReviewer; + +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 static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter(); + + 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: <br> - The + * validation date is changed to the signature time <br> - A CertStore with + * certificates and crls from the mail message is added to the CertStores.<br> + * <br> + * In <code>param</code> 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: <br> - The + * validation date is changed to the signature time <br> - A CertStore with + * certificates and crls from the mail message is added to the CertStores.<br> + * <br> + * In <code>param</code> 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; + boolean isSubclass = DEFAULT_CERT_PATH_REVIEWER.isAssignableFrom(certPathReviewerClass); + if(!isSubclass) + { + 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 = new JcaCertStoreBuilder().addCertificates(s.getCertificates()).addCRLs(s.getCRLs()).setProvider("BC").build(); + signers = s.getSignerInfos(); + + // save "from" addresses from message + Address[] froms = message.getFrom(); + InternetAddress sender = null; + try + { + if(message.getHeader("Sender") != null) + { + sender = new InternetAddress(message.getHeader("Sender")[0]); + } + } + catch (MessagingException ex) + { + //ignore garbage in Sender: header + } + fromAddresses = new String[froms.length + (sender!=null?1:0)]; + for (int i = 0; i < froms.length; i++) + { + InternetAddress inetAddr = (InternetAddress) froms[i]; + fromAddresses[i] = inetAddr.getAddress(); + } + if(sender!=null) + { + fromAddresses[froms.length] = sender.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(), selectorConverter.getCertSelector(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(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey())); + 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()); + } + catch (InstantiationException e) + { + throw new IllegalArgumentException("Cannot instantiate object of type " + + certPathReviewerClass.getName() + ": " + e.getMessage()); + } + 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) + { + ASN1Sequence altNames = ASN1Sequence.getInstance(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, false) + .getString().toLowerCase(); + addresses.add(email); + } + } + } + + return addresses; + } + + private static ASN1Primitive 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[]{Integers.valueOf(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( + addressesToString(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); + } + } + + static String addressesToString(Object[] a) + { + if (a == null) + { + return "null"; + } + + StringBuffer b = new StringBuffer(); + b.append('['); + + for (int i = 0; i != a.length; i++) + { + if (i > 0) + { + b.append(", "); + } + b.append(String.valueOf(a[i])); + } + + return b.append(']').toString(); + } + + 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) + .toASN1Primitive()); + 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 <code>true</code> + * 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(); + try + { + select.setSubject(cert.getIssuerX500Principal().getEncoded()); + } + catch (IOException e) + { + throw new IllegalStateException(e.toString()); + } + 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()).getEncoded(ASN1Encoding.DER)); + } + } + 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(); + + try + { + select.setSubject(cert.getIssuerX500Principal().getEncoded()); + select.setIssuer(cert.getIssuerX500Principal().getEncoded()); + } + catch (IOException e) + { + throw new IllegalStateException(e.toString()); + } + + 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; + } + } + + } +} |