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}