001/*
002 * Copyright 2021-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2021-2022 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-2022 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  The file containing the PEM-formatted
118   *                                  X.509 representations of the certificates
119   *                                  in the certificate chain.  This must not
120   *                                  be {@code null}, the file must exist, and
121   *                                  it must contain at least one certificate
122   *                                  (the end entity certificate), but may
123   *                                  contain additional certificates as needed
124   *                                  for the complete certificate chain.
125   *                                  Certificates should be ordered such that
126   *                                  the first certificate must be the end
127   *                                  entity certificate, and each subsequent
128   *                                  certificate must be the issuer for the
129   *                                  previous certificate.  The chain does not
130   *                                  need to be complete as long as the peer
131   *                                  may be expected to have prior knowledge of
132   *                                  any missing issuer certificates.
133   * @param  privateKeyPEMFile        The file containing the PEM-formatted
134   *                                  PKCS #8 representation of the private key
135   *                                  for the end entity certificate.  This must
136   *                                  not be {@code null}, the file must exist,
137   *                                  and it must contain exactly one
138   *                                  PEM-encoded private key.
139   *
140   * @throws  KeyStoreException  If there is a problem with any of the provided
141   *                             PEM files.
142   */
143  public PEMFileKeyManager(@NotNull final File certificateChainPEMFile,
144                           @NotNull final File privateKeyPEMFile)
145         throws KeyStoreException
146  {
147    this(Collections.singletonList(certificateChainPEMFile), privateKeyPEMFile);
148  }
149
150
151
152  /**
153   * Creates a new instance of this key manager with the provided PEM files.
154   *
155   * @param  certificateChainPEMFiles  The files containing the PEM-formatted
156   *                                   X.509 representations of the certificates
157   *                                   in the certificate chain.  This must not
158   *                                   be {@code null} or empty.  Each file must
159   *                                   exist and must contain at least one
160   *                                   certificate.  The files will be processed
161   *                                   in the order in which they are provided.
162   *                                   The first certificate in the first file
163   *                                   must be the end entity certificate, and
164   *                                   each subsequent certificate must be the
165   *                                   issuer for the previous certificate.  The
166   *                                   chain does not need to be complete as
167   *                                   long as the peer may be expected to have
168   *                                   prior knowledge of any missing issuer
169   *                                   certificates.
170   * @param  privateKeyPEMFile         The file containing the PEM-formatted
171   *                                   PKCS #8 representation of the private key
172   *                                   for the end entity certificate.  This
173   *                                   must not be {@code null}, the file must
174   *                                   exist, and it must contain exactly one
175   *                                   PEM-encoded private key.
176   *
177   * @throws  KeyStoreException  If there is a problem with any of the provided
178   *                             PEM files.
179   */
180  public PEMFileKeyManager(@NotNull final File[] certificateChainPEMFiles,
181                           @NotNull final File privateKeyPEMFile)
182         throws KeyStoreException
183  {
184    this(StaticUtils.toList(certificateChainPEMFiles), privateKeyPEMFile);
185  }
186
187
188
189  /**
190   * Creates a new instance of this key manager with the provided PEM files.
191   *
192   * @param  certificateChainPEMFiles  The files containing the PEM-formatted
193   *                                   X.509 representations of the certificates
194   *                                   in the certificate chain.  This must not
195   *                                   be {@code null} or empty.  Each file must
196   *                                   exist and must contain at least one
197   *                                   certificate.  The files will be processed
198   *                                   in the order in which they are provided.
199   *                                   The first certificate in the first file
200   *                                   must be the end entity certificate, and
201   *                                   each subsequent certificate must be the
202   *                                   issuer for the previous certificate.  The
203   *                                   chain does not need to be complete as
204   *                                   long as the peer may be expected to have
205   *                                   prior knowledge of any missing issuer
206   *                                   certificates.
207   * @param  privateKeyPEMFile         The file containing the PEM-formatted
208   *                                   PKCS #8 representation of the private key
209   *                                   for the end entity certificate.  This
210   *                                   must not be {@code null}, the file must
211   *                                   exist, and it must contain exactly one
212   *                                   PEM-encoded private key.
213   *
214   * @throws  KeyStoreException  If there is a problem with any of the provided
215   *                             PEM files.
216   */
217  public PEMFileKeyManager(@NotNull final List<File> certificateChainPEMFiles,
218                           @NotNull final File privateKeyPEMFile)
219         throws KeyStoreException
220  {
221    Validator.ensureNotNullWithMessage(certificateChainPEMFiles,
222         "PEMFileKeyManager.certificateChainPEMFiles must not be null.");
223    Validator.ensureFalse(certificateChainPEMFiles.isEmpty(),
224         "PEMFileKeyManager.certificateChainPEMFiles must not be empty.");
225    Validator.ensureNotNullWithMessage(privateKeyPEMFile,
226         "PEMFileKeyManager.privateKeyPEMFile must not be null.");
227
228    certificateChain = readCertificateChain(certificateChainPEMFiles);
229    privateKey = readPrivateKey(privateKeyPEMFile);
230
231
232    // Compute a SHA-256 fingerprint for the certificate to use as the alias.
233    try
234    {
235      final MessageDigest sha256 =
236           CryptoHelper.getMessageDigest(ALIAS_FINGERPRINT_ALGORITHM);
237      final byte[] digestBytes =
238           sha256.digest(certificateChain[0].getEncoded());
239      alias = StaticUtils.toHex(digestBytes);
240    }
241    catch (final Exception e)
242    {
243      Debug.debugException(e);
244      throw new KeyStoreException(
245           ERR_PEM_FILE_KEY_MANAGER_CANNOT_COMPUTE_ALIAS.get(
246                ALIAS_FINGERPRINT_ALGORITHM,
247                StaticUtils.getExceptionMessage(e)),
248           e);
249    }
250  }
251
252
253
254  /**
255   * Reads the certificate chain from the provided PEM files.
256   *
257   * @param  certificateChainPEMFiles  The files containing the PEM-formatted
258   *                                   X.509 representations of the certificates
259   *                                   in the certificate chain.  This must not
260   *                                   be {@code null} or empty.  Each file must
261   *                                   exist and must contain at least one
262   *                                   certificate.  The files will be processed
263   *                                   in the order in which they are provided.
264   *                                   The first certificate in the first file
265   *                                   must be the end entity certificate, and
266   *                                   each subsequent certificate must be the
267   *                                   issuer for the previous certificate.  The
268   *                                   chain does not need to be complete as
269   *                                   long as the peer may be expected to have
270   *                                   prior knowledge of any missing issuer
271   *                                   certificates.
272   *
273   * @return  The certificate chain that was read.
274   *
275   * @throws  KeyStoreException  If a problem is encountered while reading the
276   *                             certificate chain.
277   */
278  @NotNull()
279  private static X509Certificate[] readCertificateChain(
280               @NotNull final List<File> certificateChainPEMFiles)
281          throws KeyStoreException
282  {
283    com.unboundid.util.ssl.cert.X509Certificate lastCert = null;
284
285    final List<X509Certificate> certList = new ArrayList<>();
286    for (final File f : certificateChainPEMFiles)
287    {
288      if (! f.exists())
289      {
290        throw new KeyStoreException(
291             ERR_PEM_FILE_KEY_MANAGER_NO_SUCH_CERT_FILE.get(
292                  f.getAbsolutePath()));
293      }
294
295      boolean readCert = false;
296      try (final X509PEMFileReader r = new X509PEMFileReader(f))
297      {
298        while  (true)
299        {
300          final com.unboundid.util.ssl.cert.X509Certificate c =
301               r.readCertificate();
302          if (c == null)
303          {
304            if (! readCert)
305            {
306              throw new KeyStoreException(
307                   ERR_PEM_FILE_KEY_MANAGER_EMPTY_CERT_FILE.get(
308                        f.getAbsolutePath()));
309            }
310
311            break;
312          }
313
314          readCert = true;
315          if ((lastCert != null) && (! c.isIssuerFor(lastCert)))
316          {
317            throw new KeyStoreException(
318                 ERR_PEM_FILE_KEY_MANAGER_SUBSEQUENT_CERT_NOT_ISSUER.get(
319                      c.getSubjectDN().toString(), f.getAbsolutePath(),
320                      lastCert.getSubjectDN().toString()));
321          }
322
323          try
324          {
325            certList.add((X509Certificate) c.toCertificate());
326          }
327          catch (final Exception e)
328          {
329            Debug.debugException(e);
330            throw new KeyStoreException(
331                 ERR_PEM_FILE_KEY_MANAGER_CANNOT_DECODE_CERT.get(
332                      c.getSubjectDN().toString(), f.getAbsolutePath(),
333                      StaticUtils.getExceptionMessage(e)),
334                 e);
335          }
336
337          lastCert = c;
338        }
339      }
340      catch (final KeyStoreException e)
341      {
342        Debug.debugException(e);
343        throw e;
344      }
345      catch (final IOException e)
346      {
347        Debug.debugException(e);
348        throw new KeyStoreException(
349             ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_FROM_FILE.get(
350                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
351             e);
352      }
353      catch (final CertException e)
354      {
355        Debug.debugException(e);
356        throw new KeyStoreException(
357             ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_CERT.get(
358                  f.getAbsolutePath(), e.getMessage()),
359             e);
360      }
361    }
362
363    final X509Certificate[] chain = new X509Certificate[certList.size()];
364    return certList.toArray(chain);
365  }
366
367
368
369  /**
370   * Reads the private key from the provided PEM file.
371   *
372   *
373   * @param  privateKeyPEMFile         The file containing the PEM-formatted
374   *                                   PKCS #8 representation of the private key
375   *                                   for the end entity certificate.  This
376   *                                   must not be {@code null}, the file must
377   *                                   exist, and it must contain exactly one
378   *                                   PEM-encoded private key.
379   *
380   * @return  The private key that was read.
381   *
382   * @throws  KeyStoreException  If a problem is encountered while reading the
383   *                             certificate chain.
384   */
385  @NotNull()
386  private static PrivateKey readPrivateKey(
387               @NotNull final File privateKeyPEMFile)
388          throws KeyStoreException
389  {
390    if (! privateKeyPEMFile.exists())
391    {
392      throw new KeyStoreException(
393           ERR_PEM_FILE_KEY_MANAGER_NO_SUCH_KEY_FILE.get(
394                privateKeyPEMFile.getAbsolutePath()));
395    }
396
397    try (PKCS8PEMFileReader r = new PKCS8PEMFileReader(privateKeyPEMFile))
398    {
399      final PKCS8PrivateKey privateKey = r.readPrivateKey();
400      if (privateKey == null)
401      {
402        throw new KeyStoreException(
403             ERR_PEM_FILE_KEY_MANAGER_EMPTY_KEY_FILE.get(
404                  privateKeyPEMFile.getAbsolutePath()));
405      }
406
407      if (r.readPrivateKey() != null)
408      {
409        throw new KeyStoreException(
410             ERR_PEM_FILE_KEY_MANAGER_MULTIPLE_KEYS_IN_FILE.get(
411                  privateKeyPEMFile.getAbsolutePath()));
412      }
413
414      try
415      {
416        return privateKey.toPrivateKey();
417      }
418      catch (final Exception e)
419      {
420        Debug.debugException(e);
421        throw new KeyStoreException(
422             ERR_PEM_FILE_KEY_MANAGER_CANNOT_DECODE_KEY.get(
423                  privateKeyPEMFile.getAbsolutePath(),
424                  StaticUtils.getExceptionMessage(e)),
425             e);
426      }
427    }
428    catch (final KeyStoreException e)
429    {
430      Debug.debugException(e);
431      throw e;
432    }
433    catch (final IOException e)
434    {
435      Debug.debugException(e);
436      throw new KeyStoreException(
437           ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_FROM_FILE.get(
438                privateKeyPEMFile.getAbsolutePath(),
439                StaticUtils.getExceptionMessage(e)),
440           e);
441    }
442    catch (final CertException e)
443    {
444      Debug.debugException(e);
445      throw new KeyStoreException(
446           ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_KEY.get(
447                privateKeyPEMFile.getAbsolutePath(), e.getMessage()),
448           e);
449    }
450  }
451
452
453
454  /**
455   * Retrieves the aliases that may be used for a client certificate chain with
456   * the requested settings.
457   *
458   * @param  keyType  The key type for the alias to retrieve.  It may be
459   *                  {@code null} if any key type may be used.
460   * @param  issuers  The set of allowed issuers for the aliases to retrieve.
461   *                  It may be {@code null} if any issuers should be allowed.
462   *
463   * @return  An array of the aliases that may be used for a client certificate
464   *          chain with the requested settings, or {@code null} if the
465   *          certificate chain does not match the requested criteria.
466   */
467  @Override()
468  @Nullable()
469  public String[] getClientAliases(@Nullable final String keyType,
470                                   @Nullable final Principal[] issuers)
471  {
472    return getAliases(keyType, issuers);
473  }
474
475
476
477  /**
478   * Retrieves the aliases that may be used for a server certificate chain with
479   * the requested settings.
480   *
481   * @param  keyType  The key type for the alias to retrieve.  It may be
482   *                  {@code null} if any key type may be used.
483   * @param  issuers  The set of allowed issuers for the aliases to retrieve.
484   *                  It may be {@code null} if any issuers should be allowed.
485   *
486   * @return  An array of the aliases that may be used for a server certificate
487   *          chain with the requested settings, or {@code null} if the
488   *          certificate chain does not match the requested criteria.
489   */
490  @Override()
491  @Nullable()
492  public String[] getServerAliases(@Nullable final String keyType,
493                                   @Nullable final Principal[] issuers)
494  {
495    return getAliases(keyType, issuers);
496  }
497
498
499
500  /**
501   * Retrieves the aliases that may be used for a certificate chain with the
502   * requested settings.
503   *
504   * @param  keyType  The key type for the alias to retrieve.  It may be
505   *                  {@code null} if any key type may be used.
506   * @param  issuers  A list of acceptable CA issuer subject names.  It may be
507   *                  {@code null} if any issuers may be used.
508   *
509   * @return  An array of the aliases that may be used for a certificate chain
510   *          with the requested settings, or {@code null} if the certificate
511   *          chain does not match the requested criteria.
512   */
513  @Nullable()
514  private String[] getAliases(@Nullable final String keyType,
515                              @Nullable final Principal[] issuers)
516  {
517    if (! hasKeyType(keyType))
518    {
519      Debug.debug(Level.WARNING, DebugType.OTHER,
520           "PEMFileKeyManager.getAliases returning null because the " +
521                "requested keyType is '" + keyType + "' but the private " +
522                "key uses an algorithm of '" + privateKey.getAlgorithm() +
523                "'.");
524      return null;
525    }
526
527    if (! hasAnyIssuer(issuers))
528    {
529      Debug.debug(Level.WARNING, DebugType.OTHER,
530           "PEMFileKeyManager.getAliases returning null because " +
531                "certificate chain " + Arrays.toString(certificateChain) +
532                " does not use any of the allowed issuers " +
533                Arrays.toString(issuers));
534
535      return null;
536    }
537
538    return new String[] { alias };
539  }
540
541
542
543  /**
544   * Chooses the alias that should be used for the preferred client certificate
545   * chain with the requested settings.
546   *
547   * @param  keyTypes  The set of allowed key types for the alias to retrieve.
548   *                   It may be {@code null} if any key type may be used.
549   * @param  issuers   The set of allowed issuers for the alias to retrieve.  It
550   *                   may be {@code null} if any issuers should be allowed.
551   * @param  socket    The socket with which the certificate chain will be used.
552   *                   It may be {@code null} if no socket should be taken into
553   *                   consideration.
554   *
555   * @return  The alias that should be used for the preferred client certificate
556   *          chain with the requested settings, or {@code null} if there is no
557   *          applicable alias.
558   */
559  @Override()
560  @Nullable()
561  public String chooseClientAlias(@Nullable final String[] keyTypes,
562                                  @Nullable final Principal[] issuers,
563                                  @Nullable final Socket socket)
564  {
565    return chooseAlias(keyTypes, issuers);
566  }
567
568
569
570  /**
571   * Chooses the alias that should be used for the preferred server certificate
572   * chain with the requested settings.
573   *
574   * @param  keyType  The key type for the alias to retrieve.  It may be
575   *                  {@code null} if any key type may be u sed.
576   * @param  issuers  The set of allowed issuers for the alias to retrieve.  It
577   *                  may be {@code null} if any issuers should be allowed.
578   * @param  socket   The socket with which the certificate chain will be used.
579   *                  It may be {@code null} if no socket should be taken into
580   *                  consideration.
581   *
582   * @return  The alias that should be used for the preferred server certificate
583   *          chain with the requested settings, or {@code null} if there is no
584   *          applicable alias.
585   */
586  @Override()
587  @Nullable()
588  public String chooseServerAlias(@Nullable final String keyType,
589                                  @Nullable final Principal[] issuers,
590                                  @Nullable final Socket socket)
591  {
592    if (keyType == null)
593    {
594      return chooseAlias(null, issuers);
595    }
596    else
597    {
598      return chooseAlias(new String[] { keyType }, issuers);
599    }
600  }
601
602
603
604  /**
605   * Chooses the alias that should be used for the preferred certificate chain
606   * with the requested settings.
607   *
608   * @param  keyTypes  The set of allowed key types for the alias to retrieve.
609   *                   It may be {@code null} if any key type may be used.
610   * @param  issuers   The set of allowed issuers for the alias to retrieve.  It
611   *                   may be {@code null} if any issuers should be allowed.
612   *
613   * @return  The alias that should be used for the preferred certificate chain
614   *          with the requested settings, or {@code null} if there is no
615   *          applicable alias.
616   */
617  @Nullable()
618  public String chooseAlias(@Nullable final String[] keyTypes,
619                            @Nullable final Principal[] issuers)
620  {
621    if ((keyTypes != null) && (keyTypes.length > 0))
622    {
623      boolean keyTypeFound = false;
624      for (final String keyType : keyTypes)
625      {
626        if (hasKeyType(keyType))
627        {
628          keyTypeFound = true;
629          break;
630        }
631      }
632
633      if (! keyTypeFound)
634      {
635        Debug.debug(Level.WARNING, DebugType.OTHER,
636             "PEMFileKeyManager.chooseAlias returning null because " +
637                  "certificate chain " + Arrays.toString(certificateChain) +
638                  " uses a key type of " + privateKey.getAlgorithm() +
639                  ", which does not match any of the allowed key types of " +
640                  Arrays.toString(keyTypes));
641
642        return null;
643      }
644    }
645
646    if (! hasAnyIssuer(issuers))
647    {
648      Debug.debug(Level.WARNING, DebugType.OTHER,
649           "PEMFileKeyManager.chooseAlias returning null because " +
650                "certificate chain " + Arrays.toString(certificateChain) +
651                " does not use any of the allowed issuers " +
652                Arrays.toString(issuers));
653
654      return null;
655    }
656
657    return alias;
658  }
659
660
661
662  /**
663   * Indicates whether the certificate chain has the specified key type.
664   *
665   * @param  keyType  The key type for which to make the determination.  It may
666   *                  be {@code null} if the key type does not matter.
667   *
668   * @return  {@code true} if the certificate chain has the specified key type
669   *          (or if the key type does not matter), or {@code false} if not.
670   */
671  private boolean hasKeyType(@Nullable final String keyType)
672  {
673    return ((keyType == null) ||
674         privateKey.getAlgorithm().equalsIgnoreCase(keyType));
675  }
676
677
678
679  /**
680   * Indicates whether the certificate chain has any of the issuers in the
681   * provided array.
682   *
683   * @param  issuers  The array of acceptable issuers.  It may be
684   *                  {@code null} if the set of issuers does not matter.
685   *
686   * @return  {@code true} if the certificate chain uses one of the accepted
687   *          issuers (or if the issuers do not matter), or {@code false} if
688   *          not.
689   */
690  private boolean hasAnyIssuer(@Nullable final Principal[] issuers)
691  {
692    if ((issuers == null) || (issuers.length == 0))
693    {
694      return true;
695    }
696
697
698    // Check all of the issuer certificates for the chain.
699    for (final Principal acceptableIssuer : issuers)
700    {
701      final String acceptableIssuerString = acceptableIssuer.toString();
702      for (final X509Certificate c : certificateChain)
703      {
704        final Principal certificateIssuer = c.getIssuerDN();
705        final String certificateIssuerString = certificateIssuer.toString();
706        try
707        {
708          if (DN.equals(certificateIssuerString, acceptableIssuerString))
709          {
710            return true;
711          }
712        }
713        catch (final Exception e)
714        {
715          Debug.debugException(e);
716        }
717      }
718    }
719
720
721    // Also check the subject DN for the first certificate in the chain.
722    final Principal endEntitySubject = certificateChain[0].getSubjectDN();
723    final String endEntitySubjectString = endEntitySubject.toString();
724    for (final Principal acceptableIssuer : issuers)
725    {
726      final String acceptableIssuerString = acceptableIssuer.toString();
727      try
728      {
729        if (DN.equals(endEntitySubjectString, acceptableIssuerString))
730        {
731          return true;
732        }
733      }
734      catch (final Exception e)
735      {
736        Debug.debugException(e);
737      }
738    }
739
740
741    return false;
742  }
743
744
745
746  /**
747   * Retrieves the certificate chain with the specified alias.  Note that
748   * because this key manager implementation can only use a single certificate
749   * chain, it will always return the same chain for any alias, even if the
750   * requested alias is {@code null}.
751   *
752   * @param  alias  The alias for the certificate chain to retrieve.
753   *
754   * @return  The certificate chain for this key manager.
755   */
756  @Override()
757  @NotNull()
758  public X509Certificate[] getCertificateChain(@Nullable final String alias)
759  {
760    return Arrays.copyOf(certificateChain, certificateChain.length);
761  }
762
763
764
765  /**
766   * Retrieves the private key for the certificate chain with the specified
767   * alias.  Note that because this key manager implementation can only use a
768   * single certificate chain, it will always return the same private key for
769   * any alias, even if the requested alias is {@code null}.
770   *
771   * @param  alias  The alias for the private key to retrieve.
772   *
773   * @return  The private key for this key manager.
774   */
775  @Override()
776  @NotNull()
777  public PrivateKey getPrivateKey(@Nullable final String alias)
778  {
779    return privateKey;
780  }
781}