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