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