001: package org.bouncycastle.mail.smime.examples;
002:
003: import java.io.FileInputStream;
004: import java.io.InputStream;
005: import java.security.KeyPairGenerator;
006: import java.security.KeyStore;
007: import java.security.PublicKey;
008: import java.security.SecureRandom;
009: import java.security.Security;
010: import java.security.cert.CertStore;
011: import java.security.cert.CertificateFactory;
012: import java.security.cert.CollectionCertStoreParameters;
013: import java.security.cert.PKIXParameters;
014: import java.security.cert.TrustAnchor;
015: import java.security.cert.X509CRL;
016: import java.security.cert.X509Certificate;
017: import java.util.ArrayList;
018: import java.util.HashSet;
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.Locale;
022: import java.util.Properties;
023: import java.util.Set;
024:
025: import javax.mail.Session;
026: import javax.mail.internet.MimeMessage;
027: import javax.security.auth.x500.X500Principal;
028:
029: import org.bouncycastle.asn1.ASN1Encodable;
030: import org.bouncycastle.asn1.x509.X509Extensions;
031: import org.bouncycastle.cms.SignerInformation;
032: import org.bouncycastle.i18n.ErrorBundle;
033: import org.bouncycastle.jce.provider.BouncyCastleProvider;
034: import org.bouncycastle.mail.smime.validator.SignedMailValidator;
035: import org.bouncycastle.x509.PKIXCertPathReviewer;
036: import org.bouncycastle.x509.extension.X509ExtensionUtil;
037:
038: /**
039: * An Example that reads a signed mail and validates its signature. Also
040: * validating the certificate path from the signers key to a trusted entity
041: */
042: public class ValidateSignedMail {
043:
044: /*
045: * Use trusted certificates from $JAVA_HOME/lib/security/cacerts as
046: * trustanchors
047: */
048: public static final boolean useCaCerts = false;
049:
050: public static void main(String[] args) throws Exception {
051:
052: Security.addProvider(new BouncyCastleProvider());
053:
054: //
055: // Get a Session object with the default properties.
056: //
057: Properties props = System.getProperties();
058:
059: Session session = Session.getDefaultInstance(props, null);
060:
061: // read message
062: MimeMessage msg = new MimeMessage(session, new FileInputStream(
063: "signed.message"));
064:
065: // create PKIXparameters
066: PKIXParameters param;
067:
068: if (useCaCerts) {
069: KeyStore caCerts = KeyStore.getInstance("JKS");
070: String javaHome = System.getProperty("java.home");
071: caCerts.load(new FileInputStream(javaHome
072: + "/lib/security/cacerts"), "changeit"
073: .toCharArray());
074:
075: param = new PKIXParameters(caCerts);
076: } else {
077: // load trustanchors from files (here we only load one)
078: Set trustanchors = new HashSet();
079: TrustAnchor trust = getTrustAnchor("trustanchor");
080:
081: // create a dummy trustanchor if we can not find any trustanchor. so
082: // we can still try to validate the message
083: if (trust == null) {
084: System.out
085: .println("no trustanchor file found, using a dummy trustanchor");
086: trust = getDummyTrustAnchor();
087: }
088: trustanchors.add(trust);
089:
090: param = new PKIXParameters(trustanchors);
091: }
092:
093: // load one ore more crls from files (here we only load one crl)
094: List crls = new ArrayList();
095: X509CRL crl = loadCRL("crl.file");
096: if (crl != null) {
097: crls.add(crl);
098: }
099: CertStore certStore = CertStore.getInstance("Collection",
100: new CollectionCertStoreParameters(crls), "BC");
101:
102: // add crls and enable revocation checking
103: param.addCertStore(certStore);
104: param.setRevocationEnabled(true);
105:
106: // or disable revocation checking
107: // param.setRevocationEnabled(false);
108:
109: verifySignedMail(msg, param);
110: }
111:
112: public static final int TITLE = 0;
113: public static final int TEXT = 1;
114: public static final int SUMMARY = 2;
115: public static final int DETAIL = 3;
116:
117: static int dbgLvl = DETAIL;
118:
119: private static final String RESOURCE_NAME = "org.bouncycastle.mail.smime.validator.SignedMailValidatorMessages";
120:
121: public static void verifySignedMail(MimeMessage msg,
122: PKIXParameters param) throws Exception {
123: // set locale for the output
124: Locale loc = Locale.ENGLISH;
125: // Locale loc = Locale.GERMAN;
126:
127: // validate signatures
128: SignedMailValidator validator = new SignedMailValidator(msg,
129: param);
130:
131: // iterate over all signatures and print results
132: Iterator it = validator.getSignerInformationStore()
133: .getSigners().iterator();
134: while (it.hasNext()) {
135: SignerInformation signer = (SignerInformation) it.next();
136: SignedMailValidator.ValidationResult result = validator
137: .getValidationResult(signer);
138: if (result.isValidSignature()) {
139: ErrorBundle errMsg = new ErrorBundle(RESOURCE_NAME,
140: "SignedMailValidator.sigValid");
141: System.out.println(errMsg.getText(loc));
142: } else {
143: ErrorBundle errMsg = new ErrorBundle(RESOURCE_NAME,
144: "SignedMailValidator.sigInvalid");
145: System.out.println(errMsg.getText(loc));
146: // print errors
147: System.out.println("Errors:");
148: Iterator errorsIt = result.getErrors().iterator();
149: while (errorsIt.hasNext()) {
150: ErrorBundle errorMsg = (ErrorBundle) errorsIt
151: .next();
152: if (dbgLvl == DETAIL) {
153: System.out.println("\t\t"
154: + errorMsg.getDetail(loc));
155: } else {
156: System.out.println("\t\t"
157: + errorMsg.getText(loc));
158: }
159: }
160: }
161: if (!result.getNotifications().isEmpty()) {
162: System.out.println("Notifications:");
163: Iterator notIt = result.getNotifications().iterator();
164: while (notIt.hasNext()) {
165: ErrorBundle notMsg = (ErrorBundle) notIt.next();
166: if (dbgLvl == DETAIL) {
167: System.out.println("\t\t"
168: + notMsg.getDetail(loc));
169: } else {
170: System.out
171: .println("\t\t" + notMsg.getText(loc));
172: }
173: }
174: }
175: PKIXCertPathReviewer review = result.getCertPathReview();
176: if (review != null) {
177: if (review.isValidCertPath()) {
178: System.out.println("Certificate path valid");
179: } else {
180: System.out.println("Certificate path invalid");
181: }
182:
183: System.out
184: .println("\nCertificate path validation results:");
185: // global errors
186: System.out.println("Errors:");
187: Iterator errorsIt = review.getErrors(-1).iterator();
188: while (errorsIt.hasNext()) {
189: ErrorBundle errorMsg = (ErrorBundle) errorsIt
190: .next();
191: if (dbgLvl == DETAIL) {
192: System.out.println("\t\t"
193: + errorMsg.getDetail(loc));
194: } else {
195: System.out.println("\t\t"
196: + errorMsg.getText(loc));
197: }
198: }
199:
200: System.out.println("Notifications:");
201: Iterator notificationsIt = review.getNotifications(-1)
202: .iterator();
203: while (notificationsIt.hasNext()) {
204: ErrorBundle noteMsg = (ErrorBundle) notificationsIt
205: .next();
206: System.out.println("\t" + noteMsg.getText(loc));
207: }
208:
209: // per certificate errors and notifications
210: Iterator certIt = review.getCertPath()
211: .getCertificates().iterator();
212: int i = 0;
213: while (certIt.hasNext()) {
214: X509Certificate cert = (X509Certificate) certIt
215: .next();
216: System.out.println("\nCertificate " + i
217: + "\n========");
218: System.out.println("Issuer: "
219: + cert.getIssuerDN().getName());
220: System.out.println("Subject: "
221: + cert.getSubjectDN().getName());
222:
223: // errors
224: System.out.println("\tErrors:");
225: errorsIt = review.getErrors(i).iterator();
226: while (errorsIt.hasNext()) {
227: ErrorBundle errorMsg = (ErrorBundle) errorsIt
228: .next();
229: if (dbgLvl == DETAIL) {
230: System.out.println("\t\t"
231: + errorMsg.getDetail(loc));
232: } else {
233: System.out.println("\t\t"
234: + errorMsg.getText(loc));
235: }
236: }
237:
238: // notifications
239: System.out.println("\tNotifications:");
240: notificationsIt = review.getNotifications(i)
241: .iterator();
242: while (notificationsIt.hasNext()) {
243: ErrorBundle noteMsg = (ErrorBundle) notificationsIt
244: .next();
245: if (dbgLvl == DETAIL) {
246: System.out.println("\t\t"
247: + noteMsg.getDetail(loc));
248: } else {
249: System.out.println("\t\t"
250: + noteMsg.getText(loc));
251: }
252: }
253:
254: i++;
255: }
256: }
257: }
258:
259: }
260:
261: protected static TrustAnchor getTrustAnchor(String trustcert)
262: throws Exception {
263: X509Certificate cert = loadCert(trustcert);
264: if (cert != null) {
265: byte[] ncBytes = cert
266: .getExtensionValue(X509Extensions.NameConstraints
267: .getId());
268:
269: if (ncBytes != null) {
270: ASN1Encodable extValue = X509ExtensionUtil
271: .fromExtensionValue(ncBytes);
272: return new TrustAnchor(cert, extValue.getDEREncoded());
273: }
274: return new TrustAnchor(cert, null);
275: }
276: return null;
277: }
278:
279: protected static X509Certificate loadCert(String certfile) {
280: X509Certificate cert = null;
281: try {
282: InputStream in = new FileInputStream(certfile);
283:
284: CertificateFactory cf = CertificateFactory.getInstance(
285: "X.509", "BC");
286: cert = (X509Certificate) cf.generateCertificate(in);
287: } catch (Exception e) {
288: System.out.println("certfile \"" + certfile
289: + "\" not found - classpath is "
290: + System.getProperty("java.class.path"));
291: }
292: return cert;
293: }
294:
295: protected static X509CRL loadCRL(String crlfile) {
296: X509CRL crl = null;
297: try {
298: InputStream in = new FileInputStream(crlfile);
299:
300: CertificateFactory cf = CertificateFactory.getInstance(
301: "X.509", "BC");
302: crl = (X509CRL) cf.generateCRL(in);
303: } catch (Exception e) {
304: System.out.println("crlfile \"" + crlfile
305: + "\" not found - classpath is "
306: + System.getProperty("java.class.path"));
307: }
308: return crl;
309: }
310:
311: private static TrustAnchor getDummyTrustAnchor() throws Exception {
312: X500Principal principal = new X500Principal(
313: "CN=Dummy Trust Anchor");
314: KeyPairGenerator kpg = KeyPairGenerator
315: .getInstance("RSA", "BC");
316: kpg.initialize(1024, new SecureRandom());
317: PublicKey trustPubKey = kpg.generateKeyPair().getPublic();
318: return new TrustAnchor(principal, trustPubKey, null);
319: }
320:
321: }
|