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