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.interfaces.ECPrivateKey; 27 import java.security.spec.ECParameterSpec; 28 import java.security.spec.ECPrivateKeySpec; 29 import java.security.spec.InvalidKeySpecException; 30 import java.util.Arrays; 31 32 public final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder { 33 private static final long serialVersionUID = -4036633595001083922L; 34 35 private static final String ALGORITHM = "EC"; 36 37 protected transient OpenSSLKey key; 38 39 protected transient OpenSSLECGroupContext group; 40 41 public OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) { 42 this.group = group; 43 this.key = key; 44 } 45 46 public OpenSSLECPrivateKey(OpenSSLKey key) { 47 final long origGroup = NativeCrypto.EC_KEY_get0_group(key.getPkeyContext()); 48 this.group = new OpenSSLECGroupContext(NativeCrypto.EC_GROUP_dup(origGroup)); 49 this.key = key; 50 } 51 52 public OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException { 53 try { 54 group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams()); 55 final BigInteger privKey = ecKeySpec.getS(); 56 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getContext(), 0, 57 privKey.toByteArray())); 58 } catch (Exception e) { 59 throw new InvalidKeySpecException(e); 60 } 61 } 62 63 public static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException { 64 OpenSSLECGroupContext group; 65 try { 66 group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams()); 67 } catch (InvalidAlgorithmParameterException e) { 68 throw new InvalidKeyException("Unknown group parameters", e); 69 } 70 return wrapPlatformKey(ecPrivateKey, group); 71 } 72 73 private static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey, OpenSSLECGroupContext group) 74 throws InvalidKeyException { 75 return new OpenSSLKey(NativeCrypto.getECPrivateKeyWrapper(ecPrivateKey, group.getContext())); 76 } 77 78 public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException { 79 try { 80 OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey 81 .getParams()); 82 83 /** 84 * If the key is not encodable (PKCS11-like key), then wrap it and 85 * use JNI upcalls to satisfy requests. 86 */ 87 if (ecPrivateKey.getFormat() == null) { 88 return wrapPlatformKey(ecPrivateKey, group); 89 } 90 91 final BigInteger privKey = ecPrivateKey.getS(); 92 return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getContext(), 0, 93 privKey.toByteArray())); 94 } catch (Exception e) { 95 throw new InvalidKeyException(e); 96 } 97 } 98 99 @Override 100 public String getAlgorithm() { 101 return ALGORITHM; 102 } 103 104 @Override 105 public String getFormat() { 106 /* 107 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 108 * the key. Returning {@code null} tells the caller that there's no 109 * encoded format. 110 */ 111 if (key.isEngineBased()) { 112 return null; 113 } 114 115 return "PKCS#8"; 116 } 117 118 @Override 119 public byte[] getEncoded() { 120 /* 121 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 122 * the key. Returning {@code null} tells the caller that there's no 123 * encoded format. 124 */ 125 if (key.isEngineBased()) { 126 return null; 127 } 128 129 return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext()); 130 } 131 132 @Override 133 public ECParameterSpec getParams() { 134 return group.getECParameterSpec(); 135 } 136 137 @Override 138 public BigInteger getS() { 139 if (key.isEngineBased()) { 140 throw new UnsupportedOperationException("private key value S cannot be extracted"); 141 } 142 143 return getPrivateKey(); 144 } 145 146 private BigInteger getPrivateKey() { 147 return new BigInteger(NativeCrypto.EC_KEY_get_private_key(key.getPkeyContext())); 148 } 149 150 @Override 151 public OpenSSLKey getOpenSSLKey() { 152 return key; 153 } 154 155 @Override 156 public boolean equals(Object o) { 157 if (o == this) { 158 return true; 159 } 160 161 if (o instanceof OpenSSLECPrivateKey) { 162 OpenSSLECPrivateKey other = (OpenSSLECPrivateKey) o; 163 return key.equals(other.key); 164 } 165 166 if (!(o instanceof ECPrivateKey)) { 167 return false; 168 } 169 170 final ECPrivateKey other = (ECPrivateKey) o; 171 if (!getPrivateKey().equals(other.getS())) { 172 return false; 173 } 174 175 final ECParameterSpec spec = getParams(); 176 final ECParameterSpec otherSpec = other.getParams(); 177 178 return spec.getCurve().equals(otherSpec.getCurve()) 179 && spec.getGenerator().equals(otherSpec.getGenerator()) 180 && spec.getOrder().equals(otherSpec.getOrder()) 181 && spec.getCofactor() == otherSpec.getCofactor(); 182 } 183 184 @Override 185 public int hashCode() { 186 return Arrays.hashCode(NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext())); 187 } 188 189 @Override 190 public String toString() { 191 return NativeCrypto.EVP_PKEY_print_private(key.getPkeyContext()); 192 } 193 194 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 195 stream.defaultReadObject(); 196 197 byte[] encoded = (byte[]) stream.readObject(); 198 199 key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded)); 200 201 final long origGroup = NativeCrypto.EC_KEY_get0_group(key.getPkeyContext()); 202 group = new OpenSSLECGroupContext(NativeCrypto.EC_GROUP_dup(origGroup)); 203 } 204 205 private void writeObject(ObjectOutputStream stream) throws IOException { 206 if (key.isEngineBased()) { 207 throw new NotSerializableException("engine-based keys can not be serialized"); 208 } 209 210 stream.defaultWriteObject(); 211 stream.writeObject(getEncoded()); 212 } 213 } 214