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}