001/* 002 * Copyright 2017-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2017-2018 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 * Encodes this PKCS #8 private key to an ASN.1 element. 382 * 383 * @return The encoded PKCS #8 private key. 384 * 385 * @throws CertException If a problem is encountered while trying to encode 386 * the X.509 certificate. 387 */ 388 ASN1Element encode() 389 throws CertException 390 { 391 try 392 { 393 final ArrayList<ASN1Element> elements = new ArrayList<>(5); 394 elements.add(new ASN1Integer(version.getIntValue())); 395 396 if (privateKeyAlgorithmParameters == null) 397 { 398 elements.add(new ASN1Sequence( 399 new ASN1ObjectIdentifier(privateKeyAlgorithmOID))); 400 } 401 else 402 { 403 elements.add(new ASN1Sequence( 404 new ASN1ObjectIdentifier(privateKeyAlgorithmOID), 405 privateKeyAlgorithmParameters)); 406 } 407 408 elements.add(encodedPrivateKey); 409 410 if (attributesElement != null) 411 { 412 elements.add(new ASN1Element(TYPE_ATTRIBUTES, 413 attributesElement.getValue())); 414 } 415 416 if (publicKey != null) 417 { 418 elements.add(new ASN1BitString(TYPE_PUBLIC_KEY, publicKey.getBits())); 419 } 420 421 return new ASN1Sequence(elements); 422 } 423 catch (final Exception e) 424 { 425 Debug.debugException(e); 426 throw new CertException( 427 ERR_PRIVATE_KEY_ENCODE_ERROR.get(toString(), 428 StaticUtils.getExceptionMessage(e)), 429 e); 430 } 431 } 432 433 434 435 /** 436 * Retrieves the bytes that comprise the encoded representation of this 437 * PKCS #8 private key. 438 * 439 * @return The bytes that comprise the encoded representation of this PKCS #8 440 * private key. 441 */ 442 public byte[] getPKCS8PrivateKeyBytes() 443 { 444 return pkcs8PrivateKeyBytes; 445 } 446 447 448 449 /** 450 * Retrieves the private key version. 451 * 452 * @return The private key version. 453 */ 454 public PKCS8PrivateKeyVersion getVersion() 455 { 456 return version; 457 } 458 459 460 461 /** 462 * Retrieves the private key algorithm OID. 463 * 464 * @return The private key algorithm OID. 465 */ 466 public OID getPrivateKeyAlgorithmOID() 467 { 468 return privateKeyAlgorithmOID; 469 } 470 471 472 473 /** 474 * Retrieves the private key algorithm name, if available. 475 * 476 * @return The private key algorithm name, or {@code null} if private key 477 * algorithm OID is not recognized. 478 */ 479 public String getPrivateKeyAlgorithmName() 480 { 481 return privateKeyAlgorithmName; 482 } 483 484 485 486 /** 487 * Retrieves the private key algorithm name, if available, or a string 488 * representation of the OID if the name is not available. 489 * 490 * @return The private key algorithm name if it is available, or a string 491 * representation of the private key algorithm OID if it is not. 492 */ 493 public String getPrivateKeyAlgorithmNameOrOID() 494 { 495 if (privateKeyAlgorithmName == null) 496 { 497 return privateKeyAlgorithmOID.toString(); 498 } 499 else 500 { 501 return privateKeyAlgorithmName; 502 } 503 } 504 505 506 507 /** 508 * Retrieves the encoded private key algorithm parameters, if present. 509 * 510 * @return The encoded private key algorithm parameters, or {@code null} if 511 * there are no private key algorithm parameters. 512 */ 513 public ASN1Element getPrivateKeyAlgorithmParameters() 514 { 515 return privateKeyAlgorithmParameters; 516 } 517 518 519 520 /** 521 * Retrieves the encoded private key data. 522 * 523 * @return The encoded private key data. 524 */ 525 public ASN1OctetString getEncodedPrivateKey() 526 { 527 return encodedPrivateKey; 528 } 529 530 531 532 /** 533 * Retrieves the decoded private key, if available. 534 * 535 * @return The decoded private key, or {@code null} if the decoded key is 536 * not available. 537 */ 538 public DecodedPrivateKey getDecodedPrivateKey() 539 { 540 return decodedPrivateKey; 541 } 542 543 544 545 /** 546 * Retrieves an ASN.1 element containing an encoded set of private key 547 * attributes, if available. 548 * 549 * @return An ASN.1 element containing an encoded set of private key 550 * attributes, or {@code null} if the private key does not have any 551 * attributes. 552 */ 553 public ASN1Element getAttributesElement() 554 { 555 return attributesElement; 556 } 557 558 559 560 /** 561 * Retrieves the public key included in the private key, if available. 562 * 563 * @return The public key included in the private key, or {@code null} if the 564 * private key does not include a public key. 565 */ 566 public ASN1BitString getPublicKey() 567 { 568 return publicKey; 569 } 570 571 572 573 /** 574 * Converts this PKCS #8 private key object to a Java {@code PrivateKey} 575 * object. 576 * 577 * @return The Java {@code PrivateKey} object that corresponds to this 578 * PKCS #8 private key. 579 * 580 * @throws GeneralSecurityException If a problem is encountered while 581 * performing the conversion. 582 */ 583 public PrivateKey toPrivateKey() 584 throws GeneralSecurityException 585 { 586 final KeyFactory keyFactory = 587 KeyFactory.getInstance(getPrivateKeyAlgorithmNameOrOID()); 588 return keyFactory.generatePrivate( 589 new PKCS8EncodedKeySpec(pkcs8PrivateKeyBytes)); 590 } 591 592 593 594 /** 595 * Retrieves a string representation of the decoded X.509 certificate. 596 * 597 * @return A string representation of the decoded X.509 certificate. 598 */ 599 @Override() 600 public String toString() 601 { 602 final StringBuilder buffer = new StringBuilder(); 603 toString(buffer); 604 return buffer.toString(); 605 } 606 607 608 609 /** 610 * Appends a string representation of the decoded X.509 certificate to the 611 * provided buffer. 612 * 613 * @param buffer The buffer to which the information should be appended. 614 */ 615 public void toString(final StringBuilder buffer) 616 { 617 buffer.append("PKCS8PrivateKey(version='"); 618 buffer.append(version.getName()); 619 buffer.append("', privateKeyAlgorithmOID="); 620 buffer.append(privateKeyAlgorithmOID.toString()); 621 buffer.append('\''); 622 623 if (privateKeyAlgorithmName != null) 624 { 625 buffer.append(", privateKeyAlgorithmName='"); 626 buffer.append(privateKeyAlgorithmName); 627 buffer.append('\''); 628 } 629 630 if (decodedPrivateKey == null) 631 { 632 buffer.append(", encodedPrivateKey='"); 633 StaticUtils.toHex(encodedPrivateKey.getValue(), ":", buffer); 634 buffer.append('\''); 635 } 636 else 637 { 638 buffer.append(", decodedPrivateKey="); 639 decodedPrivateKey.toString(buffer); 640 641 642 if (decodedPrivateKey instanceof EllipticCurvePrivateKey) 643 { 644 try 645 { 646 final OID namedCurveOID = privateKeyAlgorithmParameters. 647 decodeAsObjectIdentifier().getOID(); 648 buffer.append(", ellipticCurvePrivateKeyParameters=namedCurve='"); 649 buffer.append(NamedCurve.getNameOrOID(namedCurveOID)); 650 buffer.append('\''); 651 } 652 catch (final Exception e) 653 { 654 Debug.debugException(e); 655 } 656 } 657 } 658 659 buffer.append("')"); 660 } 661 662 663 664 /** 665 * Retrieves a list of the lines that comprise a PEM representation of this 666 * certificate signing request. 667 * 668 * @return A list of the lines that comprise a PEM representation of this 669 * certificate signing request. 670 */ 671 public List<String> toPEM() 672 { 673 final ArrayList<String> lines = new ArrayList<>(10); 674 lines.add("-----BEGIN PRIVATE KEY-----"); 675 676 final String keyBase64 = Base64.encode(pkcs8PrivateKeyBytes); 677 lines.addAll(StaticUtils.wrapLine(keyBase64, 64)); 678 679 lines.add("-----END PRIVATE KEY-----"); 680 681 return Collections.unmodifiableList(lines); 682 } 683 684 685 686 /** 687 * Retrieves a multi-line string containing a PEM representation of this 688 * certificate signing request. 689 * 690 * @return A multi-line string containing a PEM representation of this 691 * certificate signing request. 692 */ 693 public String toPEMString() 694 { 695 final StringBuilder buffer = new StringBuilder(); 696 buffer.append("-----BEGIN PRIVATE KEY-----"); 697 buffer.append(StaticUtils.EOL); 698 699 final String keyBase64 = Base64.encode(pkcs8PrivateKeyBytes); 700 for (final String line : StaticUtils.wrapLine(keyBase64, 64)) 701 { 702 buffer.append(line); 703 buffer.append(StaticUtils.EOL); 704 } 705 buffer.append("-----END PRIVATE KEY-----"); 706 buffer.append(StaticUtils.EOL); 707 708 return buffer.toString(); 709 } 710}