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