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