001/* 002 * Copyright 2017-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2017-2019 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}