001: package org.bouncycastle.mail.smime.test;
002:
003: import junit.framework.Test;
004: import junit.framework.TestCase;
005: import junit.framework.TestSuite;
006: import org.bouncycastle.asn1.ASN1Encodable;
007: import org.bouncycastle.asn1.x509.X509Extensions;
008: import org.bouncycastle.cms.SignerInformation;
009: import org.bouncycastle.i18n.ErrorBundle;
010: import org.bouncycastle.mail.smime.validator.SignedMailValidator;
011: import org.bouncycastle.x509.PKIXCertPathReviewer;
012: import org.bouncycastle.x509.extension.X509ExtensionUtil;
013:
014: import javax.mail.Session;
015: import javax.mail.internet.MimeMessage;
016: import java.io.InputStream;
017: import java.security.Security;
018: import java.security.cert.CertPath;
019: import java.security.cert.CertStore;
020: import java.security.cert.CertificateFactory;
021: import java.security.cert.CollectionCertStoreParameters;
022: import java.security.cert.PKIXParameters;
023: import java.security.cert.TrustAnchor;
024: import java.security.cert.X509CRL;
025: import java.security.cert.X509Certificate;
026: import java.util.ArrayList;
027: import java.util.HashSet;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Locale;
031: import java.util.Properties;
032: import java.util.Set;
033: import java.util.TimeZone;
034:
035: public class SignedMailValidatorTest extends TestCase {
036: static String TEST_TRUST_ACHOR = "validator.root.crt";
037:
038: public void testShortKey() throws Exception {
039: String message = "validator.shortKey.eml";
040: PKIXParameters params = createDefaultParams();
041: SignedMailValidator.ValidationResult result = doTest(message,
042: params);
043:
044: assertTrue(result.isValidSignature());
045: assertContainsMessage(result.getNotifications(),
046: "SignedMailValidator.shortSigningKey",
047: "Warning: The signing key is only 512 bits long.");
048: }
049:
050: public void testKeyUsage() throws Exception {
051: String message = "validator.keyUsage.eml";
052: PKIXParameters params = createDefaultParams();
053: SignedMailValidator.ValidationResult result = doTest(message,
054: params);
055:
056: assertTrue(result.isVerifiedSignature());
057: assertTrue(result.getCertPathReview().isValidCertPath());
058: assertFalse(result.isValidSignature());
059:
060: assertContainsMessage(
061: result.getErrors(),
062: "SignedMailValidator.signingNotPermitted",
063: "The key usage extension of signer certificate does not permit using the key for email signatures.");
064: }
065:
066: public void testExtKeyUsage() throws Exception {
067: String message = "validator.extKeyUsage.eml";
068: PKIXParameters params = createDefaultParams();
069: SignedMailValidator.ValidationResult result = doTest(message,
070: params);
071:
072: assertTrue(result.isVerifiedSignature());
073: assertTrue(result.getCertPathReview().isValidCertPath());
074: assertFalse(result.isValidSignature());
075:
076: assertContainsMessage(
077: result.getErrors(),
078: "SignedMailValidator.extKeyUsageNotPermitted",
079: "The extended key usage extension of the signer certificate does not permit using the key for email signatures.");
080: }
081:
082: public void testNoEmail() throws Exception {
083: String message = "validator.noEmail.eml";
084: PKIXParameters params = createDefaultParams();
085: SignedMailValidator.ValidationResult result = doTest(message,
086: params);
087:
088: assertTrue(result.isVerifiedSignature());
089: assertTrue(result.getCertPathReview().isValidCertPath());
090: assertFalse(result.isValidSignature());
091:
092: assertContainsMessage(
093: result.getErrors(),
094: "SignedMailValidator.noEmailInCert",
095: "The signer certificate is not usable for email signatures: it contains no email address.");
096: }
097:
098: public void testNotYetValid() throws Exception {
099: String message = "validator.notYetValid.eml";
100: PKIXParameters params = createDefaultParams();
101: SignedMailValidator.ValidationResult result = doTest(message,
102: params);
103:
104: assertTrue(result.isVerifiedSignature());
105: assertFalse(result.isValidSignature());
106: assertContainsMessage(
107: result.getErrors(),
108: "SignedMailValidator.certNotYetValid",
109: "The message was signed at Aug 28, 2006 3:04:01 PM GMT. But the certificate is not valid before Dec 28, 2006 2:19:31 PM GMT.");
110:
111: PKIXCertPathReviewer review = result.getCertPathReview();
112: assertFalse(review.isValidCertPath());
113: assertContainsMessage(
114: review.getErrors(0),
115: "CertPathReviewer.certificateNotYetValid",
116: "Could not validate the certificate. Certificate is not valid until Dec 28, 2006 2:19:31 PM GMT.");
117: }
118:
119: public void testExpired() throws Exception {
120: String message = "validator.expired.eml";
121: PKIXParameters params = createDefaultParams();
122: SignedMailValidator.ValidationResult result = doTest(message,
123: params);
124:
125: assertTrue(result.isVerifiedSignature());
126: assertFalse(result.isValidSignature());
127: assertContainsMessage(
128: result.getErrors(),
129: "SignedMailValidator.certExpired",
130: "The message was signed at Sep 1, 2006 9:08:35 AM GMT. But the certificate expired at Sep 1, 2006 8:39:20 AM GMT.");
131:
132: PKIXCertPathReviewer review = result.getCertPathReview();
133: assertFalse(review.isValidCertPath());
134: assertContainsMessage(
135: review.getErrors(0),
136: "CertPathReviewer.certificateExpired",
137: "Could not validate the certificate. Certificate expired on Sep 1, 2006 8:39:20 AM GMT.");
138: }
139:
140: public void testRevoked() throws Exception {
141: String message = "validator.revoked.eml";
142: PKIXParameters params = createDefaultParams();
143: List crlList = new ArrayList();
144: crlList.add(loadCRL("validator.revoked.crl"));
145: CertStore crls = CertStore.getInstance("Collection",
146: new CollectionCertStoreParameters(crlList));
147: params.addCertStore(crls);
148: params.setRevocationEnabled(true);
149:
150: SignedMailValidator.ValidationResult result = doTest(message,
151: params);
152:
153: assertTrue(result.isVerifiedSignature());
154: assertFalse(result.isValidSignature());
155:
156: PKIXCertPathReviewer review = result.getCertPathReview();
157: assertFalse(review.isValidCertPath());
158: assertContainsMessage(
159: review.getErrors(0),
160: "CertPathReviewer.certRevoked",
161: "The certificate was revoked at Sep 1, 2006 9:30:00 AM GMT. Reason: Key Compromise.");
162: }
163:
164: public void testLongValidity() throws Exception {
165: String message = "validator.longValidity.eml";
166: PKIXParameters params = createDefaultParams();
167:
168: SignedMailValidator.ValidationResult result = doTest(message,
169: params);
170:
171: assertTrue(result.isVerifiedSignature());
172: assertTrue(result.isValidSignature());
173:
174: assertContainsMessage(
175: result.getNotifications(),
176: "SignedMailValidator.longValidity",
177: "Warning: The signing certificate has a very long validity period: from Sep 1, 2006 11:00:00 AM GMT until Aug 8, 2106 11:00:00 AM GMT.");
178: }
179:
180: public void testCorrputRootStore() throws Exception {
181: String message = "validator.validMail.eml";
182: Set trustanchors = new HashSet();
183: trustanchors.add(getTrustAnchor(TEST_TRUST_ACHOR));
184: trustanchors.add(getTrustAnchor("validator.fakeRoot.crt"));
185: PKIXParameters params = new PKIXParameters(trustanchors);
186: params.setRevocationEnabled(false);
187:
188: SignedMailValidator.ValidationResult result = doTest(message,
189: params);
190:
191: assertTrue(result.isVerifiedSignature());
192: assertFalse(result.isValidSignature());
193:
194: PKIXCertPathReviewer review = result.getCertPathReview();
195:
196: assertFalse(review.isValidCertPath());
197: assertContainsMessage(
198: review.getErrors(-1),
199: "CertPathReviewer.conflictingTrustAnchors",
200: "Warning: corrupt trust root store: There are 2 trusted public keys for the CA \"CN=SignedMailValidatorTest Root, C=CH\" - please ensure with CA which is the correct key.");
201: }
202:
203: public void testCircular() throws Exception {
204: String message = "circular.eml";
205: PKIXParameters params = createDefaultParams();
206: SignedMailValidator.ValidationResult result = doTest(message,
207: params);
208:
209: assertTrue(result.isVerifiedSignature());
210: assertFalse(result.isValidSignature());
211: assertFalse(result.getCertPathReview().isValidCertPath());
212: assertTrue("cert path size", result.getCertPathReview()
213: .getCertPathSize() > 2);
214: }
215:
216: public void testExtendedReviewer() throws Exception {
217: try {
218: // Get a Session object with the default properties.
219: Properties props = System.getProperties();
220: Session session = Session.getDefaultInstance(props, null);
221:
222: // read message
223: MimeMessage msg = new MimeMessage(session, getClass()
224: .getResourceAsStream("validator.shortKey.eml"));
225:
226: SignedMailValidator validator = new SignedMailValidator(
227: msg, createDefaultParams(), String.class);
228: fail();
229: } catch (IllegalArgumentException e) {
230: assertTrue(e.getMessage().startsWith(
231: "certPathReviewerClass is not a subclass of"));
232: }
233:
234: try {
235: // Get a Session object with the default properties.
236: Properties props = System.getProperties();
237: Session session = Session.getDefaultInstance(props, null);
238:
239: // read message
240: MimeMessage msg = new MimeMessage(session, getClass()
241: .getResourceAsStream("validator.shortKey.eml"));
242:
243: SignedMailValidator validator = new SignedMailValidator(
244: msg, createDefaultParams(),
245: DummyCertPathReviewer.class);
246: SignerInformation sInfo = (SignerInformation) validator
247: .getSignerInformationStore().getSigners()
248: .iterator().next();
249: SignedMailValidator.ValidationResult result = validator
250: .getValidationResult(sInfo);
251:
252: assertTrue(result.isValidSignature());
253: assertContainsMessage(result.getNotifications(),
254: "SignedMailValidator.shortSigningKey",
255: "Warning: The signing key is only 512 bits long.");
256: } catch (IllegalArgumentException e) {
257: fail();
258: }
259: }
260:
261: public void testCreateCertPath() throws Exception {
262: // load trust anchor
263: Set trustanchors = new HashSet();
264: TrustAnchor ta = getTrustAnchor("certpath_root.crt");
265: trustanchors.add(ta);
266:
267: X509Certificate rootCert = ta.getTrustedCert();
268: X509Certificate interCert1 = loadCert("certpath_inter1.crt");
269: X509Certificate interCert2 = loadCert("certpath_inter2.crt");
270: X509Certificate endCert1 = loadCert("certpath_end1.crt");
271: X509Certificate endCert2 = loadCert("certpath_end2.crt");
272:
273: // init cert stores
274: List certStores = new ArrayList();
275: List certList = new ArrayList();
276: certList.add(interCert1);
277: certList.add(interCert2);
278: CertStore store = CertStore.getInstance("Collection",
279: new CollectionCertStoreParameters(certList));
280: certStores.add(store);
281:
282: // first path
283: CertPath path = SignedMailValidator.createCertPath(endCert1,
284: trustanchors, certStores);
285: assertTrue("path size is not 3",
286: path.getCertificates().size() == 3);
287: assertEquals("different end certificate", path
288: .getCertificates().get(0), endCert1);
289: assertEquals("different intermediate certificate", path
290: .getCertificates().get(1), interCert1);
291: assertEquals("different root certificate", path
292: .getCertificates().get(2), rootCert);
293:
294: // second path
295: path = SignedMailValidator.createCertPath(endCert2,
296: trustanchors, certStores);
297: assertTrue("path size is not 3",
298: path.getCertificates().size() == 3);
299: assertEquals("different end certificate", path
300: .getCertificates().get(0), endCert2);
301: assertEquals("different intermediate certificate", path
302: .getCertificates().get(1), interCert2);
303: assertEquals("different root certificate", path
304: .getCertificates().get(2), rootCert);
305: }
306:
307: private SignedMailValidator.ValidationResult doTest(String message,
308: PKIXParameters params) throws Exception {
309: // Get a Session object with the default properties.
310: Properties props = System.getProperties();
311: Session session = Session.getDefaultInstance(props, null);
312:
313: // read message
314: MimeMessage msg = new MimeMessage(session, getClass()
315: .getResourceAsStream(message));
316:
317: SignedMailValidator validator = new SignedMailValidator(msg,
318: params);
319: SignerInformation signer = (SignerInformation) validator
320: .getSignerInformationStore().getSigners().iterator()
321: .next();
322: return validator.getValidationResult(signer);
323: }
324:
325: private void assertContainsMessage(List msgList, String messageId,
326: String text) throws Exception {
327: Iterator it = msgList.iterator();
328: boolean found = false;
329: while (it.hasNext()) {
330: ErrorBundle message = (ErrorBundle) it.next();
331: if (message.getId().equals(messageId)) {
332: found = true;
333: assertEquals(text, message.getText(Locale.ENGLISH,
334: TimeZone.getTimeZone("GMT")));
335: break;
336: }
337: }
338: assertTrue("Expected message not found!", found);
339: }
340:
341: private PKIXParameters createDefaultParams() throws Exception {
342: Set trustanchors = new HashSet();
343: trustanchors.add(getTrustAnchor(TEST_TRUST_ACHOR));
344: PKIXParameters defParams = new PKIXParameters(trustanchors);
345: defParams.setRevocationEnabled(false);
346:
347: return defParams;
348: }
349:
350: private TrustAnchor getTrustAnchor(String trustcert)
351: throws Exception {
352: X509Certificate cert = loadCert(trustcert);
353: if (cert != null) {
354: byte[] ncBytes = cert
355: .getExtensionValue(X509Extensions.NameConstraints
356: .getId());
357:
358: if (ncBytes != null) {
359: ASN1Encodable extValue = X509ExtensionUtil
360: .fromExtensionValue(ncBytes);
361: return new TrustAnchor(cert, extValue.getDEREncoded());
362: }
363: return new TrustAnchor(cert, null);
364: }
365: return null;
366: }
367:
368: private X509Certificate loadCert(String certfile) throws Exception {
369: X509Certificate cert = null;
370: InputStream in = getClass().getResourceAsStream(certfile);
371:
372: CertificateFactory cf = CertificateFactory.getInstance("X.509",
373: "BC");
374: cert = (X509Certificate) cf.generateCertificate(in);
375: return cert;
376: }
377:
378: private X509CRL loadCRL(String crlfile) throws Exception {
379: X509CRL crl = null;
380: InputStream in = this .getClass().getResourceAsStream(crlfile);
381:
382: CertificateFactory cf = CertificateFactory.getInstance("x.509",
383: "BC");
384: crl = (X509CRL) cf.generateCRL(in);
385: return crl;
386: }
387:
388: public void setUp() {
389: if (Security.getProvider("BC") == null) {
390: Security
391: .addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
392: }
393: }
394:
395: public static void main(String[] args) throws Exception {
396: junit.textui.TestRunner.run(suite());
397: }
398:
399: public static Test suite() throws Exception {
400: TestSuite suite = new TestSuite("SignedMailValidator Tests");
401:
402: suite.addTestSuite(SignedMailValidatorTest.class);
403:
404: return suite;
405: }
406:
407: }
|