001: /*
002: * Copyright 2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.ws.soap.security.xwss.callback;
018:
019: import java.io.File;
020: import java.io.IOException;
021: import java.math.BigInteger;
022: import java.security.GeneralSecurityException;
023: import java.security.InvalidAlgorithmParameterException;
024: import java.security.KeyStore;
025: import java.security.PrivateKey;
026: import java.security.PublicKey;
027: import java.security.cert.CertPathBuilder;
028: import java.security.cert.CertPathBuilderException;
029: import java.security.cert.Certificate;
030: import java.security.cert.CertificateExpiredException;
031: import java.security.cert.CertificateNotYetValidException;
032: import java.security.cert.PKIXBuilderParameters;
033: import java.security.cert.X509CertSelector;
034: import java.security.cert.X509Certificate;
035: import java.util.Arrays;
036: import java.util.Enumeration;
037: import javax.crypto.SecretKey;
038:
039: import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
040: import com.sun.xml.wss.impl.callback.DecryptionKeyCallback;
041: import com.sun.xml.wss.impl.callback.EncryptionKeyCallback;
042: import com.sun.xml.wss.impl.callback.SignatureKeyCallback;
043: import com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback;
044: import org.apache.xml.security.utils.RFC2253Parser;
045: import org.springframework.beans.factory.InitializingBean;
046: import org.springframework.core.io.FileSystemResource;
047: import org.springframework.core.io.Resource;
048: import org.springframework.util.StringUtils;
049: import org.springframework.ws.soap.security.support.KeyStoreFactoryBean;
050:
051: /**
052: * Callback handler that uses Java Security <code>KeyStore</code>s to handle cryptographic callbacks. Allows for
053: * specific key stores to be set for various cryptographic operations.
054: * <p/>
055: * This handler requires one or more key stores to be set. You can configure them in your application context by using a
056: * <code>KeyStoreFactoryBean</code>. The exact stores to be set depends on the cryptographic operations that are to be
057: * performed by this handler. The table underneath show the key store to be used for each operation: <table border="1">
058: * <tr> <td><strong>Cryptographic operation</strong></td> <td><strong>Key store used</strong></td> </tr> <tr>
059: * <td>Certificate validation</td> <td>first <code>keyStore</code>, then <code>trustStore</code></td> </tr> <tr>
060: * <td>Decryption based on private key</td> <td><code>keyStore</code></td> </tr> <tr> <td>Decryption based on symmetric
061: * key</td> <td><code>symmetricStore</code></td> </tr> <tr> <td>Encryption based on certificate</td>
062: * <td><code>trustStore</code></td> </tr> <tr> <td>Encryption based on symmetric key</td>
063: * <td><code>symmetricStore</code></td> </tr> <tr> <td>Signing</td> <td><code>keyStore</code></td> </tr> <tr>
064: * <td>Signature verification</td> <td><code>trustStore</code></td> </tr> </table>
065: * <p/>
066: * <h3>Default key stores</h3> If the <code>symmetricStore</code> is not set, it will default to the
067: * <code>keyStore</code>. If the key or trust store is not set, this handler will use the standard Java mechanism to
068: * load or create it. See {@link #loadDefaultKeyStore()} and {@link #loadDefaultTrustStore()}.
069: * <p/>
070: * <h3>Examples</h3> For instance, if you want to use the <code>KeyStoreCallbackHandler</code> to validate incoming
071: * certificates or signatures, you would use a trust store, like so:
072: * <pre>
073: * <bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
074: * <property name="trustStore" ref="trustStore"/>
075: * </bean>
076: * <p/>
077: * <bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
078: * <property name="location" value="classpath:truststore.jks"/>
079: * <property name="password" value="changeit"/>
080: * </bean>
081: * </pre>
082: * If you want to use it to decrypt incoming certificates or sign outgoing messages, you would use a key store, like
083: * so:
084: * <pre>
085: * <bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
086: * <property name="keyStore" ref="keyStore"/>
087: * <property name="privateKeyPassword" value="changeit"/>
088: * </bean>
089: * <p/>
090: * <bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
091: * <property name="location" value="classpath:keystore.jks"/>
092: * <property name="password" value="changeit"/>
093: * </bean>
094: * </pre>
095: * <p/>
096: * <h3>Handled callbacks</h3> This class handles <code>CertificateValidationCallback</code>s,
097: * <code>DecryptionKeyCallback</code>s, <code>EncryptionKeyCallback</code>s, <code>SignatureKeyCallback</code>s, and
098: * <code>SignatureVerificationKeyCallback</code>s. It throws an <code>UnsupportedCallbackException</code> for others.
099: *
100: * @author Arjen Poutsma
101: * @see KeyStore
102: * @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
103: * @see CertificateValidationCallback
104: * @see DecryptionKeyCallback
105: * @see EncryptionKeyCallback
106: * @see SignatureKeyCallback
107: * @see SignatureVerificationKeyCallback
108: * @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager">The
109: * standard Java trust store mechanism</a>
110: */
111: public class KeyStoreCallbackHandler extends
112: CryptographyCallbackHandler implements InitializingBean {
113:
114: private static final String X_509_CERTIFICATE_TYPE = "X.509";
115:
116: private static final String SUBJECT_KEY_IDENTIFIER_OID = "2.5.29.14";
117:
118: private KeyStore keyStore;
119:
120: private KeyStore symmetricStore;
121:
122: private KeyStore trustStore;
123:
124: private String defaultAlias;
125:
126: private char[] privateKeyPassword;
127:
128: private char[] symmetricKeyPassword;
129:
130: private static X509Certificate getCertificate(String alias,
131: KeyStore store) throws IOException {
132: try {
133: return (X509Certificate) store.getCertificate(alias);
134: } catch (GeneralSecurityException e) {
135: throw new IOException(e.getMessage());
136: }
137: }
138:
139: private static X509Certificate getCertificate(PublicKey pk,
140: KeyStore store) throws IOException {
141: try {
142: Enumeration aliases = store.aliases();
143: while (aliases.hasMoreElements()) {
144: String alias = (String) aliases.nextElement();
145: Certificate cert = store.getCertificate(alias);
146: if (cert == null
147: || !X_509_CERTIFICATE_TYPE.equals(cert
148: .getType())) {
149: continue;
150: }
151: X509Certificate x509Cert = (X509Certificate) cert;
152: if (x509Cert.getPublicKey().equals(pk)) {
153: return x509Cert;
154: }
155: }
156: } catch (GeneralSecurityException e) {
157: throw new IOException(e.getMessage());
158: }
159: return null;
160: }
161:
162: /** Sets the key store alias for the default certificate and private key. */
163: public void setDefaultAlias(String defaultAlias) {
164: this .defaultAlias = defaultAlias;
165: }
166:
167: /**
168: * Sets the default key store. This property is required for decription based on private keys, and signing. If this
169: * property is not set, a default key store is loaded.
170: *
171: * @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
172: * @see #loadDefaultTrustStore()
173: */
174: public void setKeyStore(KeyStore keyStore) {
175: this .keyStore = keyStore;
176: }
177:
178: /**
179: * Sets the password used to retrieve private keys from the keystore. This property is required for decription based
180: * on private keys, and signing.
181: */
182: public void setPrivateKeyPassword(String privateKeyPassword) {
183: if (privateKeyPassword != null) {
184: this .privateKeyPassword = privateKeyPassword.toCharArray();
185: }
186: }
187:
188: /**
189: * Sets the password used to retrieve keys from the symmetric keystore. If this property is not set, it default to
190: * the private key password.
191: *
192: * @see #setPrivateKeyPassword(String)
193: */
194: public void setSymmetricKeyPassword(String symmetricKeyPassword) {
195: if (symmetricKeyPassword != null) {
196: this .symmetricKeyPassword = symmetricKeyPassword
197: .toCharArray();
198: }
199: }
200:
201: /**
202: * Sets the key store used for encryption and decryption using symmetric keys. If this property is not set, it
203: * defaults to the <code>keyStore</code> property.
204: *
205: * @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
206: * @see #setKeyStore(java.security.KeyStore)
207: */
208: public void setSymmetricStore(KeyStore symmetricStore) {
209: this .symmetricStore = symmetricStore;
210: }
211:
212: /**
213: * Sets the key store used for signature verifications and encryptions. If this property is not set, a default key
214: * store will be loaded.
215: *
216: * @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
217: * @see #loadDefaultTrustStore()
218: */
219: public void setTrustStore(KeyStore trustStore) {
220: this .trustStore = trustStore;
221: }
222:
223: public void afterPropertiesSet() throws Exception {
224: if (keyStore == null) {
225: loadDefaultKeyStore();
226: }
227: if (trustStore == null) {
228: loadDefaultTrustStore();
229: }
230: if (symmetricStore == null) {
231: symmetricStore = keyStore;
232: }
233: if (symmetricKeyPassword == null) {
234: symmetricKeyPassword = privateKeyPassword;
235: }
236: }
237:
238: protected final void handleAliasPrivKeyCertRequest(
239: SignatureKeyCallback callback,
240: SignatureKeyCallback.AliasPrivKeyCertRequest request)
241: throws IOException {
242: PrivateKey privateKey = getPrivateKey(request.getAlias());
243: X509Certificate certificate = getCertificate(request.getAlias());
244: request.setPrivateKey(privateKey);
245: request.setX509Certificate(certificate);
246: }
247:
248: protected final void handleAliasSymmetricKeyRequest(
249: DecryptionKeyCallback callback,
250: DecryptionKeyCallback.AliasSymmetricKeyRequest request)
251: throws IOException {
252: SecretKey secretKey = getSymmetricKey(request.getAlias());
253: request.setSymmetricKey(secretKey);
254: }
255:
256: //
257: // Encryption
258: //
259:
260: protected final void handleAliasSymmetricKeyRequest(
261: EncryptionKeyCallback callback,
262: EncryptionKeyCallback.AliasSymmetricKeyRequest request)
263: throws IOException {
264: SecretKey secretKey = getSymmetricKey(request.getAlias());
265: request.setSymmetricKey(secretKey);
266: }
267:
268: protected final void handleAliasX509CertificateRequest(
269: EncryptionKeyCallback callback,
270: EncryptionKeyCallback.AliasX509CertificateRequest request)
271: throws IOException {
272: X509Certificate certificate = getCertificateFromTrustStore(request
273: .getAlias());
274: request.setX509Certificate(certificate);
275: }
276:
277: //
278: // Certificate validation
279: //
280:
281: protected final void handleCertificateValidationCallback(
282: CertificateValidationCallback callback) {
283: callback.setValidator(new KeyStoreCertificateValidator());
284: }
285:
286: //
287: // Signing
288: //
289:
290: protected final void handleDefaultPrivKeyCertRequest(
291: SignatureKeyCallback callback,
292: SignatureKeyCallback.DefaultPrivKeyCertRequest request)
293: throws IOException {
294: PrivateKey privateKey = getPrivateKey(defaultAlias);
295: X509Certificate certificate = getCertificate(defaultAlias);
296: request.setPrivateKey(privateKey);
297: request.setX509Certificate(certificate);
298: }
299:
300: protected final void handleDefaultX509CertificateRequest(
301: EncryptionKeyCallback callback,
302: EncryptionKeyCallback.DefaultX509CertificateRequest request)
303: throws IOException {
304: X509Certificate certificate = getCertificateFromTrustStore(defaultAlias);
305: request.setX509Certificate(certificate);
306: }
307:
308: protected final void handlePublicKeyBasedPrivKeyCertRequest(
309: SignatureKeyCallback callback,
310: SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest request)
311: throws IOException {
312: PrivateKey privateKey = getPrivateKey(request.getPublicKey());
313: X509Certificate certificate = getCertificate(request
314: .getPublicKey());
315: request.setPrivateKey(privateKey);
316: request.setX509Certificate(certificate);
317: }
318:
319: //
320: // Decryption
321: //
322: protected final void handlePublicKeyBasedPrivKeyRequest(
323: DecryptionKeyCallback callback,
324: DecryptionKeyCallback.PublicKeyBasedPrivKeyRequest request)
325: throws IOException {
326: PrivateKey key = getPrivateKey(request.getPublicKey());
327: request.setPrivateKey(key);
328: }
329:
330: protected final void handlePublicKeyBasedRequest(
331: EncryptionKeyCallback callback,
332: EncryptionKeyCallback.PublicKeyBasedRequest request)
333: throws IOException {
334: X509Certificate certificate = getCertificateFromTrustStore(request
335: .getPublicKey());
336: request.setX509Certificate(certificate);
337: }
338:
339: protected final void handlePublicKeyBasedRequest(
340: SignatureVerificationKeyCallback callback,
341: SignatureVerificationKeyCallback.PublicKeyBasedRequest request)
342: throws IOException {
343: X509Certificate certificate = getCertificateFromTrustStore(request
344: .getPublicKey());
345: request.setX509Certificate(certificate);
346: }
347:
348: protected final void handleX509CertificateBasedRequest(
349: DecryptionKeyCallback callback,
350: DecryptionKeyCallback.X509CertificateBasedRequest request)
351: throws IOException {
352: PrivateKey privKey = getPrivateKey(request.getX509Certificate());
353: request.setPrivateKey(privKey);
354: }
355:
356: protected final void handleX509IssuerSerialBasedRequest(
357: DecryptionKeyCallback callback,
358: DecryptionKeyCallback.X509IssuerSerialBasedRequest request)
359: throws IOException {
360: PrivateKey key = getPrivateKey(request.getIssuerName(), request
361: .getSerialNumber());
362: request.setPrivateKey(key);
363: }
364:
365: protected final void handleX509IssuerSerialBasedRequest(
366: SignatureVerificationKeyCallback callback,
367: SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest request)
368: throws IOException {
369: X509Certificate certificate = getCertificateFromTrustStore(
370: request.getIssuerName(), request.getSerialNumber());
371: request.setX509Certificate(certificate);
372: }
373:
374: protected final void handleX509SubjectKeyIdentifierBasedRequest(
375: DecryptionKeyCallback callback,
376: DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest request)
377: throws IOException {
378: PrivateKey key = getPrivateKey(request
379: .getSubjectKeyIdentifier());
380: request.setPrivateKey(key);
381: }
382:
383: //
384: // Signature verification
385: //
386:
387: protected final void handleX509SubjectKeyIdentifierBasedRequest(
388: SignatureVerificationKeyCallback callback,
389: SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBasedRequest request)
390: throws IOException {
391: X509Certificate certificate = getCertificateFromTrustStore(request
392: .getSubjectKeyIdentifier());
393: request.setX509Certificate(certificate);
394: }
395:
396: // Certificate methods
397:
398: protected X509Certificate getCertificate(String alias)
399: throws IOException {
400: return getCertificate(alias, keyStore);
401: }
402:
403: protected X509Certificate getCertificate(PublicKey pk)
404: throws IOException {
405: return getCertificate(pk, keyStore);
406: }
407:
408: protected X509Certificate getCertificateFromTrustStore(String alias)
409: throws IOException {
410: return getCertificate(alias, trustStore);
411: }
412:
413: protected X509Certificate getCertificateFromTrustStore(
414: byte[] subjectKeyIdentifier) throws IOException {
415: try {
416: Enumeration aliases = trustStore.aliases();
417: while (aliases.hasMoreElements()) {
418: String alias = (String) aliases.nextElement();
419: Certificate cert = trustStore.getCertificate(alias);
420: if (cert == null
421: || !X_509_CERTIFICATE_TYPE.equals(cert
422: .getType())) {
423: continue;
424: }
425: X509Certificate x509Cert = (X509Certificate) cert;
426: byte[] keyId = getSubjectKeyIdentifier(x509Cert);
427: if (keyId == null) {
428: // Cert does not contain a key identifier
429: continue;
430: }
431: if (Arrays.equals(subjectKeyIdentifier, keyId)) {
432: return x509Cert;
433: }
434: }
435: } catch (GeneralSecurityException e) {
436: throw new IOException(e.getMessage());
437: }
438: return null;
439: }
440:
441: protected X509Certificate getCertificateFromTrustStore(PublicKey pk)
442: throws IOException {
443: return getCertificate(pk, trustStore);
444: }
445:
446: protected X509Certificate getCertificateFromTrustStore(
447: String issuerName, BigInteger serialNumber)
448: throws IOException {
449: try {
450: Enumeration aliases = trustStore.aliases();
451: while (aliases.hasMoreElements()) {
452: String alias = (String) aliases.nextElement();
453: Certificate cert = trustStore.getCertificate(alias);
454: if (cert == null
455: || !X_509_CERTIFICATE_TYPE.equals(cert
456: .getType())) {
457: continue;
458: }
459: X509Certificate x509Cert = (X509Certificate) cert;
460: String this IssuerName = RFC2253Parser
461: .normalize(x509Cert.getIssuerDN().getName());
462: BigInteger this SerialNumber = x509Cert
463: .getSerialNumber();
464: if (this IssuerName.equals(issuerName)
465: && this SerialNumber.equals(serialNumber)) {
466: return x509Cert;
467: }
468: }
469: } catch (GeneralSecurityException e) {
470: throw new IOException(e.getMessage());
471: }
472: return null;
473: }
474:
475: // Private Key methods
476:
477: protected PrivateKey getPrivateKey(String alias) throws IOException {
478: try {
479: return (PrivateKey) keyStore.getKey(alias,
480: privateKeyPassword);
481: } catch (GeneralSecurityException e) {
482: throw new IOException(e.getMessage());
483: }
484: }
485:
486: protected PrivateKey getPrivateKey(PublicKey publicKey)
487: throws IOException {
488: try {
489: Enumeration aliases = keyStore.aliases();
490: while (aliases.hasMoreElements()) {
491: String alias = (String) aliases.nextElement();
492: if (keyStore.isKeyEntry(alias)) {
493: // Just returning the first one here
494: return (PrivateKey) keyStore.getKey(alias,
495: privateKeyPassword);
496: }
497: }
498: } catch (GeneralSecurityException e) {
499: throw new IOException(e.getMessage());
500: }
501: return null;
502: }
503:
504: protected PrivateKey getPrivateKey(X509Certificate certificate)
505: throws IOException {
506: try {
507: Enumeration aliases = keyStore.aliases();
508: while (aliases.hasMoreElements()) {
509: String alias = (String) aliases.nextElement();
510: if (!keyStore.isKeyEntry(alias)) {
511: continue;
512: }
513: Certificate cert = keyStore.getCertificate(alias);
514: if (cert != null && cert.equals(certificate)) {
515: return (PrivateKey) keyStore.getKey(alias,
516: privateKeyPassword);
517: }
518: }
519: } catch (GeneralSecurityException e) {
520: throw new IOException(e.getMessage());
521: }
522: return null;
523: }
524:
525: protected PrivateKey getPrivateKey(byte[] keyIdentifier)
526: throws IOException {
527: try {
528: Enumeration aliases = keyStore.aliases();
529: while (aliases.hasMoreElements()) {
530: String alias = (String) aliases.nextElement();
531: if (!keyStore.isKeyEntry(alias)) {
532: continue;
533: }
534: Certificate cert = keyStore.getCertificate(alias);
535: if (cert == null || !"X.509".equals(cert.getType())) {
536: continue;
537: }
538: X509Certificate x509Cert = (X509Certificate) cert;
539: byte[] keyId = getSubjectKeyIdentifier(x509Cert);
540: if (keyId == null) {
541: // Cert does not contain a key identifier
542: continue;
543: }
544: if (Arrays.equals(keyIdentifier, keyId)) {
545: return (PrivateKey) keyStore.getKey(alias,
546: privateKeyPassword);
547: }
548: }
549: } catch (GeneralSecurityException e) {
550: throw new IOException(e.getMessage());
551: }
552: return null;
553: }
554:
555: protected PrivateKey getPrivateKey(String issuerName,
556: BigInteger serialNumber) throws IOException {
557: try {
558: Enumeration aliases = keyStore.aliases();
559: while (aliases.hasMoreElements()) {
560: String alias = (String) aliases.nextElement();
561: if (!keyStore.isKeyEntry(alias)) {
562: continue;
563: }
564: Certificate cert = keyStore.getCertificate(alias);
565: if (cert == null || !"X.509".equals(cert.getType())) {
566: continue;
567: }
568: X509Certificate x509Cert = (X509Certificate) cert;
569: String this IssuerName = RFC2253Parser
570: .normalize(x509Cert.getIssuerDN().getName());
571: BigInteger this SerialNumber = x509Cert
572: .getSerialNumber();
573: if (this IssuerName.equals(issuerName)
574: && this SerialNumber.equals(serialNumber)) {
575: return (PrivateKey) keyStore.getKey(alias,
576: privateKeyPassword);
577: }
578: }
579: } catch (GeneralSecurityException e) {
580: throw new IOException(e.getMessage());
581: }
582: return null;
583: }
584:
585: // Utility methods
586:
587: protected final byte[] getSubjectKeyIdentifier(X509Certificate cert) {
588: byte[] subjectKeyIdentifier = cert
589: .getExtensionValue(SUBJECT_KEY_IDENTIFIER_OID);
590: if (subjectKeyIdentifier == null) {
591: return null;
592: }
593: byte[] dest = new byte[subjectKeyIdentifier.length - 4];
594: System.arraycopy(subjectKeyIdentifier, 4, dest, 0,
595: subjectKeyIdentifier.length - 4);
596: return dest;
597: }
598:
599: //
600: // Symmetric key methods
601: //
602:
603: protected SecretKey getSymmetricKey(String alias)
604: throws IOException {
605: try {
606: return (SecretKey) symmetricStore.getKey(alias,
607: symmetricKeyPassword);
608: } catch (GeneralSecurityException e) {
609: throw new IOException(e.getMessage());
610: }
611: }
612:
613: /**
614: * Loads the key store indicated by system properties. This method tries to load a key store by consulting the
615: * following system properties:<code>javax.net.ssl.keyStore</code>, <code>javax.net.ssl.keyStorePassword</code>, and
616: * <code>javax.net.ssl.keyStoreType</code>.
617: * <p/>
618: * If these properties specify a file with an appropriate password, the factory uses this file for the key store. If
619: * that file does not exist, then a default, empty keystore is created.
620: * <p/>
621: * This behavior corresponds to the standard J2SDK behavior for SSL key stores.
622: *
623: * @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509KeyManager">The
624: * standard J2SDK SSL key store mechanism</a>
625: */
626: protected void loadDefaultKeyStore() {
627: Resource location = null;
628: String type = null;
629: String password = null;
630: String locationProperty = System
631: .getProperty("javax.net.ssl.keyStore");
632: if (StringUtils.hasLength(locationProperty)) {
633: File f = new File(locationProperty);
634: if (f.exists() && f.isFile() && f.canRead()) {
635: location = new FileSystemResource(f);
636: }
637: String passwordProperty = System
638: .getProperty("javax.net.ssl.keyStorePassword");
639: if (StringUtils.hasLength(passwordProperty)) {
640: password = passwordProperty;
641: }
642: type = System.getProperty("javax.net.ssl.trustStore");
643: }
644: // use the factory bean here, easier to setup
645: KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
646: factoryBean.setLocation(location);
647: factoryBean.setPassword(password);
648: factoryBean.setType(type);
649: try {
650: factoryBean.afterPropertiesSet();
651: this .trustStore = (KeyStore) factoryBean.getObject();
652: if (logger.isDebugEnabled()) {
653: logger.debug("Loaded default key store");
654: }
655: } catch (Exception ex) {
656: logger.warn("Could not open default key store", ex);
657: }
658: }
659:
660: /**
661: * Loads a default trust store. This method uses the following algorithm: <ol> <li> If the system property
662: * <code>javax.net.ssl.trustStore</code> is defined, its value is loaded. If the
663: * <code>javax.net.ssl.trustStorePassword</code> system property is also defined, its value is used as a password.
664: * If the <code>javax.net.ssl.trustStoreType</code> system property is defined, its value is used as a key store
665: * type.
666: * <p/>
667: * If <code>javax.net.ssl.trustStore</code> is defined but the specified file does not exist, then a default, empty
668: * trust store is created. </li> <li> If the <code>javax.net.ssl.trustStore</code> system property was not
669: * specified, but if the file <code>$JAVA_HOME/lib/security/jssecacerts</code> exists, that file is used. </li>
670: * Otherwise, <li>If the file <code>$JAVA_HOME/lib/security/cacerts</code> exists, that file is used. </ol>
671: * <p/>
672: * This behavior corresponds to the standard J2SDK behavior for SSL trust stores.
673: *
674: * @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager">The
675: * standard J2SDK SSL trust store mechanism</a>
676: */
677: protected void loadDefaultTrustStore() {
678: Resource location = null;
679: String type = null;
680: String password = null;
681: String locationProperty = System
682: .getProperty("javax.net.ssl.trustStore");
683: if (StringUtils.hasLength(locationProperty)) {
684: File f = new File(locationProperty);
685: if (f.exists() && f.isFile() && f.canRead()) {
686: location = new FileSystemResource(f);
687: }
688: String passwordProperty = System
689: .getProperty("javax.net.ssl.trustStorePassword");
690: if (StringUtils.hasLength(passwordProperty)) {
691: password = passwordProperty;
692: }
693: type = System.getProperty("javax.net.ssl.trustStoreType");
694: } else {
695: String javaHome = System.getProperty("java.home");
696: location = new FileSystemResource(javaHome
697: + "/lib/security/jssecacerts");
698: if (!location.exists()) {
699: location = new FileSystemResource(javaHome
700: + "/lib/security/cacerts");
701: }
702: }
703: // use the factory bean here, easier to setup
704: KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
705: factoryBean.setLocation(location);
706: factoryBean.setPassword(password);
707: factoryBean.setType(type);
708: try {
709: factoryBean.afterPropertiesSet();
710: this .trustStore = (KeyStore) factoryBean.getObject();
711: if (logger.isDebugEnabled()) {
712: logger.debug("Loaded default trust store");
713: }
714: } catch (Exception ex) {
715: logger.warn("Could not open default trust store", ex);
716: }
717: }
718:
719: //
720: // Inner classes
721: //
722:
723: private class KeyStoreCertificateValidator implements
724: CertificateValidationCallback.CertificateValidator {
725:
726: public boolean validate(X509Certificate certificate)
727: throws CertificateValidationCallback.CertificateValidationException {
728: if (isOwnedCert(certificate)) {
729: if (logger.isDebugEnabled()) {
730: logger.debug("Certificate with DN ["
731: + certificate.getSubjectX500Principal()
732: .getName()
733: + "] is in private keystore");
734: }
735: return true;
736: } else if (trustStore == null) {
737: return false;
738: }
739:
740: try {
741: certificate.checkValidity();
742: } catch (CertificateExpiredException e) {
743: if (logger.isDebugEnabled()) {
744: logger.debug("Certificate with DN ["
745: + certificate.getSubjectX500Principal()
746: .getName() + "] has expired");
747: }
748: return false;
749: } catch (CertificateNotYetValidException e) {
750: if (logger.isDebugEnabled()) {
751: logger.debug("Certificate with DN ["
752: + certificate.getSubjectX500Principal()
753: .getName() + "] is not yet valid");
754: }
755: return false;
756: }
757:
758: X509CertSelector certSelector = new X509CertSelector();
759: certSelector.setCertificate(certificate);
760:
761: PKIXBuilderParameters parameters;
762: CertPathBuilder builder;
763: try {
764: parameters = new PKIXBuilderParameters(trustStore,
765: certSelector);
766: parameters.setRevocationEnabled(false);
767: builder = CertPathBuilder.getInstance("PKIX");
768: } catch (GeneralSecurityException ex) {
769: throw new CertificateValidationCallback.CertificateValidationException(
770: "Could not create PKIX CertPathBuilder", ex);
771: }
772:
773: try {
774: builder.build(parameters);
775: } catch (CertPathBuilderException e) {
776: if (logger.isDebugEnabled()) {
777: logger
778: .debug("Certification path of certificate with DN ["
779: + certificate
780: .getSubjectX500Principal()
781: .getName()
782: + "] could not be validated");
783: }
784: return false;
785: } catch (InvalidAlgorithmParameterException e) {
786: if (logger.isDebugEnabled()) {
787: logger.debug("Algorithm of certificate with DN ["
788: + certificate.getSubjectX500Principal()
789: .getName()
790: + "] could not be validated");
791: }
792: return false;
793: }
794: if (logger.isDebugEnabled()) {
795: logger.debug("Certificate with DN ["
796: + certificate.getSubjectX500Principal()
797: .getName() + "] validated");
798: }
799: return true;
800: }
801:
802: private boolean isOwnedCert(X509Certificate cert)
803: throws CertificateValidationCallback.CertificateValidationException {
804: if (keyStore == null) {
805: return false;
806: }
807: try {
808: Enumeration aliases = keyStore.aliases();
809: while (aliases.hasMoreElements()) {
810: String alias = (String) aliases.nextElement();
811: if (keyStore.isKeyEntry(alias)) {
812: X509Certificate x509Cert = (X509Certificate) keyStore
813: .getCertificate(alias);
814: if (x509Cert != null) {
815: if (x509Cert.equals(cert)) {
816: return true;
817: }
818: }
819: }
820: }
821: return false;
822: } catch (GeneralSecurityException e) {
823: throw new CertificateValidationCallback.CertificateValidationException(
824: "Could not determine whether certificate is contained in main key store",
825: e);
826: }
827: }
828: }
829: }
|