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}