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}