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