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 17package org.conscrypt; 18 19import java.io.IOException; 20import java.io.ObjectInputStream; 21import java.io.ObjectOutputStream; 22import java.math.BigInteger; 23import java.security.InvalidAlgorithmParameterException; 24import java.security.InvalidKeyException; 25import java.security.PrivateKey; 26import java.security.PublicKey; 27import java.security.interfaces.ECKey; 28import java.security.interfaces.ECPrivateKey; 29import java.security.spec.ECParameterSpec; 30import java.security.spec.ECPrivateKeySpec; 31import java.security.spec.InvalidKeySpecException; 32import java.util.Arrays; 33 34/** 35 * An implementation of a {@link PrivateKey} for EC keys based on BoringSSL. 36 * 37 * @hide 38 */ 39@Internal 40public final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder { 41 private static final long serialVersionUID = -4036633595001083922L; 42 43 private static final String ALGORITHM = "EC"; 44 45 protected transient OpenSSLKey key; 46 47 protected transient OpenSSLECGroupContext group; 48 49 public OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) { 50 this.group = group; 51 this.key = key; 52 } 53 54 public OpenSSLECPrivateKey(OpenSSLKey key) { 55 this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP( 56 NativeCrypto.EC_KEY_get1_group(key.getNativeRef()))); 57 this.key = key; 58 } 59 60 public OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException { 61 try { 62 group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams()); 63 final BigInteger privKey = ecKeySpec.getS(); 64 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null, 65 privKey.toByteArray())); 66 } catch (Exception e) { 67 throw new InvalidKeySpecException(e); 68 } 69 } 70 71 public static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException { 72 OpenSSLECGroupContext group; 73 try { 74 group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams()); 75 } catch (InvalidAlgorithmParameterException e) { 76 throw new InvalidKeyException("Unknown group parameters", e); 77 } 78 return wrapPlatformKey(ecPrivateKey, group); 79 } 80 81 /** 82 * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations 83 * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the 84 * provider which accepts the key. 85 */ 86 static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, 87 PublicKey publicKey) throws InvalidKeyException { 88 ECParameterSpec params = null; 89 if (privateKey instanceof ECKey) { 90 params = ((ECKey) privateKey).getParams(); 91 } else if (publicKey instanceof ECKey) { 92 params = ((ECKey) publicKey).getParams(); 93 } 94 if (params == null) { 95 throw new InvalidKeyException("EC parameters not available. Private: " + privateKey 96 + ", public: " + publicKey); 97 } 98 return wrapJCAPrivateKeyForTLSStackOnly(privateKey, params); 99 } 100 101 /** 102 * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations 103 * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the 104 * provider which accepts the key. 105 */ 106 static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, 107 ECParameterSpec params) throws InvalidKeyException { 108 if (params == null) { 109 if (privateKey instanceof ECKey) { 110 params = ((ECKey) privateKey).getParams(); 111 } 112 } 113 if (params == null) { 114 throw new InvalidKeyException("EC parameters not available: " + privateKey); 115 } 116 117 OpenSSLECGroupContext group; 118 try { 119 group = OpenSSLECGroupContext.getInstance(params); 120 } catch (InvalidAlgorithmParameterException e) { 121 throw new InvalidKeyException("Invalid EC parameters: " + params); 122 } 123 124 return new OpenSSLKey( 125 NativeCrypto.getECPrivateKeyWrapper(privateKey, group.getNativeRef()), true); 126 } 127 128 private static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey, 129 OpenSSLECGroupContext group) throws InvalidKeyException { 130 return new OpenSSLKey(NativeCrypto.getECPrivateKeyWrapper(ecPrivateKey, 131 group.getNativeRef()), true); 132 } 133 134 public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException { 135 try { 136 OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey 137 .getParams()); 138 139 /** 140 * If the key is not encodable (PKCS11-like key), then wrap it and 141 * use JNI upcalls to satisfy requests. 142 */ 143 if (ecPrivateKey.getFormat() == null) { 144 return wrapPlatformKey(ecPrivateKey, group); 145 } 146 147 final BigInteger privKey = ecPrivateKey.getS(); 148 return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null, 149 privKey.toByteArray())); 150 } catch (Exception e) { 151 throw new InvalidKeyException(e); 152 } 153 } 154 155 @Override 156 public String getAlgorithm() { 157 return ALGORITHM; 158 } 159 160 @Override 161 public String getFormat() { 162 return "PKCS#8"; 163 } 164 165 @Override 166 public byte[] getEncoded() { 167 return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef()); 168 } 169 170 @Override 171 public ECParameterSpec getParams() { 172 return group.getECParameterSpec(); 173 } 174 175 @Override 176 public BigInteger getS() { 177 return getPrivateKey(); 178 } 179 180 private BigInteger getPrivateKey() { 181 return new BigInteger(NativeCrypto.EC_KEY_get_private_key(key.getNativeRef())); 182 } 183 184 @Override 185 public OpenSSLKey getOpenSSLKey() { 186 return key; 187 } 188 189 @Override 190 public boolean equals(Object o) { 191 if (o == this) { 192 return true; 193 } 194 195 if (o instanceof OpenSSLECPrivateKey) { 196 OpenSSLECPrivateKey other = (OpenSSLECPrivateKey) o; 197 return key.equals(other.key); 198 } 199 200 if (!(o instanceof ECPrivateKey)) { 201 return false; 202 } 203 204 final ECPrivateKey other = (ECPrivateKey) o; 205 if (!getPrivateKey().equals(other.getS())) { 206 return false; 207 } 208 209 final ECParameterSpec spec = getParams(); 210 final ECParameterSpec otherSpec = other.getParams(); 211 212 return spec.getCurve().equals(otherSpec.getCurve()) 213 && spec.getGenerator().equals(otherSpec.getGenerator()) 214 && spec.getOrder().equals(otherSpec.getOrder()) 215 && spec.getCofactor() == otherSpec.getCofactor(); 216 } 217 218 @Override 219 public int hashCode() { 220 return Arrays.hashCode(NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef())); 221 } 222 223 @Override 224 public String toString() { 225 StringBuilder sb = new StringBuilder("OpenSSLECPrivateKey{"); 226 sb.append("params={"); 227 sb.append(NativeCrypto.EVP_PKEY_print_params(key.getNativeRef())); 228 sb.append("}}"); 229 return sb.toString(); 230 } 231 232 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 233 stream.defaultReadObject(); 234 235 byte[] encoded = (byte[]) stream.readObject(); 236 237 key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded)); 238 group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP( 239 NativeCrypto.EC_KEY_get1_group(key.getNativeRef()))); 240 } 241 242 private void writeObject(ObjectOutputStream stream) throws IOException { 243 stream.defaultWriteObject(); 244 stream.writeObject(getEncoded()); 245 } 246} 247