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.RSAPrivateKey; 26 import java.security.spec.InvalidKeySpecException; 27 import java.security.spec.RSAPrivateKeySpec; 28 29 public class OpenSSLRSAPrivateKey implements RSAPrivateKey, OpenSSLKeyHolder { 30 private static final long serialVersionUID = 4872170254439578735L; 31 32 protected transient OpenSSLKey key; 33 34 protected transient boolean fetchedParams; 35 36 protected BigInteger modulus; 37 38 protected BigInteger privateExponent; 39 40 OpenSSLRSAPrivateKey(OpenSSLKey key) { 41 this.key = key; 42 } 43 44 OpenSSLRSAPrivateKey(OpenSSLKey key, byte[][] params) { 45 this(key); 46 readParams(params); 47 fetchedParams = true; 48 } 49 50 @Override 51 public OpenSSLKey getOpenSSLKey() { 52 return key; 53 } 54 55 public OpenSSLRSAPrivateKey(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException { 56 this(init(rsaKeySpec)); 57 } 58 59 private static OpenSSLKey init(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException { 60 final BigInteger modulus = rsaKeySpec.getModulus(); 61 final BigInteger privateExponent = rsaKeySpec.getPrivateExponent(); 62 63 if (modulus == null) { 64 throw new InvalidKeySpecException("modulus == null"); 65 } else if (privateExponent == null) { 66 throw new InvalidKeySpecException("privateExponent == null"); 67 } 68 69 try { 70 return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA( 71 modulus.toByteArray(), 72 null, 73 privateExponent.toByteArray(), 74 null, 75 null, 76 null, 77 null, 78 null)); 79 } catch (Exception e) { 80 throw new InvalidKeySpecException(e); 81 } 82 } 83 84 static OpenSSLRSAPrivateKey getInstance(OpenSSLKey key) { 85 byte[][] params = NativeCrypto.get_RSA_private_params(key.getPkeyContext()); 86 if (params[1] != null) { 87 return new OpenSSLRSAPrivateCrtKey(key, params); 88 } 89 return new OpenSSLRSAPrivateKey(key, params); 90 } 91 92 protected static OpenSSLKey wrapPlatformKey(RSAPrivateKey rsaPrivateKey) 93 throws InvalidKeyException { 94 OpenSSLKey wrapper = Platform.wrapRsaKey(rsaPrivateKey); 95 if (wrapper != null) { 96 return wrapper; 97 } 98 return new OpenSSLKey(NativeCrypto.getRSAPrivateKeyWrapper(rsaPrivateKey, rsaPrivateKey 99 .getModulus().toByteArray()), true); 100 } 101 102 static OpenSSLKey getInstance(RSAPrivateKey rsaPrivateKey) throws InvalidKeyException { 103 /** 104 * If the key is not encodable (PKCS11-like key), then wrap it and use 105 * JNI upcalls to satisfy requests. 106 */ 107 if (rsaPrivateKey.getFormat() == null) { 108 return wrapPlatformKey(rsaPrivateKey); 109 } 110 111 final BigInteger modulus = rsaPrivateKey.getModulus(); 112 final BigInteger privateExponent = rsaPrivateKey.getPrivateExponent(); 113 114 if (modulus == null) { 115 throw new InvalidKeyException("modulus == null"); 116 } else if (privateExponent == null) { 117 throw new InvalidKeyException("privateExponent == null"); 118 } 119 120 try { 121 return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA( 122 modulus.toByteArray(), 123 null, 124 privateExponent.toByteArray(), 125 null, 126 null, 127 null, 128 null, 129 null)); 130 } catch (Exception e) { 131 throw new InvalidKeyException(e); 132 } 133 } 134 135 synchronized final void ensureReadParams() { 136 if (fetchedParams) { 137 return; 138 } 139 readParams(NativeCrypto.get_RSA_private_params(key.getPkeyContext())); 140 fetchedParams = true; 141 } 142 143 void readParams(byte[][] params) { 144 if (params[0] == null) { 145 throw new NullPointerException("modulus == null"); 146 } else if (params[2] == null && !key.isEngineBased()) { 147 throw new NullPointerException("privateExponent == null"); 148 } 149 150 modulus = new BigInteger(params[0]); 151 152 // ENGINE-based keys are not guaranteed to have a private exponent. 153 if (params[2] != null) { 154 privateExponent = new BigInteger(params[2]); 155 } 156 } 157 158 @Override 159 public final BigInteger getPrivateExponent() { 160 if (key.isEngineBased()) { 161 throw new UnsupportedOperationException("private exponent cannot be extracted"); 162 } 163 164 ensureReadParams(); 165 return privateExponent; 166 } 167 168 @Override 169 public final BigInteger getModulus() { 170 ensureReadParams(); 171 return modulus; 172 } 173 174 @Override 175 public final byte[] getEncoded() { 176 /* 177 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 178 * the key. Returning {@code null} tells the caller that there's no 179 * encoded format. 180 */ 181 if (key.isEngineBased()) { 182 return null; 183 } 184 185 return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext()); 186 } 187 188 @Override 189 public final String getFormat() { 190 /* 191 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 192 * the key. Returning {@code null} tells the caller that there's no 193 * encoded format. 194 */ 195 if (key.isEngineBased()) { 196 return null; 197 } 198 199 return "PKCS#8"; 200 } 201 202 @Override 203 public final String getAlgorithm() { 204 return "RSA"; 205 } 206 207 @Override 208 public boolean equals(Object o) { 209 if (o == this) { 210 return true; 211 } 212 213 if (o instanceof OpenSSLRSAPrivateKey) { 214 OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o; 215 return key.equals(other.getOpenSSLKey()); 216 } 217 218 if (o instanceof RSAPrivateKey) { 219 ensureReadParams(); 220 RSAPrivateKey other = (RSAPrivateKey) o; 221 222 return modulus.equals(other.getModulus()) 223 && privateExponent.equals(other.getPrivateExponent()); 224 } 225 226 return false; 227 } 228 229 @Override 230 public int hashCode() { 231 ensureReadParams(); 232 int hash = 1; 233 234 hash = hash * 3 + modulus.hashCode(); 235 if (privateExponent != null) { 236 hash = hash * 7 + privateExponent.hashCode(); 237 } 238 239 return hash; 240 } 241 242 @Override 243 public String toString() { 244 final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{"); 245 246 final boolean engineBased = key.isEngineBased(); 247 if (engineBased) { 248 sb.append("key="); 249 sb.append(key); 250 sb.append('}'); 251 } 252 253 ensureReadParams(); 254 sb.append("modulus="); 255 sb.append(modulus.toString(16)); 256 sb.append(','); 257 258 if (!engineBased) { 259 sb.append("privateExponent="); 260 sb.append(privateExponent.toString(16)); 261 sb.append(','); 262 } 263 264 return sb.toString(); 265 } 266 267 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 268 stream.defaultReadObject(); 269 270 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA( 271 modulus.toByteArray(), 272 null, 273 privateExponent.toByteArray(), 274 null, 275 null, 276 null, 277 null, 278 null)); 279 fetchedParams = true; 280 } 281 282 private void writeObject(ObjectOutputStream stream) throws IOException { 283 if (getOpenSSLKey().isEngineBased()) { 284 throw new NotSerializableException("engine-based keys can not be serialized"); 285 } 286 287 ensureReadParams(); 288 stream.defaultWriteObject(); 289 } 290 } 291