001/* 002 * Copyright 2017-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2017-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) 2017-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.cert; 037 038 039 040import java.io.Serializable; 041import java.security.GeneralSecurityException; 042import java.security.KeyFactory; 043import java.security.PrivateKey; 044import java.security.spec.PKCS8EncodedKeySpec; 045import java.util.ArrayList; 046import java.util.Collections; 047import java.util.List; 048 049import com.unboundid.asn1.ASN1BitString; 050import com.unboundid.asn1.ASN1Element; 051import com.unboundid.asn1.ASN1Integer; 052import com.unboundid.asn1.ASN1ObjectIdentifier; 053import com.unboundid.asn1.ASN1OctetString; 054import com.unboundid.asn1.ASN1Sequence; 055import com.unboundid.util.Base64; 056import com.unboundid.util.CryptoHelper; 057import com.unboundid.util.Debug; 058import com.unboundid.util.NotMutable; 059import com.unboundid.util.NotNull; 060import com.unboundid.util.Nullable; 061import com.unboundid.util.OID; 062import com.unboundid.util.StaticUtils; 063import com.unboundid.util.ThreadSafety; 064import com.unboundid.util.ThreadSafetyLevel; 065 066import static com.unboundid.util.ssl.cert.CertMessages.*; 067 068 069 070/** 071 * This class provides support for decoding an X.509 private key encoded in the 072 * PKCS #8 format as defined in 073 * <A HREF="https://www.ietf.org/rfc/rfc5958.txt">RFC 5958</A>. The private key 074 * is encoded using the ASN.1 Distinguished Encoding Rules (DER), which is a 075 * subset of BER, and is supported by the code in the 076 * {@code com.unboundid.asn1} package. The ASN.1 specification is as follows: 077 * <PRE> 078 * OneAsymmetricKey ::= SEQUENCE { 079 * version Version, 080 * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, 081 * privateKey PrivateKey, 082 * attributes [0] Attributes OPTIONAL, 083 * ..., 084 * [[2: publicKey [1] PublicKey OPTIONAL ]], 085 * ... 086 * } 087 * 088 * PrivateKeyInfo ::= OneAsymmetricKey 089 * 090 * -- PrivateKeyInfo is used by [P12]. If any items tagged as version 091 * -- 2 are used, the version must be v2, else the version should be 092 * -- v1. When v1, PrivateKeyInfo is the same as it was in [RFC5208]. 093 * 094 * Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) 095 * 096 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier 097 * { PUBLIC-KEY, 098 * { PrivateKeyAlgorithms } } 099 * 100 * PrivateKey ::= OCTET STRING 101 * -- Content varies based on type of key. The 102 * -- algorithm identifier dictates the format of 103 * -- the key. 104 * 105 * PublicKey ::= BIT STRING 106 * -- Content varies based on type of key. The 107 * -- algorithm identifier dictates the format of 108 * -- the key. 109 * 110 * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } 111 * 112 * OneAsymmetricKeyAttributes ATTRIBUTE ::= { 113 * ... -- For local profiles 114 * } 115 * </PRE> 116 */ 117@NotMutable() 118@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 119public final class PKCS8PrivateKey 120 implements Serializable 121{ 122 /** 123 * The DER type for the attributes element of the private key. 124 */ 125 private static final byte TYPE_ATTRIBUTES = (byte) 0xA0; 126 127 128 129 /** 130 * The DER type for the public key element of the private key. 131 */ 132 private static final byte TYPE_PUBLIC_KEY = (byte) 0x81; 133 134 135 136 /** 137 * The serial version UID for this serializable class. 138 */ 139 private static final long serialVersionUID = -5551171525811450486L; 140 141 142 143 // The corresponding public key, if available. 144 @Nullable private final ASN1BitString publicKey; 145 146 // The ASN.1 element with the encoded set of attributes. 147 @Nullable private final ASN1Element attributesElement; 148 149 // The ASN.1 element with the encoded private key algorithm parameters. 150 @Nullable private final ASN1Element privateKeyAlgorithmParameters; 151 152 // The encoded representation of the private key. 153 @NotNull private final ASN1OctetString encodedPrivateKey; 154 155 // The bytes that comprise the encoded representation of the PKCS #8 private 156 // key. 157 @NotNull private final byte[] pkcs8PrivateKeyBytes; 158 159 // The decoded representation of the private key, if available. 160 @Nullable private final DecodedPrivateKey decodedPrivateKey; 161 162 // The OID for the private key algorithm. 163 @NotNull private final OID privateKeyAlgorithmOID; 164 165 // The PKCS #8 private key version. 166 @NotNull private final PKCS8PrivateKeyVersion version; 167 168 // The private key algorithm name that corresponds with the private key 169 // algorithm OID, if available. 170 @Nullable private final String privateKeyAlgorithmName; 171 172 173 174 /** 175 * Creates a new PKCS #8 private key with the provided information. 176 * 177 * @param version The PKCS #8 private key version. 178 * This must not be {@code null}. 179 * @param privateKeyAlgorithmOID The OID for the private key 180 * algorithm. This must not be 181 * {@code null}. 182 * @param privateKeyAlgorithmParameters The ASN.1 element with the encoded 183 * private key algorithm parameters. 184 * This may be {@code null} if there 185 * are no parameters. 186 * @param encodedPrivateKey The encoded representation of the 187 * private key. This must not be 188 * {@code null}. 189 * @param decodedPrivateKey The decoded representation of the 190 * private key. This may be 191 * {@code null} if the decoded 192 * representation is not available. 193 * @param attributesElement The attributes element to include in 194 * the private key. This may be 195 * {@code null} if no attributes 196 * element should be included. 197 * @param publicKey The public key to include in the 198 * private key. This may be 199 * {@code null} if no public key should 200 * be included. 201 * 202 * @throws CertException If a problem is encountered while creating the 203 * private key. 204 */ 205 PKCS8PrivateKey(@NotNull final PKCS8PrivateKeyVersion version, 206 @NotNull final OID privateKeyAlgorithmOID, 207 @Nullable final ASN1Element privateKeyAlgorithmParameters, 208 @NotNull final ASN1OctetString encodedPrivateKey, 209 @Nullable final DecodedPrivateKey decodedPrivateKey, 210 @Nullable final ASN1Element attributesElement, 211 @Nullable final ASN1BitString publicKey) 212 throws CertException 213 { 214 this.version = version; 215 this.privateKeyAlgorithmOID = privateKeyAlgorithmOID; 216 this.privateKeyAlgorithmParameters = privateKeyAlgorithmParameters; 217 this.encodedPrivateKey = encodedPrivateKey; 218 this.decodedPrivateKey = decodedPrivateKey; 219 this.attributesElement = attributesElement; 220 this.publicKey = publicKey; 221 222 final PublicKeyAlgorithmIdentifier identifier = 223 PublicKeyAlgorithmIdentifier.forOID(privateKeyAlgorithmOID); 224 if (identifier == null) 225 { 226 privateKeyAlgorithmName = null; 227 } 228 else 229 { 230 privateKeyAlgorithmName = identifier.getName(); 231 } 232 233 pkcs8PrivateKeyBytes = encode().encode(); 234 } 235 236 237 238 /** 239 * Decodes the contents of the provided byte array as a PKCS #8 private key. 240 * 241 * @param privateKeyBytes The byte array containing the encoded PKCS #8 242 * private key. 243 * 244 * @throws CertException If the contents of the provided byte array could 245 * not be decoded as a valid PKCS #8 private key. 246 */ 247 public PKCS8PrivateKey(@NotNull final byte[] privateKeyBytes) 248 throws CertException 249 { 250 pkcs8PrivateKeyBytes = privateKeyBytes; 251 252 final ASN1Element[] privateKeyElements; 253 try 254 { 255 privateKeyElements = 256 ASN1Sequence.decodeAsSequence(privateKeyBytes).elements(); 257 } 258 catch (final Exception e) 259 { 260 Debug.debugException(e); 261 throw new CertException( 262 ERR_PRIVATE_KEY_DECODE_NOT_SEQUENCE.get( 263 StaticUtils.getExceptionMessage(e)), 264 e); 265 } 266 267 if (privateKeyElements.length < 3) 268 { 269 throw new CertException( 270 ERR_PRIVATE_KEY_DECODE_NOT_ENOUGH_ELEMENTS.get( 271 privateKeyElements.length)); 272 } 273 274 try 275 { 276 final int versionIntValue = 277 privateKeyElements[0].decodeAsInteger().intValue(); 278 version = PKCS8PrivateKeyVersion.valueOf(versionIntValue); 279 if (version == null) 280 { 281 throw new CertException( 282 ERR_PRIVATE_KEY_DECODE_UNSUPPORTED_VERSION.get(versionIntValue)); 283 } 284 } 285 catch (final CertException e) 286 { 287 Debug.debugException(e); 288 throw e; 289 } 290 catch (final Exception e) 291 { 292 Debug.debugException(e); 293 throw new CertException( 294 ERR_PRIVATE_KEY_DECODE_CANNOT_PARSE_VERSION.get( 295 StaticUtils.getExceptionMessage(e)), 296 e); 297 } 298 299 try 300 { 301 final ASN1Element[] privateKeyAlgorithmElements = 302 privateKeyElements[1].decodeAsSequence().elements(); 303 privateKeyAlgorithmOID = 304 privateKeyAlgorithmElements[0].decodeAsObjectIdentifier().getOID(); 305 if (privateKeyAlgorithmElements.length > 1) 306 { 307 privateKeyAlgorithmParameters = privateKeyAlgorithmElements[1]; 308 } 309 else 310 { 311 privateKeyAlgorithmParameters = null; 312 } 313 314 encodedPrivateKey = privateKeyElements[2].decodeAsOctetString(); 315 } 316 catch (final Exception e) 317 { 318 Debug.debugException(e); 319 throw new CertException( 320 ERR_PRIVATE_KEY_DECODE_CANNOT_PARSE_ALGORITHM.get( 321 StaticUtils.getExceptionMessage(e)), 322 e); 323 } 324 325 final PublicKeyAlgorithmIdentifier privateKeyAlgorithmIdentifier = 326 PublicKeyAlgorithmIdentifier.forOID(privateKeyAlgorithmOID); 327 if (privateKeyAlgorithmIdentifier == null) 328 { 329 privateKeyAlgorithmName = null; 330 decodedPrivateKey = null; 331 } 332 else 333 { 334 privateKeyAlgorithmName = privateKeyAlgorithmIdentifier.getName(); 335 336 DecodedPrivateKey pk = null; 337 switch (privateKeyAlgorithmIdentifier) 338 { 339 case RSA: 340 try 341 { 342 pk = new RSAPrivateKey(encodedPrivateKey); 343 } 344 catch (final Exception e) 345 { 346 Debug.debugException(e); 347 } 348 break; 349 350 case EC: 351 try 352 { 353 pk = new EllipticCurvePrivateKey(encodedPrivateKey); 354 } 355 catch (final Exception e) 356 { 357 Debug.debugException(e); 358 } 359 break; 360 } 361 362 decodedPrivateKey = pk; 363 } 364 365 ASN1BitString pk = null; 366 ASN1Element attrsElement = null; 367 for (int i=3; i < privateKeyElements.length; i++) 368 { 369 final ASN1Element element = privateKeyElements[i]; 370 switch (element.getType()) 371 { 372 case TYPE_ATTRIBUTES: 373 attrsElement = element; 374 break; 375 case TYPE_PUBLIC_KEY: 376 try 377 { 378 pk = ASN1BitString.decodeAsBitString(element); 379 } 380 catch (final Exception e) 381 { 382 Debug.debugException(e); 383 throw new CertException( 384 ERR_PRIVATE_KEY_DECODE_CANNOT_PARSE_PUBLIC_KEY.get( 385 StaticUtils.getExceptionMessage(e)), 386 e); 387 } 388 break; 389 } 390 } 391 392 attributesElement = attrsElement; 393 publicKey = pk; 394 } 395 396 397 398 /** 399 * Wraps the provided RSA private key bytes inside a full PKCS #8 encoded 400 * private key. 401 * 402 * @param rsaPrivateKeyBytes The bytes that comprise just the RSA private 403 * key. 404 * 405 * @return The bytes that comprise a PKCS #8 encoded representation of the 406 * provided RSA private key. 407 * 408 * @throws CertException If a problem is encountered while trying to wrap 409 * the private key. 410 */ 411 @NotNull() 412 static byte[] wrapRSAPrivateKey(@NotNull final byte[] rsaPrivateKeyBytes) 413 throws CertException 414 { 415 try 416 { 417 final ArrayList<ASN1Element> elements = new ArrayList<>(5); 418 elements.add(new ASN1Integer(PKCS8PrivateKeyVersion.V1.getIntValue())); 419 elements.add(new ASN1Sequence(new ASN1ObjectIdentifier( 420 PublicKeyAlgorithmIdentifier.RSA.getOID()))); 421 elements.add(new ASN1OctetString(rsaPrivateKeyBytes)); 422 return new ASN1Sequence(elements).encode(); 423 } 424 catch (final Exception e) 425 { 426 Debug.debugException(e); 427 throw new CertException( 428 ERR_PRIVATE_KEY_WRAP_RSA_KEY_ERROR.get( 429 StaticUtils.getExceptionMessage(e)), 430 e); 431 } 432 } 433 434 435 436 /** 437 * Encodes this PKCS #8 private key to an ASN.1 element. 438 * 439 * @return The encoded PKCS #8 private key. 440 * 441 * @throws CertException If a problem is encountered while trying to encode 442 * the X.509 certificate. 443 */ 444 @NotNull() 445 ASN1Element encode() 446 throws CertException 447 { 448 try 449 { 450 final ArrayList<ASN1Element> elements = new ArrayList<>(5); 451 elements.add(new ASN1Integer(version.getIntValue())); 452 453 if (privateKeyAlgorithmParameters == null) 454 { 455 elements.add(new ASN1Sequence( 456 new ASN1ObjectIdentifier(privateKeyAlgorithmOID))); 457 } 458 else 459 { 460 elements.add(new ASN1Sequence( 461 new ASN1ObjectIdentifier(privateKeyAlgorithmOID), 462 privateKeyAlgorithmParameters)); 463 } 464 465 elements.add(encodedPrivateKey); 466 467 if (attributesElement != null) 468 { 469 elements.add(new ASN1Element(TYPE_ATTRIBUTES, 470 attributesElement.getValue())); 471 } 472 473 if (publicKey != null) 474 { 475 elements.add(new ASN1BitString(TYPE_PUBLIC_KEY, publicKey.getBits())); 476 } 477 478 return new ASN1Sequence(elements); 479 } 480 catch (final Exception e) 481 { 482 Debug.debugException(e); 483 throw new CertException( 484 ERR_PRIVATE_KEY_ENCODE_ERROR.get(toString(), 485 StaticUtils.getExceptionMessage(e)), 486 e); 487 } 488 } 489 490 491 492 /** 493 * Retrieves the bytes that comprise the encoded representation of this 494 * PKCS #8 private key. 495 * 496 * @return The bytes that comprise the encoded representation of this PKCS #8 497 * private key. 498 */ 499 @NotNull() 500 public byte[] getPKCS8PrivateKeyBytes() 501 { 502 return pkcs8PrivateKeyBytes; 503 } 504 505 506 507 /** 508 * Retrieves the private key version. 509 * 510 * @return The private key version. 511 */ 512 @NotNull() 513 public PKCS8PrivateKeyVersion getVersion() 514 { 515 return version; 516 } 517 518 519 520 /** 521 * Retrieves the private key algorithm OID. 522 * 523 * @return The private key algorithm OID. 524 */ 525 @NotNull() 526 public OID getPrivateKeyAlgorithmOID() 527 { 528 return privateKeyAlgorithmOID; 529 } 530 531 532 533 /** 534 * Retrieves the private key algorithm name, if available. 535 * 536 * @return The private key algorithm name, or {@code null} if private key 537 * algorithm OID is not recognized. 538 */ 539 @Nullable() 540 public String getPrivateKeyAlgorithmName() 541 { 542 return privateKeyAlgorithmName; 543 } 544 545 546 547 /** 548 * Retrieves the private key algorithm name, if available, or a string 549 * representation of the OID if the name is not available. 550 * 551 * @return The private key algorithm name if it is available, or a string 552 * representation of the private key algorithm OID if it is not. 553 */ 554 @NotNull() 555 public String getPrivateKeyAlgorithmNameOrOID() 556 { 557 if (privateKeyAlgorithmName == null) 558 { 559 return privateKeyAlgorithmOID.toString(); 560 } 561 else 562 { 563 return privateKeyAlgorithmName; 564 } 565 } 566 567 568 569 /** 570 * Retrieves the encoded private key algorithm parameters, if present. 571 * 572 * @return The encoded private key algorithm parameters, or {@code null} if 573 * there are no private key algorithm parameters. 574 */ 575 @Nullable() 576 public ASN1Element getPrivateKeyAlgorithmParameters() 577 { 578 return privateKeyAlgorithmParameters; 579 } 580 581 582 583 /** 584 * Retrieves the encoded private key data. 585 * 586 * @return The encoded private key data. 587 */ 588 @NotNull() 589 public ASN1OctetString getEncodedPrivateKey() 590 { 591 return encodedPrivateKey; 592 } 593 594 595 596 /** 597 * Retrieves the decoded private key, if available. 598 * 599 * @return The decoded private key, or {@code null} if the decoded key is 600 * not available. 601 */ 602 @Nullable() 603 public DecodedPrivateKey getDecodedPrivateKey() 604 { 605 return decodedPrivateKey; 606 } 607 608 609 610 /** 611 * Retrieves an ASN.1 element containing an encoded set of private key 612 * attributes, if available. 613 * 614 * @return An ASN.1 element containing an encoded set of private key 615 * attributes, or {@code null} if the private key does not have any 616 * attributes. 617 */ 618 @Nullable() 619 public ASN1Element getAttributesElement() 620 { 621 return attributesElement; 622 } 623 624 625 626 /** 627 * Retrieves the public key included in the private key, if available. 628 * 629 * @return The public key included in the private key, or {@code null} if the 630 * private key does not include a public key. 631 */ 632 @Nullable() 633 public ASN1BitString getPublicKey() 634 { 635 return publicKey; 636 } 637 638 639 640 /** 641 * Converts this PKCS #8 private key object to a Java {@code PrivateKey} 642 * object. 643 * 644 * @return The Java {@code PrivateKey} object that corresponds to this 645 * PKCS #8 private key. 646 * 647 * @throws GeneralSecurityException If a problem is encountered while 648 * performing the conversion. 649 */ 650 @NotNull() 651 public PrivateKey toPrivateKey() 652 throws GeneralSecurityException 653 { 654 final KeyFactory keyFactory = 655 CryptoHelper.getKeyFactory(getPrivateKeyAlgorithmNameOrOID()); 656 return keyFactory.generatePrivate( 657 new PKCS8EncodedKeySpec(pkcs8PrivateKeyBytes)); 658 } 659 660 661 662 /** 663 * Retrieves a string representation of the decoded X.509 certificate. 664 * 665 * @return A string representation of the decoded X.509 certificate. 666 */ 667 @Override() 668 @NotNull() 669 public String toString() 670 { 671 final StringBuilder buffer = new StringBuilder(); 672 toString(buffer); 673 return buffer.toString(); 674 } 675 676 677 678 /** 679 * Appends a string representation of the decoded X.509 certificate to the 680 * provided buffer. 681 * 682 * @param buffer The buffer to which the information should be appended. 683 */ 684 public void toString(@NotNull final StringBuilder buffer) 685 { 686 buffer.append("PKCS8PrivateKey(version='"); 687 buffer.append(version.getName()); 688 buffer.append("', privateKeyAlgorithmOID="); 689 buffer.append(privateKeyAlgorithmOID.toString()); 690 buffer.append('\''); 691 692 if (privateKeyAlgorithmName != null) 693 { 694 buffer.append(", privateKeyAlgorithmName='"); 695 buffer.append(privateKeyAlgorithmName); 696 buffer.append('\''); 697 } 698 699 if (decodedPrivateKey == null) 700 { 701 buffer.append(", encodedPrivateKey='"); 702 StaticUtils.toHex(encodedPrivateKey.getValue(), ":", buffer); 703 buffer.append('\''); 704 } 705 else 706 { 707 buffer.append(", decodedPrivateKey="); 708 decodedPrivateKey.toString(buffer); 709 710 711 if (decodedPrivateKey instanceof EllipticCurvePrivateKey) 712 { 713 try 714 { 715 final OID namedCurveOID = privateKeyAlgorithmParameters. 716 decodeAsObjectIdentifier().getOID(); 717 buffer.append(", ellipticCurvePrivateKeyParameters=namedCurve='"); 718 buffer.append(NamedCurve.getNameOrOID(namedCurveOID)); 719 buffer.append('\''); 720 } 721 catch (final Exception e) 722 { 723 Debug.debugException(e); 724 } 725 } 726 } 727 728 buffer.append("')"); 729 } 730 731 732 733 /** 734 * Retrieves a list of the lines that comprise a PEM representation of this 735 * PKCS #8 private key. 736 * 737 * @return A list of the lines that comprise a PEM representation of this 738 * PKCS #8 private key. 739 */ 740 @NotNull() 741 public List<String> toPEM() 742 { 743 final ArrayList<String> lines = new ArrayList<>(10); 744 lines.add(PKCS8PEMFileReader.BEGIN_PRIVATE_KEY_HEADER); 745 746 final String keyBase64 = Base64.encode(pkcs8PrivateKeyBytes); 747 lines.addAll(StaticUtils.wrapLine(keyBase64, 64)); 748 749 lines.add(PKCS8PEMFileReader.END_PRIVATE_KEY_FOOTER); 750 751 return Collections.unmodifiableList(lines); 752 } 753 754 755 756 /** 757 * Retrieves a multi-line string containing a PEM representation of this 758 * PKCS #8 private key. 759 * 760 * @return A multi-line string containing a PEM representation of this 761 * PKCS #8 private key. 762 */ 763 @NotNull() 764 public String toPEMString() 765 { 766 final StringBuilder buffer = new StringBuilder(); 767 buffer.append(PKCS8PEMFileReader.BEGIN_PRIVATE_KEY_HEADER); 768 buffer.append(StaticUtils.EOL); 769 770 final String keyBase64 = Base64.encode(pkcs8PrivateKeyBytes); 771 for (final String line : StaticUtils.wrapLine(keyBase64, 64)) 772 { 773 buffer.append(line); 774 buffer.append(StaticUtils.EOL); 775 } 776 buffer.append(PKCS8PEMFileReader.END_PRIVATE_KEY_FOOTER); 777 buffer.append(StaticUtils.EOL); 778 779 return buffer.toString(); 780 } 781 782 783 784 /** 785 * Retrieves a list of the lines that comprise a PEM representation of this 786 * private key that is encrypted with the provided settings. 787 * 788 * @param encryptionPassword The password to use to generate the 789 * encryption key. It must not be {@code null}. 790 * @param encryptionProperties The properties to use when encrypting the 791 * key. It must not be {@code null}. 792 * 793 * @return A list of the lines that comprise a PEM representation of this 794 * private key that is encrypted with the provided settings. 795 * 796 * @throws CertException If a problem occurs while encrypting the private 797 * key. 798 */ 799 @NotNull() 800 public List<String> toEncryptedPEM( 801 @NotNull final char[] encryptionPassword, 802 @NotNull final PKCS8EncryptionProperties encryptionProperties) 803 throws CertException 804 { 805 final byte[] encryptedPrivateKeyBytes = 806 PKCS8EncryptionHandler.encryptPrivateKey(this, encryptionPassword, 807 encryptionProperties); 808 809 final ArrayList<String> lines = new ArrayList<>(10); 810 lines.add(PKCS8PEMFileReader.BEGIN_ENCRYPTED_PRIVATE_KEY_HEADER); 811 812 final String keyBase64 = Base64.encode(encryptedPrivateKeyBytes); 813 lines.addAll(StaticUtils.wrapLine(keyBase64, 64)); 814 815 lines.add(PKCS8PEMFileReader.END_ENCRYPTED_PRIVATE_KEY_FOOTER); 816 817 return Collections.unmodifiableList(lines); 818 } 819 820 821 822 /** 823 * Retrieves a multi-line string containing a PEM representation of this 824 * private key that is encrypted with the provided settings. 825 * 826 * @param encryptionPassword The password to use to generate the 827 * encryption key. It must not be {@code null}. 828 * @param encryptionProperties The properties to use when encrypting the 829 * key. It must not be {@code null}. 830 * 831 * @return A multi-line string containing a PEM representation of this 832 * private key that is encrypted with the provided settings. 833 * 834 * @throws CertException If a problem occurs while encrypting the private 835 * key. 836 */ 837 @NotNull() 838 public String toEncryptedPEMString( 839 @NotNull final char[] encryptionPassword, 840 @NotNull final PKCS8EncryptionProperties encryptionProperties) 841 throws CertException 842 { 843 final byte[] encryptedPrivateKeyBytes = 844 PKCS8EncryptionHandler.encryptPrivateKey(this, encryptionPassword, 845 encryptionProperties); 846 847 final StringBuilder buffer = new StringBuilder(); 848 buffer.append(PKCS8PEMFileReader.BEGIN_ENCRYPTED_PRIVATE_KEY_HEADER); 849 buffer.append(StaticUtils.EOL); 850 851 final String keyBase64 = Base64.encode(encryptedPrivateKeyBytes); 852 for (final String line : StaticUtils.wrapLine(keyBase64, 64)) 853 { 854 buffer.append(line); 855 buffer.append(StaticUtils.EOL); 856 } 857 buffer.append(PKCS8PEMFileReader.END_ENCRYPTED_PRIVATE_KEY_FOOTER); 858 buffer.append(StaticUtils.EOL); 859 860 return buffer.toString(); 861 } 862}