001/*
002 * Copyright 2021-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2021-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2021-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util.ssl;
037
038
039
040import java.io.File;
041import java.io.IOException;
042import java.io.Serializable;
043import java.net.Socket;
044import java.security.KeyStoreException;
045import java.security.MessageDigest;
046import java.security.Principal;
047import java.security.PrivateKey;
048import java.security.cert.X509Certificate;
049import java.util.ArrayList;
050import java.util.Arrays;
051import java.util.Collections;
052import java.util.List;
053import java.util.logging.Level;
054import javax.net.ssl.X509KeyManager;
055
056import com.unboundid.ldap.sdk.DN;
057import com.unboundid.util.CryptoHelper;
058import com.unboundid.util.Debug;
059import com.unboundid.util.DebugType;
060import com.unboundid.util.NotMutable;
061import com.unboundid.util.NotNull;
062import com.unboundid.util.Nullable;
063import com.unboundid.util.StaticUtils;
064import com.unboundid.util.ThreadSafety;
065import com.unboundid.util.ThreadSafetyLevel;
066import com.unboundid.util.Validator;
067import com.unboundid.util.ssl.cert.CertException;
068import com.unboundid.util.ssl.cert.PKCS8PEMFileReader;
069import com.unboundid.util.ssl.cert.PKCS8PrivateKey;
070import com.unboundid.util.ssl.cert.X509PEMFileReader;
071
072import static com.unboundid.util.ssl.SSLMessages.*;
073
074
075
076/**
077 * This class provides an implementation of an X.509 key manager that can obtain
078 * a certificate chain and private key from PEM files.  This key manager will
079 * only support a single entry, and the alias for that entry will be a SHA-256
080 * fingerprint for the certificate.  However, the certificate can be retrieved
081 * with any (or no) alias.
082 */
083@NotMutable()
084@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
085public final class PEMFileKeyManager
086       implements X509KeyManager, Serializable
087{
088  /**
089   * The serial version UID for this serializable class.
090   */
091  private static final   long serialVersionUID = 1973401278035832777L;
092
093
094
095  /**
096   * The name of the digest algorithm that will be used to generate a
097   * certificate fingerprint for use as the alias.
098   */
099  @NotNull private static final String ALIAS_FINGERPRINT_ALGORITHM = "SHA-256";
100
101
102
103  // The certificate chain read from PEM files.
104  @NotNull private final X509Certificate[] certificateChain;
105
106  // The private key read from a PEM file.
107  @NotNull private final PrivateKey privateKey;
108
109  // The alias that will be used for the certificate chain.
110  @NotNull private final String alias;
111
112
113
114  /**
115   * Creates a new instance of this key manager with the provided PEM files.
116   *
117   * @param  certificateChainPEMFile
118   *              The file containing the PEM-formatted X.509 representations of
119   *              the certificates in the certificate chain.  This must not be
120   *              {@code null}, the file must exist, and it must contain at
121   *              least one certificate (the end entity certificate), but may
122   *              contain additional certificates as needed for the complete
123   *              certificate chain.  Certificates should be ordered such that
124   *              the first certificate must be the end entity certificate, and
125   *              each subsequent certificate must be the issuer for the
126   *              previous certificate.  The chain does not need to be complete
127   *              as long as the peer may be expected to have prior knowledge of
128   *              any missing issuer certificates.
129   * @param  privateKeyPEMFile
130   *              The file containing the PEM-formatted PKCS #8 representation
131   *              of the private key for the end entity certificate.  This must
132   *              not be {@code null}, the file must exist, and it must contain
133   *              exactly one PEM-encoded private key.  The private key must not
134   *              be encrypted.
135   *
136   * @throws  KeyStoreException  If there is a problem with any of the provided
137   *                             PEM files.
138   */
139  public PEMFileKeyManager(@NotNull final File certificateChainPEMFile,
140                           @NotNull final File privateKeyPEMFile)
141         throws KeyStoreException
142  {
143    this(Collections.singletonList(certificateChainPEMFile), privateKeyPEMFile);
144  }
145
146
147
148  /**
149   * Creates a new instance of this key manager with the provided PEM files.
150   *
151   * @param  certificateChainPEMFile
152   *              The file containing the PEM-formatted X.509 representations of
153   *              the certificates in the certificate chain.  This must not be
154   *              {@code null}, the file must exist, and it must contain at
155   *              least one certificate (the end entity certificate), but may
156   *              contain additional certificates as needed for the complete
157   *              certificate chain.  Certificates should be ordered such that
158   *              the first certificate must be the end entity certificate, and
159   *              each subsequent certificate must be the issuer for the
160   *              previous certificate.  The chain does not need to be complete
161   *              as long as the peer may be expected to have prior knowledge of
162   *              any missing issuer certificates.
163   * @param  privateKeyPEMFile
164   *              The file containing the PEM-formatted PKCS #8 representation
165   *              of the private key for the end entity certificate.  This must
166   *              not be {@code null}, the file must exist, and it must contain
167   *              exactly one PEM-encoded private key.  The private key may
168   *              optionally be encrypted.
169   * @param  privateKeyEncryptionPassword
170   *              The password needed to decrypt the private key if it is
171   *              encrypted.  This may be {@code null} if the private key is not
172   *              encrypted.
173   *
174   * @throws  KeyStoreException  If there is a problem with any of the provided
175   *                             PEM files.
176   */
177  public PEMFileKeyManager(@NotNull final File certificateChainPEMFile,
178                           @NotNull final File privateKeyPEMFile,
179                           @Nullable final char[] privateKeyEncryptionPassword)
180         throws KeyStoreException
181  {
182    this(Collections.singletonList(certificateChainPEMFile), privateKeyPEMFile,
183         privateKeyEncryptionPassword);
184  }
185
186
187
188  /**
189   * Creates a new instance of this key manager with the provided PEM files.
190   *
191   * @param  certificateChainPEMFiles
192   *              The files containing the PEM-formatted X.509 representations
193   *              of the certificates in the certificate chain.  This must not
194   *              be {@code null} or empty.  Each file must exist and must
195   *              contain at least one certificate.  The files will be processed
196   *              in the order in which they are provided.  The first
197   *              certificate in the first file must be the end entity
198   *              certificate, and each subsequent certificate must be the
199   *              issuer for the previous certificate.  The chain does not need
200   *              to be complete as long as the peer may be expected to have
201   *              prior knowledge of any missing issuer certificates.
202   * @param  privateKeyPEMFile
203   *              The file containing the PEM-formatted PKCS #8 representation
204   *              of the private key for the end entity certificate.  This must
205   *              not be {@code null}, the file must exist, and it must contain
206   *              exactly one PEM-encoded private key.  The private key must not
207   *              be encrypted.
208   *
209   * @throws  KeyStoreException  If there is a problem with any of the provided
210   *                             PEM files.
211   */
212  public PEMFileKeyManager(@NotNull final File[] certificateChainPEMFiles,
213                           @NotNull final File privateKeyPEMFile)
214         throws KeyStoreException
215  {
216    this(StaticUtils.toList(certificateChainPEMFiles), privateKeyPEMFile);
217  }
218
219
220
221  /**
222   * Creates a new instance of this key manager with the provided PEM files.
223   *
224   * @param  certificateChainPEMFiles
225   *              The files containing the PEM-formatted X.509 representations
226   *              of the certificates in the certificate chain.  This must not
227   *              be {@code null} or empty.  Each file must exist and must
228   *              contain at least one certificate.  The files will be processed
229   *              in the order in which they are provided.  The first
230   *              certificate in the first file must be the end entity
231   *              certificate, and each subsequent certificate must be the
232   *              issuer for the previous certificate.  The chain does not need
233   *              to be complete as long as the peer may be expected to have
234   *              prior knowledge of any missing issuer certificates.
235   * @param  privateKeyPEMFile
236   *              The file containing the PEM-formatted PKCS #8 representation
237   *              of the private key for the end entity certificate.  This must
238   *              not be {@code null}, the file must exist, and it must contain
239   *              exactly one PEM-encoded private key.  The private key may
240   *              optionally be encrypted.
241   * @param  privateKeyEncryptionPassword
242   *              The password needed to decrypt the private key if it is
243   *              encrypted.  This may be {@code null} if the private key is not
244   *              encrypted.
245   *
246   * @throws  KeyStoreException  If there is a problem with any of the provided
247   *                             PEM files.
248   */
249  public PEMFileKeyManager(@NotNull final File[] certificateChainPEMFiles,
250                           @NotNull final File privateKeyPEMFile,
251                           @Nullable final char[] privateKeyEncryptionPassword)
252         throws KeyStoreException
253  {
254    this(StaticUtils.toList(certificateChainPEMFiles), privateKeyPEMFile,
255         privateKeyEncryptionPassword);
256  }
257
258
259
260  /**
261   * Creates a new instance of this key manager with the provided PEM files.
262   *
263   * @param  certificateChainPEMFiles
264   *              The files containing the PEM-formatted X.509 representations
265   *              of the certificates in the certificate chain.  This must not
266   *              be {@code null} or empty.  Each file must exist and must
267   *              contain at least one certificate.  The files will be processed
268   *              in the order in which they are provided.  The first
269   *              certificate in the first file must be the end entity
270   *              certificate, and each subsequent certificate must be the
271   *              issuer for the previous certificate.  The chain does not need
272   *              to be complete as long as the peer may be expected to have
273   *              prior knowledge of any missing issuer certificates.
274   * @param  privateKeyPEMFile
275   *              The file containing the PEM-formatted PKCS #8 representation
276   *              of the private key for the end entity certificate.  This must
277   *              not be {@code null}, the file must exist, and it must contain
278   *              exactly one PEM-encoded private key.  The private key must not
279   *              be encrypted.
280   *
281   * @throws  KeyStoreException  If there is a problem with any of the provided
282   *                             PEM files.
283   */
284  public PEMFileKeyManager(@NotNull final List<File> certificateChainPEMFiles,
285                           @NotNull final File privateKeyPEMFile)
286         throws KeyStoreException
287  {
288    this(certificateChainPEMFiles, privateKeyPEMFile, null);
289  }
290
291
292
293  /**
294   * Creates a new instance of this key manager with the provided PEM files.
295   *
296   * @param  certificateChainPEMFiles
297   *              The files containing the PEM-formatted X.509 representations
298   *              of the certificates in the certificate chain.  This must not
299   *              be {@code null} or empty.  Each file must exist and must
300   *              contain at least one certificate.  The files will be processed
301   *              in the order in which they are provided.  The first
302   *              certificate in the first file must be the end entity
303   *              certificate, and each subsequent certificate must be the
304   *              issuer for the previous certificate.  The chain does not need
305   *              to be complete as long as the peer may be expected to have
306   *              prior knowledge of any missing issuer certificates.
307   * @param  privateKeyPEMFile
308   *              The file containing the PEM-formatted PKCS #8 representation
309   *              of the private key for the end entity certificate.  This must
310   *              not be {@code null}, the file must exist, and it must contain
311   *              exactly one PEM-encoded private key.  The private key may
312   *              optionally be encrypted.
313   * @param  privateKeyEncryptionPassword
314   *              The password needed to decrypt the private key if it is
315   *              encrypted.  This may be {@code null} if the private key is not
316   *              encrypted.
317   *
318   * @throws  KeyStoreException  If there is a problem with any of the provided
319   *                             PEM files.
320   */
321  public PEMFileKeyManager(@NotNull final List<File> certificateChainPEMFiles,
322                           @NotNull final File privateKeyPEMFile,
323                           @Nullable final char[] privateKeyEncryptionPassword)
324         throws KeyStoreException
325  {
326    Validator.ensureNotNullWithMessage(certificateChainPEMFiles,
327         "PEMFileKeyManager.certificateChainPEMFiles must not be null.");
328    Validator.ensureFalse(certificateChainPEMFiles.isEmpty(),
329         "PEMFileKeyManager.certificateChainPEMFiles must not be empty.");
330    Validator.ensureNotNullWithMessage(privateKeyPEMFile,
331         "PEMFileKeyManager.privateKeyPEMFile must not be null.");
332
333    certificateChain = readCertificateChain(certificateChainPEMFiles);
334    privateKey = readPrivateKey(privateKeyPEMFile,
335         privateKeyEncryptionPassword);
336
337
338    // Compute a SHA-256 fingerprint for the certificate to use as the alias.
339    try
340    {
341      final MessageDigest sha256 =
342           CryptoHelper.getMessageDigest(ALIAS_FINGERPRINT_ALGORITHM);
343      final byte[] digestBytes =
344           sha256.digest(certificateChain[0].getEncoded());
345      alias = StaticUtils.toHex(digestBytes);
346    }
347    catch (final Exception e)
348    {
349      Debug.debugException(e);
350      throw new KeyStoreException(
351           ERR_PEM_FILE_KEY_MANAGER_CANNOT_COMPUTE_ALIAS.get(
352                ALIAS_FINGERPRINT_ALGORITHM,
353                StaticUtils.getExceptionMessage(e)),
354           e);
355    }
356  }
357
358
359
360  /**
361   * Reads the certificate chain from the provided PEM files.
362   *
363   * @param  certificateChainPEMFiles  The files containing the PEM-formatted
364   *                                   X.509 representations of the certificates
365   *                                   in the certificate chain.  This must not
366   *                                   be {@code null} or empty.  Each file must
367   *                                   exist and must contain at least one
368   *                                   certificate.  The files will be processed
369   *                                   in the order in which they are provided.
370   *                                   The first certificate in the first file
371   *                                   must be the end entity certificate, and
372   *                                   each subsequent certificate must be the
373   *                                   issuer for the previous certificate.  The
374   *                                   chain does not need to be complete as
375   *                                   long as the peer may be expected to have
376   *                                   prior knowledge of any missing issuer
377   *                                   certificates.
378   *
379   * @return  The certificate chain that was read.
380   *
381   * @throws  KeyStoreException  If a problem is encountered while reading the
382   *                             certificate chain.
383   */
384  @NotNull()
385  private static X509Certificate[] readCertificateChain(
386               @NotNull final List<File> certificateChainPEMFiles)
387          throws KeyStoreException
388  {
389    com.unboundid.util.ssl.cert.X509Certificate lastCert = null;
390
391    final List<X509Certificate> certList = new ArrayList<>();
392    for (final File f : certificateChainPEMFiles)
393    {
394      if (! f.exists())
395      {
396        throw new KeyStoreException(
397             ERR_PEM_FILE_KEY_MANAGER_NO_SUCH_CERT_FILE.get(
398                  f.getAbsolutePath()));
399      }
400
401      boolean readCert = false;
402      try (final X509PEMFileReader r = new X509PEMFileReader(f))
403      {
404        while  (true)
405        {
406          final com.unboundid.util.ssl.cert.X509Certificate c =
407               r.readCertificate();
408          if (c == null)
409          {
410            if (! readCert)
411            {
412              throw new KeyStoreException(
413                   ERR_PEM_FILE_KEY_MANAGER_EMPTY_CERT_FILE.get(
414                        f.getAbsolutePath()));
415            }
416
417            break;
418          }
419
420          readCert = true;
421          if ((lastCert != null) && (! c.isIssuerFor(lastCert)))
422          {
423            throw new KeyStoreException(
424                 ERR_PEM_FILE_KEY_MANAGER_SUBSEQUENT_CERT_NOT_ISSUER.get(
425                      c.getSubjectDN().toString(), f.getAbsolutePath(),
426                      lastCert.getSubjectDN().toString()));
427          }
428
429          try
430          {
431            certList.add((X509Certificate) c.toCertificate());
432          }
433          catch (final Exception e)
434          {
435            Debug.debugException(e);
436            throw new KeyStoreException(
437                 ERR_PEM_FILE_KEY_MANAGER_CANNOT_DECODE_CERT.get(
438                      c.getSubjectDN().toString(), f.getAbsolutePath(),
439                      StaticUtils.getExceptionMessage(e)),
440                 e);
441          }
442
443          lastCert = c;
444        }
445      }
446      catch (final KeyStoreException e)
447      {
448        Debug.debugException(e);
449        throw e;
450      }
451      catch (final IOException e)
452      {
453        Debug.debugException(e);
454        throw new KeyStoreException(
455             ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_FROM_FILE.get(
456                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
457             e);
458      }
459      catch (final CertException e)
460      {
461        Debug.debugException(e);
462        throw new KeyStoreException(
463             ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_CERT.get(
464                  f.getAbsolutePath(), e.getMessage()),
465             e);
466      }
467    }
468
469    final X509Certificate[] chain = new X509Certificate[certList.size()];
470    return certList.toArray(chain);
471  }
472
473
474
475  /**
476   * Reads the private key from the provided PEM file.
477   *
478   *
479   * @param  privateKeyPEMFile
480   *              The file containing the PEM-formatted PKCS #8 representation
481   *              of the private key for the end entity certificate.  This must
482   *              not be {@code null}, the file must exist, and it must contain
483   *              exactly one PEM-encoded private key.  The private key may
484   *              optionally be encrypted.
485   * @param  encryptionPassword
486   *              The password needed to decrypt the private key if it is
487   *              encrypted.  This may be {@code null} if the private key is not
488   *              encrypted.
489   *
490   * @return  The private key that was read.
491   *
492   * @throws  KeyStoreException  If a problem is encountered while reading the
493   *                             certificate chain.
494   */
495  @NotNull()
496  private static PrivateKey readPrivateKey(
497               @NotNull final File privateKeyPEMFile,
498               @Nullable final char[] encryptionPassword)
499          throws KeyStoreException
500  {
501    if (! privateKeyPEMFile.exists())
502    {
503      throw new KeyStoreException(
504           ERR_PEM_FILE_KEY_MANAGER_NO_SUCH_KEY_FILE.get(
505                privateKeyPEMFile.getAbsolutePath()));
506    }
507
508    try (PKCS8PEMFileReader r = new PKCS8PEMFileReader(privateKeyPEMFile))
509    {
510      final PKCS8PrivateKey privateKey = r.readPrivateKey(encryptionPassword);
511      if (privateKey == null)
512      {
513        throw new KeyStoreException(
514             ERR_PEM_FILE_KEY_MANAGER_EMPTY_KEY_FILE.get(
515                  privateKeyPEMFile.getAbsolutePath()));
516      }
517
518      if (r.readPrivateKey(encryptionPassword) != null)
519      {
520        throw new KeyStoreException(
521             ERR_PEM_FILE_KEY_MANAGER_MULTIPLE_KEYS_IN_FILE.get(
522                  privateKeyPEMFile.getAbsolutePath()));
523      }
524
525      try
526      {
527        return privateKey.toPrivateKey();
528      }
529      catch (final Exception e)
530      {
531        Debug.debugException(e);
532        throw new KeyStoreException(
533             ERR_PEM_FILE_KEY_MANAGER_CANNOT_DECODE_KEY.get(
534                  privateKeyPEMFile.getAbsolutePath(),
535                  StaticUtils.getExceptionMessage(e)),
536             e);
537      }
538    }
539    catch (final KeyStoreException e)
540    {
541      Debug.debugException(e);
542      throw e;
543    }
544    catch (final IOException e)
545    {
546      Debug.debugException(e);
547      throw new KeyStoreException(
548           ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_FROM_FILE.get(
549                privateKeyPEMFile.getAbsolutePath(),
550                StaticUtils.getExceptionMessage(e)),
551           e);
552    }
553    catch (final CertException e)
554    {
555      Debug.debugException(e);
556      throw new KeyStoreException(
557           ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_KEY.get(
558                privateKeyPEMFile.getAbsolutePath(), e.getMessage()),
559           e);
560    }
561  }
562
563
564
565  /**
566   * Retrieves the aliases that may be used for a client certificate chain with
567   * the requested settings.
568   *
569   * @param  keyType  The key type for the alias to retrieve.  It may be
570   *                  {@code null} if any key type may be used.
571   * @param  issuers  The set of allowed issuers for the aliases to retrieve.
572   *                  It may be {@code null} if any issuers should be allowed.
573   *
574   * @return  An array of the aliases that may be used for a client certificate
575   *          chain with the requested settings, or {@code null} if the
576   *          certificate chain does not match the requested criteria.
577   */
578  @Override()
579  @Nullable()
580  public String[] getClientAliases(@Nullable final String keyType,
581                                   @Nullable final Principal[] issuers)
582  {
583    return getAliases(keyType, issuers);
584  }
585
586
587
588  /**
589   * Retrieves the aliases that may be used for a server certificate chain with
590   * the requested settings.
591   *
592   * @param  keyType  The key type for the alias to retrieve.  It may be
593   *                  {@code null} if any key type may be used.
594   * @param  issuers  The set of allowed issuers for the aliases to retrieve.
595   *                  It may be {@code null} if any issuers should be allowed.
596   *
597   * @return  An array of the aliases that may be used for a server certificate
598   *          chain with the requested settings, or {@code null} if the
599   *          certificate chain does not match the requested criteria.
600   */
601  @Override()
602  @Nullable()
603  public String[] getServerAliases(@Nullable final String keyType,
604                                   @Nullable final Principal[] issuers)
605  {
606    return getAliases(keyType, issuers);
607  }
608
609
610
611  /**
612   * Retrieves the aliases that may be used for a certificate chain with the
613   * requested settings.
614   *
615   * @param  keyType  The key type for the alias to retrieve.  It may be
616   *                  {@code null} if any key type may be used.
617   * @param  issuers  A list of acceptable CA issuer subject names.  It may be
618   *                  {@code null} if any issuers may be used.
619   *
620   * @return  An array of the aliases that may be used for a certificate chain
621   *          with the requested settings, or {@code null} if the certificate
622   *          chain does not match the requested criteria.
623   */
624  @Nullable()
625  private String[] getAliases(@Nullable final String keyType,
626                              @Nullable final Principal[] issuers)
627  {
628    if (! hasKeyType(keyType))
629    {
630      Debug.debug(Level.WARNING, DebugType.OTHER,
631           "PEMFileKeyManager.getAliases returning null because the " +
632                "requested keyType is '" + keyType + "' but the private " +
633                "key uses an algorithm of '" + privateKey.getAlgorithm() +
634                "'.");
635      return null;
636    }
637
638    if (! hasAnyIssuer(issuers))
639    {
640      Debug.debug(Level.WARNING, DebugType.OTHER,
641           "PEMFileKeyManager.getAliases returning null because " +
642                "certificate chain " + Arrays.toString(certificateChain) +
643                " does not use any of the allowed issuers " +
644                Arrays.toString(issuers));
645
646      return null;
647    }
648
649    return new String[] { alias };
650  }
651
652
653
654  /**
655   * Chooses the alias that should be used for the preferred client certificate
656   * chain with the requested settings.
657   *
658   * @param  keyTypes  The set of allowed key types for the alias to retrieve.
659   *                   It may be {@code null} if any key type may be used.
660   * @param  issuers   The set of allowed issuers for the alias to retrieve.  It
661   *                   may be {@code null} if any issuers should be allowed.
662   * @param  socket    The socket with which the certificate chain will be used.
663   *                   It may be {@code null} if no socket should be taken into
664   *                   consideration.
665   *
666   * @return  The alias that should be used for the preferred client certificate
667   *          chain with the requested settings, or {@code null} if there is no
668   *          applicable alias.
669   */
670  @Override()
671  @Nullable()
672  public String chooseClientAlias(@Nullable final String[] keyTypes,
673                                  @Nullable final Principal[] issuers,
674                                  @Nullable final Socket socket)
675  {
676    return chooseAlias(keyTypes, issuers);
677  }
678
679
680
681  /**
682   * Chooses the alias that should be used for the preferred server certificate
683   * chain with the requested settings.
684   *
685   * @param  keyType  The key type for the alias to retrieve.  It may be
686   *                  {@code null} if any key type may be u sed.
687   * @param  issuers  The set of allowed issuers for the alias to retrieve.  It
688   *                  may be {@code null} if any issuers should be allowed.
689   * @param  socket   The socket with which the certificate chain will be used.
690   *                  It may be {@code null} if no socket should be taken into
691   *                  consideration.
692   *
693   * @return  The alias that should be used for the preferred server certificate
694   *          chain with the requested settings, or {@code null} if there is no
695   *          applicable alias.
696   */
697  @Override()
698  @Nullable()
699  public String chooseServerAlias(@Nullable final String keyType,
700                                  @Nullable final Principal[] issuers,
701                                  @Nullable final Socket socket)
702  {
703    if (keyType == null)
704    {
705      return chooseAlias(null, issuers);
706    }
707    else
708    {
709      return chooseAlias(new String[] { keyType }, issuers);
710    }
711  }
712
713
714
715  /**
716   * Chooses the alias that should be used for the preferred certificate chain
717   * with the requested settings.
718   *
719   * @param  keyTypes  The set of allowed key types for the alias to retrieve.
720   *                   It may be {@code null} if any key type may be used.
721   * @param  issuers   The set of allowed issuers for the alias to retrieve.  It
722   *                   may be {@code null} if any issuers should be allowed.
723   *
724   * @return  The alias that should be used for the preferred certificate chain
725   *          with the requested settings, or {@code null} if there is no
726   *          applicable alias.
727   */
728  @Nullable()
729  public String chooseAlias(@Nullable final String[] keyTypes,
730                            @Nullable final Principal[] issuers)
731  {
732    if ((keyTypes != null) && (keyTypes.length > 0))
733    {
734      boolean keyTypeFound = false;
735      for (final String keyType : keyTypes)
736      {
737        if (hasKeyType(keyType))
738        {
739          keyTypeFound = true;
740          break;
741        }
742      }
743
744      if (! keyTypeFound)
745      {
746        Debug.debug(Level.WARNING, DebugType.OTHER,
747             "PEMFileKeyManager.chooseAlias returning null because " +
748                  "certificate chain " + Arrays.toString(certificateChain) +
749                  " uses a key type of " + privateKey.getAlgorithm() +
750                  ", which does not match any of the allowed key types of " +
751                  Arrays.toString(keyTypes));
752
753        return null;
754      }
755    }
756
757    if (! hasAnyIssuer(issuers))
758    {
759      Debug.debug(Level.WARNING, DebugType.OTHER,
760           "PEMFileKeyManager.chooseAlias returning null because " +
761                "certificate chain " + Arrays.toString(certificateChain) +
762                " does not use any of the allowed issuers " +
763                Arrays.toString(issuers));
764
765      return null;
766    }
767
768    return alias;
769  }
770
771
772
773  /**
774   * Indicates whether the certificate chain has the specified key type.
775   *
776   * @param  keyType  The key type for which to make the determination.  It may
777   *                  be {@code null} if the key type does not matter.
778   *
779   * @return  {@code true} if the certificate chain has the specified key type
780   *          (or if the key type does not matter), or {@code false} if not.
781   */
782  private boolean hasKeyType(@Nullable final String keyType)
783  {
784    return ((keyType == null) ||
785         privateKey.getAlgorithm().equalsIgnoreCase(keyType));
786  }
787
788
789
790  /**
791   * Indicates whether the certificate chain has any of the issuers in the
792   * provided array.
793   *
794   * @param  issuers  The array of acceptable issuers.  It may be
795   *                  {@code null} if the set of issuers does not matter.
796   *
797   * @return  {@code true} if the certificate chain uses one of the accepted
798   *          issuers (or if the issuers do not matter), or {@code false} if
799   *          not.
800   */
801  private boolean hasAnyIssuer(@Nullable final Principal[] issuers)
802  {
803    if ((issuers == null) || (issuers.length == 0))
804    {
805      return true;
806    }
807
808
809    // Check all of the issuer certificates for the chain.
810    for (final Principal acceptableIssuer : issuers)
811    {
812      final String acceptableIssuerString = acceptableIssuer.toString();
813      for (final X509Certificate c : certificateChain)
814      {
815        final Principal certificateIssuer = c.getIssuerDN();
816        final String certificateIssuerString = certificateIssuer.toString();
817        try
818        {
819          if (DN.equals(certificateIssuerString, acceptableIssuerString))
820          {
821            return true;
822          }
823        }
824        catch (final Exception e)
825        {
826          Debug.debugException(e);
827        }
828      }
829    }
830
831
832    // Also check the subject DN for the first certificate in the chain.
833    final Principal endEntitySubject = certificateChain[0].getSubjectDN();
834    final String endEntitySubjectString = endEntitySubject.toString();
835    for (final Principal acceptableIssuer : issuers)
836    {
837      final String acceptableIssuerString = acceptableIssuer.toString();
838      try
839      {
840        if (DN.equals(endEntitySubjectString, acceptableIssuerString))
841        {
842          return true;
843        }
844      }
845      catch (final Exception e)
846      {
847        Debug.debugException(e);
848      }
849    }
850
851
852    return false;
853  }
854
855
856
857  /**
858   * Retrieves the certificate chain with the specified alias.  Note that
859   * because this key manager implementation can only use a single certificate
860   * chain, it will always return the same chain for any alias, even if the
861   * requested alias is {@code null}.
862   *
863   * @param  alias  The alias for the certificate chain to retrieve.
864   *
865   * @return  The certificate chain for this key manager.
866   */
867  @Override()
868  @NotNull()
869  public X509Certificate[] getCertificateChain(@Nullable final String alias)
870  {
871    return Arrays.copyOf(certificateChain, certificateChain.length);
872  }
873
874
875
876  /**
877   * Retrieves the private key for the certificate chain with the specified
878   * alias.  Note that because this key manager implementation can only use a
879   * single certificate chain, it will always return the same private key for
880   * any alias, even if the requested alias is {@code null}.
881   *
882   * @param  alias  The alias for the private key to retrieve.
883   *
884   * @return  The private key for this key manager.
885   */
886  @Override()
887  @NotNull()
888  public PrivateKey getPrivateKey(@Nullable final String alias)
889  {
890    return privateKey;
891  }
892}