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.util.ArrayList;
026
027import com.unboundid.asn1.ASN1BitString;
028import com.unboundid.asn1.ASN1Element;
029import com.unboundid.asn1.ASN1Integer;
030import com.unboundid.asn1.ASN1ObjectIdentifier;
031import com.unboundid.asn1.ASN1OctetString;
032import com.unboundid.asn1.ASN1Sequence;
033import com.unboundid.util.Debug;
034import com.unboundid.util.NotMutable;
035import com.unboundid.util.OID;
036import com.unboundid.util.StaticUtils;
037import com.unboundid.util.ThreadSafety;
038import com.unboundid.util.ThreadSafetyLevel;
039
040import static com.unboundid.util.ssl.cert.CertMessages.*;
041
042
043
044/**
045 * This class provides a data structure for representing the information
046 * contained in an elliptic curve private key.  As per
047 * <A HREF="https://www.ietf.org/rfc/rfc5915.txt">RFC 5915</A> section 3,
048 * an elliptic curve private key is encoded as follows:
049 * <PRE>
050 *   ECPrivateKey ::= SEQUENCE {
051 *     version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
052 *     privateKey     OCTET STRING,
053 *     parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
054 *     publicKey  [1] BIT STRING OPTIONAL
055 *   }
056 * </PRE>
057 */
058@NotMutable()
059@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
060public final class EllipticCurvePrivateKey
061       extends DecodedPrivateKey
062{
063  /**
064   * The DER type for the parameters element of the private key sequence.
065   */
066  private static final byte TYPE_PARAMETERS = (byte) 0xA0;
067
068
069
070  /**
071   * The DER type for the public key element of the private key sequence.
072   */
073  private static final byte TYPE_PUBLIC_KEY = (byte) 0x81;
074
075
076
077  /**
078   * The serial version UID for this serializable class.
079   */
080  private static final long serialVersionUID = -7102211426269543850L;
081
082
083
084  // The public key that corresponds to the private key.
085  private final ASN1BitString publicKey;
086
087  // The bytes that make up the actual private key.
088  private final byte[] privateKeyBytes;
089
090  // The version number for the private key.
091  private final int version;
092
093  // The OID for the named curve.
094  private final OID namedCurveOID;
095
096
097
098  /**
099   * Creates a new elliptic curve decoded private key from the provided
100   * information.
101   *
102   * @param  version          The version number for the private key.
103   * @param  privateKeyBytes  The bytes that make up the actual private key.
104   *                          This must not be {@code null}.
105   * @param  namedCurveOID    The OID for the named curve.  This may be
106   *                          {@code null} if it is not to be included in the
107   *                          private key.
108   * @param  publicKey        The encoded public key.  This may be {@code null}
109   *                          if it is not to be included in the private key.
110   */
111  EllipticCurvePrivateKey(final int version, final byte[] privateKeyBytes,
112                          final OID namedCurveOID,
113                          final ASN1BitString publicKey)
114  {
115    this.version = version;
116    this.privateKeyBytes = privateKeyBytes;
117    this.namedCurveOID = namedCurveOID;
118    this.publicKey = publicKey;
119  }
120
121
122
123  /**
124   * Creates a new elliptic curve decoded private key from the provided octet
125   * string.
126   *
127   * @param  encodedPrivateKey  The encoded private key to be decoded as an
128   *                            elliptic curve private key.
129   *
130   * @throws  CertException  If the provided private key cannot be decoded as an
131   *                         elliptic curve private key.
132   */
133  EllipticCurvePrivateKey(final ASN1OctetString encodedPrivateKey)
134       throws CertException
135  {
136    try
137    {
138      final ASN1Element[] elements = ASN1Sequence.decodeAsSequence(
139           encodedPrivateKey.getValue()).elements();
140      version = elements[0].decodeAsInteger().intValue();
141
142      if ((version != 1))
143      {
144        throw new CertException(
145             ERR_EC_PRIVATE_KEY_UNSUPPORTED_VERSION.get(version));
146      }
147
148      privateKeyBytes = elements[1].decodeAsOctetString().getValue();
149
150      ASN1BitString pubKey = null;
151      OID curveOID = null;
152      for (int i=2; i < elements.length; i++)
153      {
154        switch (elements[i].getType())
155        {
156          case TYPE_PARAMETERS:
157            curveOID = elements[i].decodeAsObjectIdentifier().getOID();
158            break;
159          case TYPE_PUBLIC_KEY:
160            pubKey = elements[i].decodeAsBitString();
161            break;
162        }
163      }
164
165      namedCurveOID = curveOID;
166      publicKey = pubKey;
167    }
168    catch (final CertException e)
169    {
170      Debug.debugException(e);
171      throw e;
172    }
173    catch (final Exception e)
174    {
175      Debug.debugException(e);
176      throw new CertException(
177           ERR_EC_PRIVATE_KEY_CANNOT_DECODE.get(
178                StaticUtils.getExceptionMessage(e)),
179           e);
180    }
181  }
182
183
184
185  /**
186   * Encodes this elliptic curve private key.
187   *
188   * @return  The encoded representation of this private key.
189   *
190   * @throws  CertException  If a problem is encountered while encoding this
191   *                         private key.
192   */
193  ASN1OctetString encode()
194       throws CertException
195  {
196    try
197    {
198      final ArrayList<ASN1Element> elements = new ArrayList<>(4);
199      elements.add(new ASN1Integer(version));
200      elements.add(new ASN1OctetString(privateKeyBytes));
201
202      if (namedCurveOID != null)
203      {
204        elements.add(new ASN1ObjectIdentifier(TYPE_PARAMETERS, namedCurveOID));
205      }
206
207      if (publicKey != null)
208      {
209        elements.add(new ASN1BitString(TYPE_PUBLIC_KEY, publicKey.getBits()));
210      }
211
212      return new ASN1OctetString(new ASN1Sequence(elements).encode());
213    }
214    catch (final Exception e)
215    {
216      Debug.debugException(e);
217      throw new CertException(
218           ERR_EC_PRIVATE_KEY_CANNOT_ENCODE.get(toString(),
219                StaticUtils.getExceptionMessage(e)),
220           e);
221    }
222  }
223
224
225
226  /**
227   * Retrieves the version for the elliptic curve private key.
228   *
229   * @return  The version for the elliptic curve private key.
230   */
231  public int getVersion()
232  {
233    return version;
234  }
235
236
237
238  /**
239   * Retrieves the bytes that make up the actual elliptic curve private key.
240   *
241   * @return  The bytes that make up the actual elliptic curve private key.
242   */
243  public byte[] getPrivateKeyBytes()
244  {
245    return privateKeyBytes;
246  }
247
248
249
250  /**
251   * Retrieves the OID for the named curve with which this private key is
252   * associated, if available.
253   *
254   * @return  The OID for the named curve with which this private key is
255   *          associated, or {@code null} if it was not included in the encoded
256   *          key.
257   */
258  public OID getNamedCurveOID()
259  {
260    return namedCurveOID;
261  }
262
263
264
265  /**
266   * Retrieves the encoded public key with which this private key is associated,
267   * if available.
268   *
269   * @return  The encoded public key with which this private key is associated,
270   *          or {@code null} if it was not included in the encoded key.
271   */
272  public ASN1BitString getPublicKey()
273  {
274    return publicKey;
275  }
276
277
278
279  /**
280   * {@inheritDoc}
281   */
282  @Override()
283  public void toString(final StringBuilder buffer)
284  {
285    buffer.append("EllipticCurvePrivateKey(version=");
286    buffer.append(version);
287    buffer.append(", privateKeyBytes=");
288    StaticUtils.toHex(privateKeyBytes, ":", buffer);
289
290    if (namedCurveOID != null)
291    {
292      buffer.append(", namedCurveOID='");
293      buffer.append(namedCurveOID.toString());
294      buffer.append('\'');
295
296      final NamedCurve namedCurve = NamedCurve.forOID(namedCurveOID);
297      if (namedCurve != null)
298      {
299        buffer.append(", namedCurveName='");
300        buffer.append(namedCurve.getName());
301        buffer.append('\'');
302      }
303    }
304
305    if (publicKey != null)
306    {
307      try
308      {
309        final byte[] publicKeyBytes = publicKey.getBytes();
310        buffer.append(", publicKeyBytes=");
311        StaticUtils.toHex(publicKeyBytes, ":", buffer);
312      }
313      catch (final Exception e)
314      {
315        Debug.debugException(e);
316        buffer.append(", publicKeyBitString=");
317        publicKey.toString(buffer);
318      }
319    }
320
321    buffer.append(')');
322  }
323}