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