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}