1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.wss4j.common.crypto;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.math.BigInteger;
25 import java.nio.file.Files;
26 import java.nio.file.Paths;
27 import java.security.GeneralSecurityException;
28 import java.security.InvalidAlgorithmParameterException;
29 import java.security.Key;
30 import java.security.KeyStore;
31 import java.security.KeyStoreException;
32 import java.security.MessageDigest;
33 import java.security.NoSuchAlgorithmException;
34 import java.security.NoSuchProviderException;
35 import java.security.PrivateKey;
36 import java.security.PublicKey;
37 import java.security.UnrecoverableKeyException;
38 import java.security.cert.CertPath;
39 import java.security.cert.CertPathValidator;
40 import java.security.cert.CertStore;
41 import java.security.cert.Certificate;
42 import java.security.cert.CertificateEncodingException;
43 import java.security.cert.CertificateException;
44 import java.security.cert.CertificateExpiredException;
45 import java.security.cert.CertificateFactory;
46 import java.security.cert.CertificateNotYetValidException;
47 import java.security.cert.CollectionCertStoreParameters;
48 import java.security.cert.PKIXParameters;
49 import java.security.cert.TrustAnchor;
50 import java.security.cert.X509CRL;
51 import java.security.cert.X509Certificate;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collection;
55 import java.util.Enumeration;
56 import java.util.HashSet;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Properties;
60 import java.util.Set;
61 import java.util.concurrent.ConcurrentHashMap;
62 import java.util.regex.Pattern;
63
64 import javax.security.auth.callback.Callback;
65 import javax.security.auth.callback.CallbackHandler;
66 import javax.security.auth.callback.UnsupportedCallbackException;
67 import javax.security.auth.x500.X500Principal;
68
69 import org.apache.wss4j.common.ext.WSPasswordCallback;
70 import org.apache.wss4j.common.ext.WSSecurityException;
71 import org.apache.wss4j.common.util.Loader;
72
73
74
75
76
77 public class Merlin extends CryptoBase {
78
79 public static final String ENCRYPTED_PASSWORD_PREFIX = "ENC(";
80 public static final String ENCRYPTED_PASSWORD_SUFFIX = ")";
81
82 public static final String PREFIX = "org.apache.wss4j.crypto.merlin.";
83 public static final String OLD_PREFIX = "org.apache.ws.security.crypto.merlin.";
84
85
86
87
88 public static final String OLD_KEYSTORE_FILE = "file";
89
90
91
92
93 public static final String CRYPTO_KEYSTORE_PROVIDER = "keystore.provider";
94 public static final String CRYPTO_CERT_PROVIDER = "cert.provider";
95 public static final String CRYPTO_CERT_PROVIDER_HANDLES_NAME_CONSTRAINTS =
96 "cert.provider.nameconstraints";
97
98
99
100
101 public static final String KEYSTORE_FILE = "keystore.file";
102 public static final String KEYSTORE_PASSWORD = "keystore.password";
103 public static final String KEYSTORE_TYPE = "keystore.type";
104 public static final String KEYSTORE_ALIAS = "keystore.alias";
105 public static final String KEYSTORE_PRIVATE_PASSWORD = "keystore.private.password";
106 public static final String KEYSTORE_PRIVATE_KEY_CACHING = "keystore.private.caching";
107
108
109
110
111 public static final String LOAD_CA_CERTS = "load.cacerts";
112 public static final String TRUSTSTORE_FILE = "truststore.file";
113 public static final String TRUSTSTORE_PASSWORD = "truststore.password";
114 public static final String TRUSTSTORE_TYPE = "truststore.type";
115 public static final String TRUSTSTORE_PROVIDER = "truststore.provider";
116
117
118
119
120 public static final String X509_CRL_FILE = "x509crl.file";
121
122 private static final org.slf4j.Logger LOG =
123 org.slf4j.LoggerFactory.getLogger(Merlin.class);
124 private static final String COMMA_SEPARATOR = ",";
125
126 protected Properties properties;
127 protected KeyStore keystore;
128 protected KeyStore truststore;
129 protected CertStore crlCertStore;
130 protected boolean loadCACerts;
131 protected boolean privatePasswordSet;
132 protected PasswordEncryptor passwordEncryptor;
133
134 private boolean certProviderHandlesNameConstraints = false;
135 private boolean enablePrivateKeyCaching = true;
136 private Map<String, PrivateKey> privateKeyCache = new ConcurrentHashMap<>();
137
138 public Merlin() {
139
140 }
141
142 public Merlin(boolean loadCACerts, String cacertsPasswd) {
143 super();
144
145 if (truststore == null && loadCACerts) {
146 String cacertsPath = System.getProperty("java.home") + "/lib/security/cacerts";
147
148 try (InputStream cacertsIs = Files.newInputStream(Paths.get(cacertsPath))) {
149
150 truststore = KeyStore.getInstance(KeyStore.getDefaultType());
151 truststore.load(cacertsIs, cacertsPasswd.toCharArray());
152 this.loadCACerts = true;
153 } catch (Exception e) {
154 LOG.warn("CA certs could not be loaded: " + e.getMessage());
155 }
156 }
157 }
158
159 public Merlin(Properties properties, ClassLoader loader, PasswordEncryptor passwordEncryptor)
160 throws WSSecurityException, IOException {
161 loadProperties(properties, loader, passwordEncryptor);
162 }
163
164 public void loadProperties(
165 Properties properties,
166 ClassLoader loader,
167 PasswordEncryptor passwordEncryptor
168 ) throws WSSecurityException, IOException {
169 if (properties == null) {
170 return;
171 }
172 this.properties = properties;
173 this.passwordEncryptor = passwordEncryptor;
174
175 String prefix = PREFIX;
176 for (Object key : properties.keySet()) {
177 if (key instanceof String) {
178 String propKey = (String)key;
179 if (propKey.startsWith(PREFIX)) {
180 break;
181 } else if (propKey.startsWith(OLD_PREFIX)) {
182 prefix = OLD_PREFIX;
183 break;
184 }
185 }
186 }
187
188
189
190
191 String keystoreProvider = this.properties.getProperty(prefix + CRYPTO_KEYSTORE_PROVIDER);
192 if (keystoreProvider != null) {
193 keystoreProvider = keystoreProvider.trim();
194
195 if (keystoreProvider.length() == 0) {
196 keystoreProvider = null;
197 }
198 }
199 String certProvider = properties.getProperty(prefix + CRYPTO_CERT_PROVIDER);
200 if (certProvider != null) {
201 setCryptoProvider(certProvider);
202 }
203 String cpNameConstraintsProp =
204 properties.getProperty(prefix + CRYPTO_CERT_PROVIDER_HANDLES_NAME_CONSTRAINTS);
205 if (cpNameConstraintsProp != null) {
206 certProviderHandlesNameConstraints = Boolean.parseBoolean(cpNameConstraintsProp);
207 }
208
209
210
211
212 String alias = properties.getProperty(prefix + KEYSTORE_ALIAS);
213 if (alias != null) {
214 alias = alias.trim();
215 setDefaultX509Identifier(alias);
216 }
217 String keyStoreLocation = properties.getProperty(prefix + KEYSTORE_FILE);
218 if (keyStoreLocation == null) {
219 keyStoreLocation = properties.getProperty(prefix + OLD_KEYSTORE_FILE);
220 }
221 if (keyStoreLocation != null) {
222 keyStoreLocation = keyStoreLocation.trim();
223
224 try (InputStream is = loadInputStream(loader, keyStoreLocation)) {
225 String passwd = properties.getProperty(prefix + KEYSTORE_PASSWORD);
226 if (passwd != null) {
227 passwd = passwd.trim();
228 passwd = decryptPassword(passwd, passwordEncryptor);
229 }
230 String type = properties.getProperty(prefix + KEYSTORE_TYPE, KeyStore.getDefaultType());
231 if (type != null) {
232 type = type.trim();
233 }
234 keystore = load(is, passwd, keystoreProvider, type);
235 LOG.debug(
236 "The KeyStore {} of type {} has been loaded", keyStoreLocation, type
237 );
238 String privatePasswd = properties.getProperty(prefix + KEYSTORE_PRIVATE_PASSWORD);
239 if (privatePasswd != null) {
240 privatePasswordSet = true;
241 }
242 }
243
244 String privateKeyCachingProp = properties.getProperty(prefix + KEYSTORE_PRIVATE_KEY_CACHING);
245 if (privateKeyCachingProp != null) {
246 enablePrivateKeyCaching = Boolean.parseBoolean(privateKeyCachingProp);
247 }
248 } else {
249 LOG.debug("The KeyStore is not loaded as KEYSTORE_FILE is null");
250 }
251
252
253
254
255 String trustProvider = this.properties.getProperty(prefix + TRUSTSTORE_PROVIDER);
256 if (trustProvider != null) {
257 trustProvider = trustProvider.trim();
258
259 if (trustProvider.length() == 0) {
260 trustProvider = null;
261 } else {
262 setTrustProvider(trustProvider);
263 }
264 } else if (keystoreProvider != null) {
265
266 trustProvider = keystoreProvider;
267 setTrustProvider(trustProvider);
268 }
269
270 String trustStoreLocation = properties.getProperty(prefix + TRUSTSTORE_FILE);
271 if (trustStoreLocation != null) {
272 trustStoreLocation = trustStoreLocation.trim();
273
274 try (InputStream is = loadInputStream(loader, trustStoreLocation)) {
275 String passwd = properties.getProperty(prefix + TRUSTSTORE_PASSWORD);
276 if (passwd != null) {
277 passwd = passwd.trim();
278 passwd = decryptPassword(passwd, passwordEncryptor);
279 }
280 String type = properties.getProperty(prefix + TRUSTSTORE_TYPE, KeyStore.getDefaultType());
281 if (type != null) {
282 type = type.trim();
283 }
284 truststore = load(is, passwd, trustProvider, type);
285 LOG.debug(
286 "The TrustStore {} of type {} has been loaded", trustStoreLocation, type
287 );
288 loadCACerts = false;
289 }
290 } else {
291 String loadCacerts = properties.getProperty(prefix + LOAD_CA_CERTS, "false");
292 if (loadCacerts != null) {
293 loadCacerts = loadCacerts.trim();
294 }
295 if (Boolean.valueOf(loadCacerts)) {
296 String cacertsPath = (System.getProperty("java.home") + "/lib/security/cacerts").trim();
297 try (InputStream is = Files.newInputStream(Paths.get(cacertsPath))) {
298 String cacertsPasswd = properties.getProperty(prefix + TRUSTSTORE_PASSWORD, "changeit");
299 if (cacertsPasswd != null) {
300 cacertsPasswd = cacertsPasswd.trim();
301 cacertsPasswd = decryptPassword(cacertsPasswd, passwordEncryptor);
302 }
303 truststore = load(is, cacertsPasswd, null, KeyStore.getDefaultType());
304 LOG.debug("CA certs have been loaded");
305 loadCACerts = true;
306 }
307 }
308 }
309
310
311
312
313 String crlLocations = properties.getProperty(prefix + X509_CRL_FILE);
314 if (crlLocations != null) {
315 String[] splittedCrlsLocations = crlLocations.split(COMMA_SEPARATOR);
316 List<X509CRL> crls = new ArrayList<>(splittedCrlsLocations.length);
317 for (String crlLocation : splittedCrlsLocations) {
318 try (InputStream is = loadInputStream(loader, crlLocation.trim())) {
319 CertificateFactory cf = getCertificateFactory();
320 X509CRL crl = (X509CRL)cf.generateCRL(is);
321 crls.add(crl);
322 } catch (Exception e) {
323 LOG.debug(e.getMessage(), e);
324 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "ioError00");
325 }
326 }
327 try {
328 if (keystoreProvider == null || keystoreProvider.length() == 0) {
329 crlCertStore =
330 CertStore.getInstance(
331 "Collection",
332 new CollectionCertStoreParameters(crls)
333 );
334
335 } else {
336 crlCertStore =
337 CertStore.getInstance(
338 "Collection",
339 new CollectionCertStoreParameters(crls),
340 keystoreProvider
341 );
342 }
343 } catch (Exception e) {
344 LOG.debug(e.getMessage(), e);
345 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "ioError00");
346 }
347 LOG.debug("The CRL files {} have been loaded", crlLocations);
348 }
349 }
350
351
352
353
354 public static InputStream loadInputStream(ClassLoader loader, String location)
355 throws WSSecurityException, IOException {
356 return Loader.loadInputStream(loader, location);
357 }
358
359
360
361
362
363
364
365
366
367 protected KeyStore load(InputStream input, String storepass, String provider, String type)
368 throws WSSecurityException {
369 KeyStore ks = null;
370
371 try {
372 if (provider == null || provider.length() == 0) {
373 ks = KeyStore.getInstance(type);
374 } else {
375 ks = KeyStore.getInstance(type, provider);
376 }
377
378 ks.load(input, storepass == null || storepass.length() == 0
379 ? new char[0] : storepass.toCharArray());
380 } catch (IOException | GeneralSecurityException e) {
381 LOG.debug(e.getMessage(), e);
382 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "failedCredentialLoad");
383 }
384 return ks;
385 }
386
387
388
389
390
391
392
393
394
395
396 public KeyStore getKeyStore() {
397 return keystore;
398 }
399
400
401
402
403
404
405 public void setKeyStore(KeyStore keyStore) {
406 keystore = keyStore;
407 }
408
409
410
411
412
413
414 public KeyStore getTrustStore() {
415 return truststore;
416 }
417
418
419
420
421
422
423 public void setTrustStore(KeyStore trustStore) {
424 truststore = trustStore;
425 }
426
427
428
429
430
431
432
433 public void setCRLCertStore(CertStore crlCertStore) {
434 this.crlCertStore = crlCertStore;
435 }
436
437
438
439
440
441
442
443 public CertStore getCRLCertStore() {
444 return crlCertStore;
445 }
446
447
448
449
450
451
452
453
454
455 @Override
456 public CertificateFactory getCertificateFactory() throws WSSecurityException {
457 if (certificateFactory != null) {
458 return certificateFactory;
459 }
460
461 String provider = getCryptoProvider();
462 String keyStoreProvider = null;
463 if (keystore != null) {
464 keyStoreProvider = keystore.getProvider().getName();
465 }
466
467 try {
468 if (provider == null || provider.length() == 0) {
469 if (keyStoreProvider != null && keyStoreProvider.length() != 0) {
470 try {
471 certificateFactory =
472 CertificateFactory.getInstance(
473 "X.509", mapKeystoreProviderToCertProvider(keyStoreProvider)
474 );
475 } catch (Exception ex) {
476 LOG.debug("The keystore provider '" + keyStoreProvider + "' does not support X.509 because \""
477 + ex.getMessage() + "\". The JVM default provider will be tried out next", ex);
478
479
480 }
481 }
482 if (certificateFactory == null) {
483 certificateFactory = CertificateFactory.getInstance("X.509");
484 }
485 } else {
486 certificateFactory = CertificateFactory.getInstance("X.509", provider);
487 }
488 } catch (CertificateException e) {
489 throw new WSSecurityException(
490 WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, e, "unsupportedCertType"
491 );
492 } catch (NoSuchProviderException e) {
493 throw new WSSecurityException(
494 WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, e, "noSecProvider"
495 );
496 }
497
498 return certificateFactory;
499 }
500
501 private String mapKeystoreProviderToCertProvider(String s) {
502 if ("SunJSSE".equals(s)) {
503 return "SUN";
504 }
505 return s;
506 }
507
508
509
510
511
512
513
514
515
516
517 @Override
518 public String getDefaultX509Identifier() throws WSSecurityException {
519 if (super.getDefaultX509Identifier() != null) {
520 return super.getDefaultX509Identifier();
521 }
522
523 if (keystore != null) {
524 try {
525 Enumeration<String> as = keystore.aliases();
526 if (as.hasMoreElements()) {
527 String alias = as.nextElement();
528 if (!as.hasMoreElements()) {
529 setDefaultX509Identifier(alias);
530 return alias;
531 }
532 }
533 } catch (KeyStoreException ex) {
534 throw new WSSecurityException(
535 WSSecurityException.ErrorCode.FAILURE, ex, "keystore"
536 );
537 }
538 }
539 return null;
540 }
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557 public X509Certificate[] getX509Certificates(CryptoType cryptoType) throws WSSecurityException {
558 if (cryptoType == null) {
559 return new X509Certificate[0];
560 }
561 CryptoType.TYPE type = cryptoType.getType();
562 X509Certificate[] certs = new X509Certificate[0];
563 switch (type) {
564 case ISSUER_SERIAL:
565 certs = getX509Certificates(cryptoType.getIssuer(), cryptoType.getSerial());
566 break;
567 case THUMBPRINT_SHA1:
568 certs = getX509Certificates(cryptoType.getBytes());
569 break;
570 case SKI_BYTES:
571 certs = getX509CertificatesSKI(cryptoType.getBytes());
572 break;
573 case SUBJECT_DN:
574 certs = getX509CertificatesSubjectDN(cryptoType.getSubjectDN());
575 break;
576 case ALIAS:
577 certs = getX509Certificates(cryptoType.getAlias());
578 break;
579 case ENDPOINT:
580 break;
581 }
582 return certs;
583 }
584
585
586
587
588
589
590
591
592 public String getX509Identifier(X509Certificate cert) throws WSSecurityException {
593 String identifier = null;
594
595 if (keystore != null) {
596 identifier = getIdentifier(cert, keystore);
597 }
598
599 if (identifier == null && truststore != null) {
600 identifier = getIdentifier(cert, truststore);
601 }
602
603 return identifier;
604 }
605
606
607
608
609
610
611
612
613 public PrivateKey getPrivateKey(
614 X509Certificate certificate,
615 CallbackHandler callbackHandler
616 ) throws WSSecurityException {
617 if (keystore == null) {
618 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
619 new Object[] {"The keystore is null"});
620 }
621 if (callbackHandler == null) {
622 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
623 new Object[] {"The CallbackHandler is null"});
624 }
625
626 String identifier = getIdentifier(certificate, keystore);
627 if (identifier == null) {
628 try {
629 String msg = "Cannot find key for certificate";
630 String logMsg = createKeyStoreErrorMessage(keystore);
631 LOG.error(msg + logMsg);
632 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
633 new Object[] {msg});
634 } catch (KeyStoreException ex) {
635 throw new WSSecurityException(
636 WSSecurityException.ErrorCode.FAILURE, ex, "noPrivateKey",
637 new Object[] {ex.getMessage()}
638 );
639 }
640 }
641 String password = getPassword(identifier, callbackHandler);
642 return getPrivateKey(identifier, password);
643 }
644
645
646
647
648
649
650
651
652 public PrivateKey getPrivateKey(
653 PublicKey publicKey,
654 CallbackHandler callbackHandler
655 ) throws WSSecurityException {
656 if (keystore == null) {
657 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
658 new Object[] {"The keystore is null"});
659 }
660 if (callbackHandler == null) {
661 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
662 new Object[] {"The CallbackHandler is null"});
663 }
664
665 String identifier = getIdentifier(publicKey, keystore);
666 if (identifier == null) {
667 try {
668 String msg = "Cannot find key for corresponding public key";
669 String logMsg = createKeyStoreErrorMessage(keystore);
670 LOG.error(msg + logMsg);
671 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
672 new Object[] {msg});
673 } catch (KeyStoreException ex) {
674 throw new WSSecurityException(
675 WSSecurityException.ErrorCode.FAILURE, ex, "noPrivateKey",
676 new Object[] {ex.getMessage()}
677 );
678 }
679 }
680 String password = getPassword(identifier, callbackHandler);
681 return getPrivateKey(identifier, password);
682 }
683
684
685
686
687
688
689
690
691 public PrivateKey getPrivateKey(
692 String identifier,
693 String password
694 ) throws WSSecurityException {
695 if (keystore == null) {
696 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
697 new Object[] {"The keystore is null"});
698 }
699 try {
700 if (identifier == null || !keystore.isKeyEntry(identifier)) {
701 String msg = "Cannot find key for alias: [" + identifier + "]";
702 String logMsg = createKeyStoreErrorMessage(keystore);
703 LOG.error(msg + logMsg);
704 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
705 new Object[] {msg});
706 }
707 String pwd = password;
708 if (pwd == null && privatePasswordSet) {
709 pwd = properties.getProperty(PREFIX + KEYSTORE_PRIVATE_PASSWORD);
710 if (pwd == null) {
711 pwd = properties.getProperty(OLD_PREFIX + KEYSTORE_PRIVATE_PASSWORD);
712 }
713 if (pwd != null) {
714 pwd = pwd.trim();
715 pwd = decryptPassword(pwd, passwordEncryptor);
716 }
717 }
718 if (enablePrivateKeyCaching) {
719 Key privateKey = privateKeyCache.get(identifier);
720 if (privateKey != null) {
721 return (PrivateKey) privateKey;
722 }
723 }
724
725 Key keyTmp = keystore.getKey(identifier, pwd == null
726 ? new char[]{} : pwd.toCharArray());
727 if (!(keyTmp instanceof PrivateKey)) {
728 String msg = "Key is not a private key, alias: [" + identifier + "]";
729 String logMsg = createKeyStoreErrorMessage(keystore);
730 LOG.error(msg + logMsg);
731 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
732 new Object[] {msg});
733 }
734
735 if (enablePrivateKeyCaching) {
736 privateKeyCache.put(identifier, (PrivateKey) keyTmp);
737 }
738 return (PrivateKey) keyTmp;
739 } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException ex) {
740 throw new WSSecurityException(
741 WSSecurityException.ErrorCode.FAILURE, ex, "noPrivateKey", new Object[] {ex.getMessage()}
742 );
743 }
744 }
745
746
747
748
749
750
751
752
753
754
755 protected void verifyTrust(
756 X509Certificate[] certs,
757 boolean enableRevocation,
758 Collection<Pattern> subjectCertConstraints
759 ) throws WSSecurityException {
760
761
762
763 if (certs.length == 1 && !enableRevocation) {
764 String issuerString = certs[0].getIssuerX500Principal().getName();
765 BigInteger issuerSerial = certs[0].getSerialNumber();
766
767 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ISSUER_SERIAL);
768 cryptoType.setIssuerSerial(issuerString, issuerSerial);
769 X509Certificate[] foundCerts = getX509Certificates(cryptoType);
770
771
772
773
774
775 if (foundCerts != null && foundCerts.length > 0 && foundCerts[0] != null && foundCerts[0].equals(certs[0])) {
776 try {
777 certs[0].checkValidity();
778 } catch (CertificateExpiredException | CertificateNotYetValidException e) {
779 throw new WSSecurityException(
780 WSSecurityException.ErrorCode.FAILED_CHECK, e, "invalidCert"
781 );
782 }
783 LOG.debug(
784 "Direct trust for certificate with {}", certs[0].getSubjectX500Principal().getName()
785 );
786 return;
787 }
788 }
789
790
791
792
793
794 List<Certificate[]> foundIssuingCertChains = null;
795 String issuerString = certs[0].getIssuerX500Principal().getName();
796 if (certs.length == 1) {
797
798 Object subject = convertSubjectToPrincipal(issuerString);
799
800 if (keystore != null) {
801 foundIssuingCertChains = getCertificates(subject, keystore, false);
802 }
803
804
805 if ((foundIssuingCertChains == null || foundIssuingCertChains.isEmpty()) && truststore != null) {
806 foundIssuingCertChains = getCertificates(subject, truststore, true);
807 }
808
809 if (foundIssuingCertChains == null || foundIssuingCertChains.isEmpty()
810 || foundIssuingCertChains.get(0).length < 1) {
811 String subjectString = certs[0].getSubjectX500Principal().getName();
812 LOG.debug(
813 "No certs found in keystore for issuer {} of certificate for {}",
814 issuerString, subjectString
815 );
816 throw new WSSecurityException(
817 WSSecurityException.ErrorCode.FAILURE, "certpath", new Object[] {"No trusted certs found"}
818 );
819 }
820 }
821
822
823
824
825
826 LOG.debug(
827 "Preparing to validate certificate path for issuer {}", issuerString
828 );
829
830 try {
831 Set<TrustAnchor> set = new HashSet<>();
832 if (truststore != null) {
833 addTrustAnchors(set, truststore);
834 }
835
836
837
838
839
840
841 if (keystore != null && (truststore == null || loadCACerts)) {
842 addTrustAnchors(set, keystore);
843 }
844
845
846 String provider = getCryptoProvider();
847 CertPathValidator validator = null;
848 if (provider == null || provider.length() == 0) {
849 validator = CertPathValidator.getInstance("PKIX");
850 } else {
851 validator = CertPathValidator.getInstance("PKIX", provider);
852 }
853
854 PKIXParameters param = createPKIXParameters(set, enableRevocation);
855
856
857 if (foundIssuingCertChains != null && !foundIssuingCertChains.isEmpty()) {
858 java.security.cert.CertPathValidatorException validatorException = null;
859
860 for (Certificate[] foundCertChain : foundIssuingCertChains) {
861 X509Certificate[] x509certs = new X509Certificate[foundCertChain.length + 1];
862 x509certs[0] = certs[0];
863 System.arraycopy(foundCertChain, 0, x509certs, 1, foundCertChain.length);
864
865 List<X509Certificate> certList = Arrays.asList(x509certs);
866 CertPath path = getCertificateFactory().generateCertPath(certList);
867
868 try {
869 validator.validate(path, param);
870
871 validatorException = null;
872 break;
873 } catch (java.security.cert.CertPathValidatorException e) {
874 validatorException = e;
875 }
876 }
877
878 if (validatorException != null) {
879 throw validatorException;
880 }
881 } else {
882 List<X509Certificate> certList = Arrays.asList(certs);
883 CertPath path = getCertificateFactory().generateCertPath(certList);
884
885 validator.validate(path, param);
886 }
887 } catch (NoSuchProviderException | NoSuchAlgorithmException
888 | CertificateException | InvalidAlgorithmParameterException
889 | java.security.cert.CertPathValidatorException
890 | KeyStoreException e) {
891 throw new WSSecurityException(
892 WSSecurityException.ErrorCode.FAILURE, e, "certpath"
893 );
894 }
895
896
897 if (!matchesSubjectDnPattern(certs[0], subjectCertConstraints)) {
898 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
899 }
900 }
901
902 @Override
903 public void verifyTrust(X509Certificate[] certs, boolean enableRevocation,
904 Collection<Pattern> subjectCertConstraints,
905 Collection<Pattern> issuerCertConstraints) throws WSSecurityException {
906 verifyTrust(certs, enableRevocation, subjectCertConstraints);
907 if (!matchesIssuerDnPattern(certs[0], issuerCertConstraints)) {
908 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
909 }
910 }
911
912
913 protected PKIXParameters createPKIXParameters(
914 Set<TrustAnchor> trustAnchors, boolean enableRevocation
915 ) throws InvalidAlgorithmParameterException {
916 PKIXParameters param = new PKIXParameters(trustAnchors);
917 param.setRevocationEnabled(enableRevocation);
918 if (enableRevocation && crlCertStore != null) {
919 param.addCertStore(crlCertStore);
920 }
921
922 return param;
923 }
924
925
926
927
928
929
930
931 public void verifyTrust(PublicKey publicKey) throws WSSecurityException {
932
933
934
935 if (publicKey == null) {
936 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
937 }
938
939
940
941
942
943 if (!findPublicKeyInKeyStore(publicKey, keystore, false)
944 && !findPublicKeyInKeyStore(publicKey, truststore, true)) {
945 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
946 }
947 }
948
949
950
951
952
953
954
955
956
957 private X509Certificate[] getX509Certificates(
958 String issuer,
959 BigInteger serialNumber
960 ) throws WSSecurityException {
961
962
963
964
965
966
967
968
969 Object issuerName = null;
970 try {
971 X500Principal issuerRDN = new X500Principal(issuer);
972 issuerName = createBCX509Name(issuerRDN.getName());
973 } catch (IllegalArgumentException ex) {
974 issuerName = createBCX509Name(issuer);
975 }
976 Certificate[] certs = null;
977 if (keystore != null) {
978 certs = getCertificates(issuerName, serialNumber, keystore, false);
979 }
980
981
982 if ((certs == null || certs.length == 0) && truststore != null) {
983 certs = getCertificates(issuerName, serialNumber, truststore, true);
984 }
985
986 if (certs == null || certs.length == 0) {
987 return new X509Certificate[0];
988 }
989
990 return Arrays.copyOf(certs, certs.length, X509Certificate[].class);
991 }
992
993
994
995
996
997
998
999
1000 private Certificate[] getCertificates(
1001 Object issuerRDN,
1002 BigInteger serialNumber,
1003 KeyStore store,
1004 boolean truststore
1005 ) throws WSSecurityException {
1006 String keystore = "keystore";
1007 if (truststore) {
1008 keystore = "truststore";
1009 }
1010 LOG.debug("Searching {} for cert with issuer {} and serial {}", keystore, issuerRDN, serialNumber);
1011 try {
1012 for (Enumeration<String> e = store.aliases(); e.hasMoreElements();) {
1013 String alias = e.nextElement();
1014 Certificate[] certs = store.getCertificateChain(alias);
1015 if (certs == null || certs.length == 0) {
1016
1017 Certificate cert = store.getCertificate(alias);
1018 if (cert != null) {
1019 certs = new Certificate[]{cert};
1020 }
1021 }
1022
1023 if (certs != null && certs.length > 0 && certs[0] instanceof X509Certificate) {
1024 X509Certificate x509cert = (X509Certificate) certs[0];
1025 LOG.debug("Keystore alias {} has issuer {} and serial {}", alias,
1026 x509cert.getIssuerX500Principal().getName(), x509cert.getSerialNumber());
1027 if (x509cert.getSerialNumber().compareTo(serialNumber) == 0) {
1028 Object certName =
1029 createBCX509Name(x509cert.getIssuerX500Principal().getName());
1030 if (certName.equals(issuerRDN)) {
1031 LOG.debug("Issuer Serial match found using keystore alias {}", alias);
1032 return certs;
1033 }
1034 }
1035 }
1036 }
1037 } catch (KeyStoreException e) {
1038 throw new WSSecurityException(
1039 WSSecurityException.ErrorCode.FAILURE, e, "keystore"
1040 );
1041 }
1042
1043 LOG.debug("No issuer serial match found in {}", keystore);
1044 return new Certificate[]{};
1045 }
1046
1047
1048
1049
1050
1051
1052
1053
1054 private X509Certificate[] getX509Certificates(byte[] thumbprint) throws WSSecurityException {
1055 MessageDigest sha = null;
1056
1057 try {
1058 sha = MessageDigest.getInstance("SHA1");
1059 } catch (NoSuchAlgorithmException e) {
1060 throw new WSSecurityException(
1061 WSSecurityException.ErrorCode.FAILURE, e, "decoding.general"
1062 );
1063 }
1064 Certificate[] certs = null;
1065 if (keystore != null) {
1066 certs = getCertificates(thumbprint, keystore, sha, false);
1067 }
1068
1069
1070 if ((certs == null || certs.length == 0) && truststore != null) {
1071 certs = getCertificates(thumbprint, truststore, sha, true);
1072 }
1073
1074 if (certs == null || certs.length == 0) {
1075 return new X509Certificate[0];
1076 }
1077
1078 return Arrays.copyOf(certs, certs.length, X509Certificate[].class);
1079 }
1080
1081
1082
1083
1084
1085
1086
1087
1088 private Certificate[] getCertificates(
1089 byte[] thumbprint,
1090 KeyStore store,
1091 MessageDigest sha,
1092 boolean truststore
1093 ) throws WSSecurityException {
1094 String keystore = "keystore";
1095 if (truststore) {
1096 keystore = "truststore";
1097 }
1098 LOG.debug("Searching {} for cert using a SHA-1 thumbprint", keystore);
1099 try {
1100 for (Enumeration<String> e = store.aliases(); e.hasMoreElements();) {
1101 String alias = e.nextElement();
1102 Certificate[] certs = store.getCertificateChain(alias);
1103 if (certs == null || certs.length == 0) {
1104
1105 Certificate cert = store.getCertificate(alias);
1106 if (cert != null) {
1107 certs = new Certificate[]{cert};
1108 }
1109 }
1110
1111 if (certs != null && certs.length > 0 && certs[0] instanceof X509Certificate) {
1112 X509Certificate x509cert = (X509Certificate) certs[0];
1113 try {
1114 sha.update(x509cert.getEncoded());
1115 } catch (CertificateEncodingException ex) {
1116 throw new WSSecurityException(
1117 WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, ex, "encodeError"
1118 );
1119 }
1120 byte[] data = sha.digest();
1121
1122 if (Arrays.equals(data, thumbprint)) {
1123 LOG.debug("Thumbprint match found using keystore alias {}", alias);
1124 return certs;
1125 }
1126 }
1127 }
1128 } catch (KeyStoreException e) {
1129 throw new WSSecurityException(
1130 WSSecurityException.ErrorCode.FAILURE, e, "keystore"
1131 );
1132 }
1133
1134 LOG.debug("No thumbprint match found in {}", keystore);
1135 return new Certificate[]{};
1136 }
1137
1138
1139
1140
1141
1142
1143
1144 private X509Certificate[] getX509CertificatesSKI(byte[] skiBytes) throws WSSecurityException {
1145 Certificate[] certs = null;
1146 if (keystore != null) {
1147 certs = getCertificates(skiBytes, keystore, false);
1148 }
1149
1150
1151 if ((certs == null || certs.length == 0) && truststore != null) {
1152 certs = getCertificates(skiBytes, truststore, true);
1153 }
1154
1155 if (certs == null || certs.length == 0) {
1156 return new X509Certificate[0];
1157 }
1158
1159 return Arrays.copyOf(certs, certs.length, X509Certificate[].class);
1160 }
1161
1162
1163
1164
1165
1166
1167
1168
1169 private Certificate[] getCertificates(
1170 byte[] skiBytes,
1171 KeyStore store,
1172 boolean truststore
1173 ) throws WSSecurityException {
1174 String keystore = "keystore";
1175 if (truststore) {
1176 keystore = "truststore";
1177 }
1178 LOG.debug("Searching {} for cert using Subject Key Identifier bytes", keystore);
1179 try {
1180 for (Enumeration<String> e = store.aliases(); e.hasMoreElements();) {
1181 String alias = e.nextElement();
1182 Certificate[] certs = store.getCertificateChain(alias);
1183 if (certs == null || certs.length == 0) {
1184
1185 Certificate cert = store.getCertificate(alias);
1186 if (cert != null) {
1187 certs = new Certificate[]{cert};
1188 }
1189 }
1190
1191 if (certs != null && certs.length > 0 && certs[0] instanceof X509Certificate) {
1192 X509Certificate x509cert = (X509Certificate) certs[0];
1193 byte[] data = getSKIBytesFromCert(x509cert);
1194 if (data.length == skiBytes.length && Arrays.equals(data, skiBytes)) {
1195 LOG.debug("SKI match found using keystore alias {}", alias);
1196 return certs;
1197 }
1198 }
1199 }
1200 } catch (KeyStoreException e) {
1201 throw new WSSecurityException(
1202 WSSecurityException.ErrorCode.FAILURE, e, "keystore"
1203 );
1204 }
1205
1206 LOG.debug("No SKI match found in {}", keystore);
1207 return new Certificate[]{};
1208 }
1209
1210
1211
1212
1213
1214
1215
1216
1217 private X509Certificate[] getX509CertificatesSubjectDN(String subjectDN) throws WSSecurityException {
1218 Object subject = convertSubjectToPrincipal(subjectDN);
1219
1220 List<Certificate[]> certs = null;
1221 if (keystore != null) {
1222 certs = getCertificates(subject, keystore, false);
1223 }
1224
1225
1226 if ((certs == null || certs.isEmpty()) && truststore != null) {
1227 certs = getCertificates(subject, truststore, true);
1228 }
1229
1230 if (certs == null || certs.isEmpty()) {
1231 return new X509Certificate[0];
1232 }
1233
1234
1235 return Arrays.copyOf(certs.get(0), certs.get(0).length, X509Certificate[].class);
1236 }
1237
1238 private Object convertSubjectToPrincipal(String subjectDN) {
1239
1240
1241
1242
1243
1244
1245
1246
1247 try {
1248 X500Principal subjectRDN = new X500Principal(subjectDN);
1249 return createBCX509Name(subjectRDN.getName());
1250 } catch (IllegalArgumentException ex) {
1251 return createBCX509Name(subjectDN);
1252 }
1253 }
1254
1255
1256
1257
1258
1259
1260
1261
1262 private X509Certificate[] getX509Certificates(String identifier) throws WSSecurityException {
1263 if (identifier == null) {
1264 return new X509Certificate[0];
1265 }
1266 Certificate[] certs = null;
1267 try {
1268 if (keystore != null) {
1269
1270 certs = keystore.getCertificateChain(identifier);
1271 if (certs == null || certs.length == 0) {
1272
1273 Certificate cert = keystore.getCertificate(identifier);
1274 if (cert != null) {
1275 certs = new Certificate[]{cert};
1276 }
1277 }
1278 }
1279
1280 if (certs == null && truststore != null) {
1281
1282 certs = truststore.getCertificateChain(identifier);
1283 if (certs == null) {
1284 Certificate cert = truststore.getCertificate(identifier);
1285 if (cert != null) {
1286 certs = new Certificate[]{cert};
1287 }
1288 }
1289 }
1290
1291 if (certs == null) {
1292 return new X509Certificate[0];
1293 }
1294 } catch (KeyStoreException e) {
1295 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "keystore");
1296 }
1297
1298 return Arrays.copyOf(certs, certs.length, X509Certificate[].class);
1299 }
1300
1301
1302
1303
1304 private boolean findPublicKeyInKeyStore(PublicKey publicKey, KeyStore keyStoreToSearch, boolean truststore) {
1305 if (keyStoreToSearch == null) {
1306 return false;
1307 }
1308 String keystore = "keystore";
1309 if (truststore) {
1310 keystore = "truststore";
1311 }
1312 LOG.debug("Searching {} for public key {}", keystore, publicKey);
1313 try {
1314 for (Enumeration<String> e = keyStoreToSearch.aliases(); e.hasMoreElements();) {
1315 String alias = e.nextElement();
1316 Certificate[] certs = keyStoreToSearch.getCertificateChain(alias);
1317 if (certs == null || certs.length == 0) {
1318
1319 Certificate cert = keyStoreToSearch.getCertificate(alias);
1320 if (cert != null) {
1321 certs = new Certificate[]{cert};
1322 }
1323 }
1324
1325 if (certs != null && certs.length > 0 && certs[0] instanceof X509Certificate
1326 && publicKey.equals(((X509Certificate)certs[0]).getPublicKey())) {
1327 LOG.debug("PublicKey match found using keystore alias {}", alias);
1328 return true;
1329 }
1330 }
1331 } catch (KeyStoreException e) {
1332 return false;
1333 }
1334
1335 LOG.debug("No PublicKey match found in {}", keystore);
1336 return false;
1337 }
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347 private List<Certificate[]> getCertificates(Object subjectRDN, KeyStore store, boolean truststore)
1348 throws WSSecurityException {
1349 String keystore = "keystore";
1350 if (truststore) {
1351 keystore = "truststore";
1352 }
1353 LOG.debug("Searching {} for cert with Subject {}", keystore, subjectRDN);
1354 List<Certificate[]> foundCerts = new ArrayList<>();
1355 try {
1356 for (Enumeration<String> e = store.aliases(); e.hasMoreElements();) {
1357 String alias = e.nextElement();
1358 Certificate[] certs = store.getCertificateChain(alias);
1359 if (certs == null || certs.length == 0) {
1360
1361 Certificate cert = store.getCertificate(alias);
1362 if (cert != null) {
1363 certs = new Certificate[]{cert};
1364 }
1365 }
1366 if (certs != null && certs.length > 0 && certs[0] instanceof X509Certificate) {
1367 X500Principal foundRDN = ((X509Certificate)certs[0]).getSubjectX500Principal();
1368 Object certName = createBCX509Name(foundRDN.getName());
1369
1370 if (subjectRDN.equals(certName)) {
1371 LOG.debug("Subject certificate match found using keystore alias {}", alias);
1372 foundCerts.add(certs);
1373 }
1374 }
1375 }
1376 } catch (KeyStoreException e) {
1377 throw new WSSecurityException(
1378 WSSecurityException.ErrorCode.FAILURE, e, "keystore"
1379 );
1380 }
1381
1382 if (foundCerts.isEmpty()) {
1383 LOG.debug("No Subject match found in {}", keystore);
1384 }
1385 return foundCerts;
1386 }
1387
1388 private static String createKeyStoreErrorMessage(KeyStore keystore) throws KeyStoreException {
1389 Enumeration<String> aliases = keystore.aliases();
1390 StringBuilder sb = new StringBuilder(keystore.size() * 7);
1391 boolean firstAlias = true;
1392 while (aliases.hasMoreElements()) {
1393 if (!firstAlias) {
1394 sb.append(", ");
1395 }
1396 sb.append(aliases.nextElement());
1397 firstAlias = false;
1398 }
1399 String msg = " in keystore of type [" + keystore.getType()
1400 + "] from provider [" + keystore.getProvider()
1401 + "] with size [" + keystore.size() + "] and aliases: {"
1402 + sb.toString() + "}";
1403 return msg;
1404 }
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423 protected void addTrustAnchors(Set<TrustAnchor> set, KeyStore keyStore)
1424 throws KeyStoreException, WSSecurityException {
1425 Enumeration<String> aliases = keyStore.aliases();
1426 while (aliases.hasMoreElements()) {
1427 String alias = aliases.nextElement();
1428 X509Certificate cert =
1429 (X509Certificate) keyStore.getCertificate(alias);
1430 if (cert != null) {
1431 if (certProviderHandlesNameConstraints) {
1432 TrustAnchor anchor = new TrustAnchor(cert, getNameConstraints(cert));
1433 set.add(anchor);
1434 } else {
1435 TrustAnchor anchor = new TrustAnchor(cert, null);
1436 set.add(anchor);
1437 }
1438 }
1439 }
1440 }
1441
1442
1443
1444
1445
1446
1447
1448
1449 private String getIdentifier(X509Certificate cert, KeyStore store)
1450 throws WSSecurityException {
1451 try {
1452 for (Enumeration<String> e = store.aliases(); e.hasMoreElements();) {
1453 String alias = e.nextElement();
1454
1455 Certificate[] certs = store.getCertificateChain(alias);
1456 if (certs == null || certs.length == 0) {
1457
1458 Certificate retrievedCert = store.getCertificate(alias);
1459 if (retrievedCert != null) {
1460 certs = new Certificate[]{retrievedCert};
1461 }
1462 }
1463
1464 if (certs != null && certs.length > 0 && certs[0].equals(cert)) {
1465 return alias;
1466 }
1467 }
1468 } catch (KeyStoreException e) {
1469 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "keystore");
1470 }
1471 return null;
1472 }
1473
1474 private String getIdentifier(PublicKey publicKey, KeyStore store)
1475 throws WSSecurityException {
1476 try {
1477 for (Enumeration<String> e = store.aliases(); e.hasMoreElements();) {
1478 String alias = e.nextElement();
1479
1480 Certificate[] certs = store.getCertificateChain(alias);
1481 if (certs == null || certs.length == 0) {
1482
1483 Certificate retrievedCert = store.getCertificate(alias);
1484 if (retrievedCert != null) {
1485 certs = new Certificate[]{retrievedCert};
1486 }
1487 }
1488
1489 if (certs != null && certs.length > 0 && certs[0].getPublicKey().equals(publicKey)) {
1490 return alias;
1491 }
1492 }
1493 } catch (KeyStoreException e) {
1494 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "keystore");
1495 }
1496 return null;
1497 }
1498
1499
1500
1501
1502
1503
1504
1505
1506 private String getPassword(
1507 String identifier,
1508 CallbackHandler cb
1509 ) throws WSSecurityException {
1510 WSPasswordCallback pwCb =
1511 new WSPasswordCallback(identifier, WSPasswordCallback.DECRYPT);
1512 try {
1513 Callback[] callbacks = new Callback[]{pwCb};
1514 cb.handle(callbacks);
1515 } catch (IOException | UnsupportedCallbackException e) {
1516 throw new WSSecurityException(
1517 WSSecurityException.ErrorCode.FAILURE, e, "noPassword", new Object[] {identifier}
1518 );
1519 }
1520
1521 return pwCb.getPassword();
1522 }
1523
1524 protected String decryptPassword(String password, PasswordEncryptor passwordEncryptor) {
1525 if (password.startsWith(ENCRYPTED_PASSWORD_PREFIX)
1526 && password.endsWith(ENCRYPTED_PASSWORD_SUFFIX)) {
1527 if (passwordEncryptor == null) {
1528 String error =
1529 "The Crypto properties has an encrypted password, but no PasswordEncryptor is configured!";
1530 LOG.debug(error);
1531 return password;
1532 }
1533 String substring = password.substring(ENCRYPTED_PASSWORD_PREFIX.length(),
1534 password.length() - 1);
1535 return passwordEncryptor.decrypt(substring);
1536 }
1537
1538 return password;
1539 }
1540
1541 public void setPasswordEncryptor(PasswordEncryptor passwordEncryptor) {
1542 this.passwordEncryptor = passwordEncryptor;
1543 }
1544
1545 public void clearCache() {
1546 if (enablePrivateKeyCaching) {
1547 privateKeyCache.clear();
1548 }
1549 }
1550
1551 public boolean isEnablePrivateKeyCaching() {
1552 return enablePrivateKeyCaching;
1553 }
1554
1555 public void setEnablePrivateKeyCaching(boolean enablePrivateKeyCaching) {
1556 this.enablePrivateKeyCaching = enablePrivateKeyCaching;
1557 }
1558 }