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