1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import java.io.IOException; 20 import java.io.NotSerializableException; 21 import java.io.ObjectInputStream; 22 import java.io.ObjectOutputStream; 23 import java.math.BigInteger; 24 import java.security.InvalidAlgorithmParameterException; 25 import java.security.InvalidKeyException; 26 import java.security.PrivateKey; 27 import java.security.PublicKey; 28 import java.security.interfaces.ECKey; 29 import java.security.interfaces.ECPrivateKey; 30 import java.security.spec.ECParameterSpec; 31 import java.security.spec.ECPrivateKeySpec; 32 import java.security.spec.InvalidKeySpecException; 33 import java.util.Arrays; 34 35 public final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder { 36 private static final long serialVersionUID = -4036633595001083922L; 37 38 private static final String ALGORITHM = "EC"; 39 40 protected transient OpenSSLKey key; 41 42 protected transient OpenSSLECGroupContext group; 43 OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key)44 public OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) { 45 this.group = group; 46 this.key = key; 47 } 48 OpenSSLECPrivateKey(OpenSSLKey key)49 public OpenSSLECPrivateKey(OpenSSLKey key) { 50 this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP( 51 NativeCrypto.EC_KEY_get1_group(key.getNativeRef()))); 52 this.key = key; 53 } 54 OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec)55 public OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException { 56 try { 57 group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams()); 58 final BigInteger privKey = ecKeySpec.getS(); 59 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null, 60 privKey.toByteArray())); 61 } catch (Exception e) { 62 throw new InvalidKeySpecException(e); 63 } 64 } 65 wrapPlatformKey(ECPrivateKey ecPrivateKey)66 public static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException { 67 OpenSSLECGroupContext group; 68 try { 69 group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams()); 70 } catch (InvalidAlgorithmParameterException e) { 71 throw new InvalidKeyException("Unknown group parameters", e); 72 } 73 return wrapPlatformKey(ecPrivateKey, group); 74 } 75 76 /** 77 * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations 78 * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the 79 * provider which accepts the key. 80 */ wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, PublicKey publicKey)81 static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, 82 PublicKey publicKey) throws InvalidKeyException { 83 ECParameterSpec params = null; 84 if (privateKey instanceof ECKey) { 85 params = ((ECKey) privateKey).getParams(); 86 } else if (publicKey instanceof ECKey) { 87 params = ((ECKey) publicKey).getParams(); 88 } 89 if (params == null) { 90 throw new InvalidKeyException("EC parameters not available. Private: " + privateKey 91 + ", public: " + publicKey); 92 } 93 return wrapJCAPrivateKeyForTLSStackOnly(privateKey, params); 94 } 95 96 /** 97 * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations 98 * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the 99 * provider which accepts the key. 100 */ wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, ECParameterSpec params)101 static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, 102 ECParameterSpec params) throws InvalidKeyException { 103 if (params == null) { 104 if (privateKey instanceof ECKey) { 105 params = ((ECKey) privateKey).getParams(); 106 } 107 } 108 if (params == null) { 109 throw new InvalidKeyException("EC parameters not available: " + privateKey); 110 } 111 112 OpenSSLECGroupContext group; 113 try { 114 group = OpenSSLECGroupContext.getInstance(params); 115 } catch (InvalidAlgorithmParameterException e) { 116 throw new InvalidKeyException("Invalid EC parameters: " + params); 117 } 118 119 return new OpenSSLKey( 120 NativeCrypto.getECPrivateKeyWrapper(privateKey, group.getNativeRef()), true); 121 } 122 wrapPlatformKey(ECPrivateKey ecPrivateKey, OpenSSLECGroupContext group)123 private static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey, 124 OpenSSLECGroupContext group) throws InvalidKeyException { 125 return new OpenSSLKey(NativeCrypto.getECPrivateKeyWrapper(ecPrivateKey, 126 group.getNativeRef()), true); 127 } 128 getInstance(ECPrivateKey ecPrivateKey)129 public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException { 130 try { 131 OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey 132 .getParams()); 133 134 /** 135 * If the key is not encodable (PKCS11-like key), then wrap it and 136 * use JNI upcalls to satisfy requests. 137 */ 138 if (ecPrivateKey.getFormat() == null) { 139 return wrapPlatformKey(ecPrivateKey, group); 140 } 141 142 final BigInteger privKey = ecPrivateKey.getS(); 143 return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null, 144 privKey.toByteArray())); 145 } catch (Exception e) { 146 throw new InvalidKeyException(e); 147 } 148 } 149 150 @Override getAlgorithm()151 public String getAlgorithm() { 152 return ALGORITHM; 153 } 154 155 @Override getFormat()156 public String getFormat() { 157 /* 158 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 159 * the key. Returning {@code null} tells the caller that there's no 160 * encoded format. 161 */ 162 if (key.isEngineBased()) { 163 return null; 164 } 165 166 return "PKCS#8"; 167 } 168 169 @Override getEncoded()170 public byte[] getEncoded() { 171 /* 172 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 173 * the key. Returning {@code null} tells the caller that there's no 174 * encoded format. 175 */ 176 if (key.isEngineBased()) { 177 return null; 178 } 179 180 return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef()); 181 } 182 183 @Override getParams()184 public ECParameterSpec getParams() { 185 return group.getECParameterSpec(); 186 } 187 188 @Override getS()189 public BigInteger getS() { 190 if (key.isEngineBased()) { 191 throw new UnsupportedOperationException("private key value S cannot be extracted"); 192 } 193 194 return getPrivateKey(); 195 } 196 getPrivateKey()197 private BigInteger getPrivateKey() { 198 return new BigInteger(NativeCrypto.EC_KEY_get_private_key(key.getNativeRef())); 199 } 200 201 @Override getOpenSSLKey()202 public OpenSSLKey getOpenSSLKey() { 203 return key; 204 } 205 206 @Override equals(Object o)207 public boolean equals(Object o) { 208 if (o == this) { 209 return true; 210 } 211 212 if (o instanceof OpenSSLECPrivateKey) { 213 OpenSSLECPrivateKey other = (OpenSSLECPrivateKey) o; 214 return key.equals(other.key); 215 } 216 217 if (!(o instanceof ECPrivateKey)) { 218 return false; 219 } 220 221 final ECPrivateKey other = (ECPrivateKey) o; 222 if (!getPrivateKey().equals(other.getS())) { 223 return false; 224 } 225 226 final ECParameterSpec spec = getParams(); 227 final ECParameterSpec otherSpec = other.getParams(); 228 229 return spec.getCurve().equals(otherSpec.getCurve()) 230 && spec.getGenerator().equals(otherSpec.getGenerator()) 231 && spec.getOrder().equals(otherSpec.getOrder()) 232 && spec.getCofactor() == otherSpec.getCofactor(); 233 } 234 235 @Override hashCode()236 public int hashCode() { 237 return Arrays.hashCode(NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef())); 238 } 239 240 @Override toString()241 public String toString() { 242 StringBuilder sb = new StringBuilder("OpenSSLECPrivateKey{"); 243 sb.append("params={"); 244 sb.append(NativeCrypto.EVP_PKEY_print_params(key.getNativeRef())); 245 sb.append("}}"); 246 return sb.toString(); 247 } 248 readObject(ObjectInputStream stream)249 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 250 stream.defaultReadObject(); 251 252 byte[] encoded = (byte[]) stream.readObject(); 253 254 key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded)); 255 group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP( 256 NativeCrypto.EC_KEY_get1_group(key.getNativeRef()))); 257 } 258 writeObject(ObjectOutputStream stream)259 private void writeObject(ObjectOutputStream stream) throws IOException { 260 if (key.isEngineBased()) { 261 throw new NotSerializableException("engine-based keys can not be serialized"); 262 } 263 264 stream.defaultWriteObject(); 265 stream.writeObject(getEncoded()); 266 } 267 } 268