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 static OpenSSLKey getInstance(RSAPrivateKey rsaPrivateKey) throws InvalidKeyException { 93 final BigInteger modulus = rsaPrivateKey.getModulus(); 94 final BigInteger privateExponent = rsaPrivateKey.getPrivateExponent(); 95 96 if (modulus == null) { 97 throw new InvalidKeyException("modulus == null"); 98 } else if (privateExponent == null) { 99 throw new InvalidKeyException("privateExponent == null"); 100 } 101 102 try { 103 return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA( 104 modulus.toByteArray(), 105 null, 106 privateExponent.toByteArray(), 107 null, 108 null, 109 null, 110 null, 111 null)); 112 } catch (Exception e) { 113 throw new InvalidKeyException(e); 114 } 115 } 116 117 synchronized final void ensureReadParams() { 118 if (fetchedParams) { 119 return; 120 } 121 readParams(NativeCrypto.get_RSA_private_params(key.getPkeyContext())); 122 fetchedParams = true; 123 } 124 125 void readParams(byte[][] params) { 126 if (params[0] == null) { 127 throw new NullPointerException("modulus == null"); 128 } else if (params[2] == null && !key.isEngineBased()) { 129 throw new NullPointerException("privateExponent == null"); 130 } 131 132 modulus = new BigInteger(params[0]); 133 134 // ENGINE-based keys are not guaranteed to have a private exponent. 135 if (params[2] != null) { 136 privateExponent = new BigInteger(params[2]); 137 } 138 } 139 140 @Override 141 public final BigInteger getPrivateExponent() { 142 if (key.isEngineBased()) { 143 throw new UnsupportedOperationException("private exponent cannot be extracted"); 144 } 145 146 ensureReadParams(); 147 return privateExponent; 148 } 149 150 @Override 151 public final BigInteger getModulus() { 152 ensureReadParams(); 153 return modulus; 154 } 155 156 @Override 157 public final byte[] getEncoded() { 158 /* 159 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 160 * the key. Returning {@code null} tells the caller that there's no 161 * encoded format. 162 */ 163 if (key.isEngineBased()) { 164 return null; 165 } 166 167 return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext()); 168 } 169 170 public final String getFormat() { 171 /* 172 * If we're using an OpenSSL ENGINE, there's no guarantee we can export 173 * the key. Returning {@code null} tells the caller that there's no 174 * encoded format. 175 */ 176 if (key.isEngineBased()) { 177 return null; 178 } 179 180 return "PKCS#8"; 181 } 182 183 @Override 184 public final String getAlgorithm() { 185 return "RSA"; 186 } 187 188 @Override 189 public boolean equals(Object o) { 190 if (o == this) { 191 return true; 192 } 193 194 if (o instanceof OpenSSLRSAPrivateKey) { 195 OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o; 196 return key.equals(other.getOpenSSLKey()); 197 } 198 199 if (o instanceof RSAPrivateKey) { 200 ensureReadParams(); 201 RSAPrivateKey other = (RSAPrivateKey) o; 202 203 return modulus.equals(other.getModulus()) 204 && privateExponent.equals(other.getPrivateExponent()); 205 } 206 207 return false; 208 } 209 210 @Override 211 public int hashCode() { 212 ensureReadParams(); 213 int hash = 1; 214 215 hash = hash * 3 + modulus.hashCode(); 216 if (privateExponent != null) { 217 hash = hash * 7 + privateExponent.hashCode(); 218 } 219 220 return hash; 221 } 222 223 @Override 224 public String toString() { 225 final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{"); 226 227 final boolean engineBased = key.isEngineBased(); 228 if (engineBased) { 229 sb.append("key="); 230 sb.append(key); 231 sb.append('}'); 232 } 233 234 ensureReadParams(); 235 sb.append("modulus="); 236 sb.append(modulus.toString(16)); 237 sb.append(','); 238 239 if (!engineBased) { 240 sb.append("privateExponent="); 241 sb.append(privateExponent.toString(16)); 242 sb.append(','); 243 } 244 245 return sb.toString(); 246 } 247 248 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 249 stream.defaultReadObject(); 250 251 key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA( 252 modulus.toByteArray(), 253 null, 254 privateExponent.toByteArray(), 255 null, 256 null, 257 null, 258 null, 259 null)); 260 fetchedParams = true; 261 } 262 263 private void writeObject(ObjectOutputStream stream) throws IOException { 264 if (getOpenSSLKey().isEngineBased()) { 265 throw new NotSerializableException("engine-based keys can not be serialized"); 266 } 267 268 ensureReadParams(); 269 stream.defaultWriteObject(); 270 } 271 } 272