View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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   * A Crypto implementation based on two Java KeyStore objects, one being the keystore, and one
75   * being the truststore.
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       * Deprecated types
87       */
88      public static final String OLD_KEYSTORE_FILE = "file";
89  
90      /*
91       * Crypto providers
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       * KeyStore configuration types
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      * TrustStore configuration types
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      * CRL configuration
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         // default constructor
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         // Load the provider(s)
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         // Load the KeyStore
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         // Load the TrustStore
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             // Fallback to keystore provider for compatibility reason
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         // Load the CRL file(s)
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      * Load a KeyStore object as an InputStream, using the ClassLoader and location arguments
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      * Loads the keystore from an <code>InputStream </code>.
362      * <p/>
363      *
364      * @param input <code>InputStream</code> to read from
365      * @throws WSSecurityException
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     // Accessor methods
389     //
390 
391     /**
392      * Gets the Keystore that was loaded
393      *
394      * @return the Keystore
395      */
396     public KeyStore getKeyStore() {
397         return keystore;
398     }
399 
400     /**
401      * Set the Keystore on this Crypto instance
402      *
403      * @param keyStore the Keystore to set
404      */
405     public void setKeyStore(KeyStore keyStore) {
406         keystore = keyStore;
407     }
408 
409     /**
410      * Gets the trust store that was loaded by the underlying implementation
411      *
412      * @return the trust store
413      */
414     public KeyStore getTrustStore() {
415         return truststore;
416     }
417 
418     /**
419      * Set the trust store on this Crypto instance
420      *
421      * @param trustStore the trust store to set
422      */
423     public void setTrustStore(KeyStore trustStore) {
424         truststore = trustStore;
425     }
426 
427     /**
428      * Set the CertStore from which to obtain a list of CRLs for Certificate Revocation
429      * checking.
430      * @param crlCertStore the CertStore from which to obtain a list of CRLs for Certificate
431      * Revocation checking.
432      */
433     public void setCRLCertStore(CertStore crlCertStore) {
434         this.crlCertStore = crlCertStore;
435     }
436 
437     /**
438      * Get the CertStore from which to obtain a list of CRLs for Certificate Revocation
439      * checking.
440      * @return the CertStore from which to obtain a list of CRLs for Certificate
441      * Revocation checking.
442      */
443     public CertStore getCRLCertStore() {
444         return crlCertStore;
445     }
446 
447     /**
448      * Singleton certificate factory for this Crypto instance.
449      * <p/>
450      *
451      * @return Returns a <code>CertificateFactory</code> to construct
452      *         X509 certificates
453      * @throws WSSecurityException
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                         //Ignore, we'll just use the default since they didn't specify one.
479                         //Hopefully that will work for them.
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      * Retrieves the identifier name of the default certificate. This should be the certificate
510      * that is used for signature and encryption. This identifier corresponds to the certificate
511      * that should be used whenever KeyInfo is not present in a signed or an encrypted
512      * message. May return null. The identifier is implementation specific, e.g. it could be the
513      * KeyStore alias.
514      *
515      * @return name of the default X509 certificate.
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     // Keystore-specific Crypto functionality methods
544     //
545 
546     /**
547      * Get an X509Certificate (chain) corresponding to the CryptoType argument. The supported
548      * types are as follows:
549      *
550      * TYPE.ISSUER_SERIAL - A certificate (chain) is located by the issuer name and serial number
551      * TYPE.THUMBPRINT_SHA1 - A certificate (chain) is located by the SHA1 of the (root) cert
552      * TYPE.SKI_BYTES - A certificate (chain) is located by the SKI bytes of the (root) cert
553      * TYPE.SUBJECT_DN - A certificate (chain) is located by the Subject DN of the (root) cert
554      * TYPE.ALIAS - A certificate (chain) is located by an alias, which for this implementation
555      * means an alias of the keystore or truststore.
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      * Get the implementation-specific identifier corresponding to the cert parameter. In this
587      * case, the identifier corresponds to a KeyStore alias.
588      * @param cert The X509Certificate for which to search for an identifier
589      * @return the identifier corresponding to the cert parameter
590      * @throws WSSecurityException
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      * Gets the private key corresponding to the certificate.
608      *
609      * @param certificate The X509Certificate corresponding to the private key
610      * @param callbackHandler The callbackHandler needed to get the password
611      * @return The private key
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      * Gets the private key corresponding to the given PublicKey.
647      *
648      * @param publicKey The PublicKey corresponding to the private key
649      * @param callbackHandler The callbackHandler needed to get the password
650      * @return The private key
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      * Gets the private key corresponding to the identifier.
686      *
687      * @param identifier The implementation-specific identifier corresponding to the key
688      * @param password The password needed to get the key
689      * @return The private key
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      * Evaluate whether a given certificate chain should be trusted.
748      *
749      * @param certs Certificate chain to validate
750      * @param enableRevocation whether to enable CRL verification or not
751      * @param subjectCertConstraints A set of constraints on the Subject DN of the certificates
752      *
753      * @throws WSSecurityException if the certificate chain is invalid
754      */
755     protected void verifyTrust(
756         X509Certificate[] certs,
757         boolean enableRevocation,
758         Collection<Pattern> subjectCertConstraints
759     ) throws WSSecurityException {
760         //
761         // FIRST step - Search the keystore for the transmitted certificate
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             // If a certificate has been found, the certificates must be compared
773             // to ensure against phony DNs (compare encoded form including signature)
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         // SECOND step - Search for the issuer cert (chain) of the transmitted certificate in the
792         // keystore or the truststore
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             //If we can't find the issuer in the keystore then look at the truststore
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         // THIRD step
824         // Check the certificate trust path for the issuer cert chain
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             // Add certificates from the keystore - only if there is no TrustStore, apart from
838             // the case that the truststore is the JDK CA certs. This behaviour is preserved
839             // for backwards compatibility reasons
840             //
841             if (keystore != null && (truststore == null || loadCACerts)) {
842                 addTrustAnchors(set, keystore);
843             }
844 
845             // Verify the trust path using the above settings
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             // Generate cert path
857             if (foundIssuingCertChains != null && !foundIssuingCertChains.isEmpty()) {
858                 java.security.cert.CertPathValidatorException validatorException = null;
859                 // Try each potential issuing cert path for a match
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                         // We have a valid cert path at this point so break
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         // Finally check Cert Constraints
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     // Separated out to allow subclasses to override it
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      * Evaluate whether a given public key should be trusted.
927      *
928      * @param publicKey The PublicKey to be evaluated
929      * @throws WSSecurityException if the PublicKey is invalid
930      */
931     public void verifyTrust(PublicKey publicKey) throws WSSecurityException {
932         //
933         // If the public key is null, do not trust the signature
934         //
935         if (publicKey == null) {
936             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
937         }
938 
939         //
940         // Search the keystore for the transmitted public key (direct trust). If not found
941         // then search the truststore for the transmitted public key (direct trust)
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      * Get an X509 Certificate (chain) according to a given serial number and issuer string.
951      *
952      * @param issuer The Issuer String
953      * @param serialNumber The serial number of the certificate
954      * @return an X509 Certificate (chain) corresponding to the found certificate(s)
955      * @throws WSSecurityException
956      */
957     private X509Certificate[] getX509Certificates(
958         String issuer,
959         BigInteger serialNumber
960     ) throws WSSecurityException {
961         //
962         // Convert the subject DN to a java X500Principal object first. This is to ensure
963         // interop with a DN constructed from .NET, where e.g. it uses "S" instead of "ST".
964         // Then convert it to a BouncyCastle X509Name, which will order the attributes of
965         // the DN in a particular way (see WSS-168). If the conversion to an X500Principal
966         // object fails (e.g. if the DN contains "E" instead of "EMAILADDRESS"), then fall
967         // back on a direct conversion to a BC X509Name
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         //If we can't find the issuer in the keystore then look at the truststore
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      * Get an X509 Certificate (chain) of the X500Principal argument in the supplied KeyStore
995      * @param issuerRDN either an X500Principal or a BouncyCastle X509Name instance.
996      * @param store The KeyStore
997      * @return an X509 Certificate (chain)
998      * @throws WSSecurityException
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                     // no cert chain, so lets check if getCertificate gives us a result.
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      * Get an X509 Certificate (chain) according to a given Thumbprint.
1049      *
1050      * @param thumbprint The SHA1 thumbprint info bytes
1051      * @return the X509 Certificate (chain) that was found (can be null)
1052      * @throws WSSecurityException if problems during keystore handling or wrong certificate
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         //If we can't find the issuer in the keystore then look at the truststore
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      * Get an X509 Certificate (chain) of the X500Principal argument in the supplied KeyStore
1083      * @param thumbprint
1084      * @param store The KeyStore
1085      * @return an X509 Certificate (chain)
1086      * @throws WSSecurityException
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                     // no cert chain, so lets check if getCertificate gives us a result.
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      * Get an X509 Certificate (chain) according to a given SubjectKeyIdentifier.
1140      *
1141      * @param skiBytes The SKI bytes
1142      * @return the X509 certificate (chain) that was found (can be null)
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         //If we can't find the issuer in the keystore then look at the truststore
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      * Get an X509 Certificate (chain) of the X500Principal argument in the supplied KeyStore
1164      * @param skiBytes
1165      * @param store The KeyStore
1166      * @return an X509 Certificate (chain)
1167      * @throws WSSecurityException
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                     // no cert chain, so lets check if getCertificate gives us a result.
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      * Get an X509 Certificate (chain) according to a given DN of the subject of the certificate
1212      *
1213      * @param subjectDN The DN of subject to look for
1214      * @return An X509 Certificate (chain) with the same DN as given in the parameters
1215      * @throws WSSecurityException
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         //If we can't find the issuer in the keystore then look at the truststore
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         // We just choose the first entry
1235         return Arrays.copyOf(certs.get(0), certs.get(0).length, X509Certificate[].class);
1236     }
1237 
1238     private Object convertSubjectToPrincipal(String subjectDN) {
1239         //
1240         // Convert the subject DN to a java X500Principal object first. This is to ensure
1241         // interop with a DN constructed from .NET, where e.g. it uses "S" instead of "ST".
1242         // Then convert it to a BouncyCastle X509Name, which will order the attributes of
1243         // the DN in a particular way (see WSS-168). If the conversion to an X500Principal
1244         // object fails (e.g. if the DN contains "E" instead of "EMAILADDRESS"), then fall
1245         // back on a direct conversion to a BC X509Name
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      * Get an X509 Certificate (chain) that correspond to the identifier. For this implementation,
1257      * the identifier corresponds to the KeyStore alias.
1258      *
1259      * @param identifier The identifier that corresponds to the returned certs
1260      * @return an X509 Certificate (chain) that corresponds to the identifier
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                 // There's a chance that there can only be a set of trust stores
1270                 certs = keystore.getCertificateChain(identifier);
1271                 if (certs == null || certs.length == 0) {
1272                     // no cert chain, so lets check if getCertificate gives us a result.
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                 // Now look into the trust stores
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      * Find the Public Key in a keystore.
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                     // no cert chain, so lets check if getCertificate gives us a result.
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      * Get an X509 Certificate (chain) of the X500Principal argument in the supplied KeyStore. If multiple
1341      * certs match the Subject DN, then multiple cert chains are returned.
1342      * @param subjectRDN either an X500Principal or a BouncyCastle X509Name instance.
1343      * @param store The KeyStore
1344      * @return an X509 Certificate (chain)
1345      * @throws WSSecurityException
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                     // no cert chain, so lets check if getCertificate gives us a result.
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      * Adds {@code TrustAnchor}s found in the provided key store to the set.
1408      * <p>
1409      * When the Trust Anchors are constructed, the value of the
1410      * {@link #CRYPTO_CERT_PROVIDER_HANDLES_NAME_CONSTRAINTS} property will be checked.
1411      * If it has been set to {@code true}, then {@code NameConstraint}s will be added
1412      * to their Trust Anchors; if unset or set to false, the Name Constraints
1413      * will be nulled out on their Trust Anchors.
1414      *
1415      * The default Sun PKIX Path Validator does not support Name Constraints on
1416      * Trust Anchors and will throw an InvalidAlgorithmParameterException if they
1417      * are provided. Other implementations may also be unsafe.
1418      *
1419      * @param set       the set to which to add the {@code TrustAnchor}s
1420      * @param keyStore  the store to search for {@code X509Certificate}s
1421      * @throws KeyStoreException if a problem occurs accessing the keyStore
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      * Get an implementation-specific identifier that corresponds to the X509Certificate. In
1444      * this case, the identifier is the KeyStore alias.
1445      * @param cert The X509Certificate corresponding to the returned identifier
1446      * @param store The KeyStore to search
1447      * @return An implementation-specific identifier that corresponds to the X509Certificate
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                     // no cert chain, so lets check if getCertificate gives us a  result.
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                     // no cert chain, so lets check if getCertificate gives us a  result.
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      * Get a password from the CallbackHandler
1501      * @param identifier The identifier to give to the Callback
1502      * @param cb The CallbackHandler
1503      * @return The password retrieved from the CallbackHandler
1504      * @throws WSSecurityException
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 }