OpenSSLCipherRSA.java revision cc73183fa6f5f6f9935307aacfbfc5d93a867a23
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.security.AlgorithmParameters; 20import java.security.InvalidAlgorithmParameterException; 21import java.security.InvalidKeyException; 22import java.security.InvalidParameterException; 23import java.security.Key; 24import java.security.KeyFactory; 25import java.security.NoSuchAlgorithmException; 26import java.security.SecureRandom; 27import java.security.SignatureException; 28import java.security.interfaces.RSAPrivateCrtKey; 29import java.security.interfaces.RSAPrivateKey; 30import java.security.interfaces.RSAPublicKey; 31import java.security.spec.AlgorithmParameterSpec; 32import java.security.spec.InvalidKeySpecException; 33import java.security.spec.PKCS8EncodedKeySpec; 34import java.security.spec.X509EncodedKeySpec; 35import java.util.Arrays; 36import javax.crypto.BadPaddingException; 37import javax.crypto.Cipher; 38import javax.crypto.CipherSpi; 39import javax.crypto.IllegalBlockSizeException; 40import javax.crypto.NoSuchPaddingException; 41import javax.crypto.ShortBufferException; 42import javax.crypto.spec.SecretKeySpec; 43import org.conscrypt.util.EmptyArray; 44 45public abstract class OpenSSLCipherRSA extends CipherSpi { 46 /** 47 * The current OpenSSL key we're operating on. 48 */ 49 private OpenSSLKey key; 50 51 /** 52 * Current key type: private or public. 53 */ 54 private boolean usingPrivateKey; 55 56 /** 57 * Current cipher mode: encrypting or decrypting. 58 */ 59 private boolean encrypting; 60 61 /** 62 * Buffer for operations 63 */ 64 private byte[] buffer; 65 66 /** 67 * Current offset in the buffer. 68 */ 69 private int bufferOffset; 70 71 /** 72 * Flag that indicates an exception should be thrown when the input is too 73 * large during doFinal. 74 */ 75 private boolean inputTooLarge; 76 77 /** 78 * Current padding mode 79 */ 80 private int padding = NativeCrypto.RSA_PKCS1_PADDING; 81 82 protected OpenSSLCipherRSA(int padding) { 83 this.padding = padding; 84 } 85 86 @Override 87 protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 88 final String modeUpper = mode.toUpperCase(); 89 if ("NONE".equals(modeUpper) || "ECB".equals(modeUpper)) { 90 return; 91 } 92 93 throw new NoSuchAlgorithmException("mode not supported: " + mode); 94 } 95 96 @Override 97 protected void engineSetPadding(String padding) throws NoSuchPaddingException { 98 final String paddingUpper = padding.toUpperCase(); 99 if ("PKCS1PADDING".equals(paddingUpper)) { 100 this.padding = NativeCrypto.RSA_PKCS1_PADDING; 101 return; 102 } 103 if ("NOPADDING".equals(paddingUpper)) { 104 this.padding = NativeCrypto.RSA_NO_PADDING; 105 return; 106 } 107 108 throw new NoSuchPaddingException("padding not supported: " + padding); 109 } 110 111 @Override 112 protected int engineGetBlockSize() { 113 if (encrypting) { 114 return paddedBlockSizeBytes(); 115 } 116 return keySizeBytes(); 117 } 118 119 @Override 120 protected int engineGetOutputSize(int inputLen) { 121 if (encrypting) { 122 return keySizeBytes(); 123 } 124 return paddedBlockSizeBytes(); 125 } 126 127 private int paddedBlockSizeBytes() { 128 int paddedBlockSizeBytes = keySizeBytes(); 129 if (padding == NativeCrypto.RSA_PKCS1_PADDING) { 130 paddedBlockSizeBytes--; // for 0 prefix 131 paddedBlockSizeBytes -= 10; // PKCS1 padding header length 132 } 133 return paddedBlockSizeBytes; 134 } 135 136 private int keySizeBytes() { 137 if (key == null) { 138 throw new IllegalStateException("cipher is not initialized"); 139 } 140 return NativeCrypto.RSA_size(this.key.getPkeyContext()); 141 } 142 143 @Override 144 protected byte[] engineGetIV() { 145 return null; 146 } 147 148 @Override 149 protected AlgorithmParameters engineGetParameters() { 150 return null; 151 } 152 153 private void engineInitInternal(int opmode, Key key) throws InvalidKeyException { 154 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { 155 encrypting = true; 156 } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { 157 encrypting = false; 158 } else { 159 throw new InvalidParameterException("Unsupported opmode " + opmode); 160 } 161 162 if (key instanceof OpenSSLRSAPrivateKey) { 163 OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) key; 164 usingPrivateKey = true; 165 this.key = rsaPrivateKey.getOpenSSLKey(); 166 } else if (key instanceof RSAPrivateCrtKey) { 167 RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) key; 168 usingPrivateKey = true; 169 this.key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey); 170 } else if (key instanceof RSAPrivateKey) { 171 RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) key; 172 usingPrivateKey = true; 173 this.key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey); 174 } else if (key instanceof OpenSSLRSAPublicKey) { 175 OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) key; 176 usingPrivateKey = false; 177 this.key = rsaPublicKey.getOpenSSLKey(); 178 } else if (key instanceof RSAPublicKey) { 179 RSAPublicKey rsaPublicKey = (RSAPublicKey) key; 180 usingPrivateKey = false; 181 this.key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey); 182 } else { 183 throw new InvalidKeyException("Need RSA private or public key"); 184 } 185 186 buffer = new byte[NativeCrypto.RSA_size(this.key.getPkeyContext())]; 187 inputTooLarge = false; 188 } 189 190 @Override 191 protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { 192 engineInitInternal(opmode, key); 193 } 194 195 @Override 196 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 197 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 198 if (params != null) { 199 throw new InvalidAlgorithmParameterException("unknown param type: " 200 + params.getClass().getName()); 201 } 202 203 engineInitInternal(opmode, key); 204 } 205 206 @Override 207 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) 208 throws InvalidKeyException, InvalidAlgorithmParameterException { 209 if (params != null) { 210 throw new InvalidAlgorithmParameterException("unknown param type: " 211 + params.getClass().getName()); 212 } 213 214 engineInitInternal(opmode, key); 215 } 216 217 @Override 218 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 219 if (bufferOffset + inputLen > buffer.length) { 220 inputTooLarge = true; 221 return EmptyArray.BYTE; 222 } 223 224 System.arraycopy(input, inputOffset, buffer, bufferOffset, inputLen); 225 bufferOffset += inputLen; 226 return EmptyArray.BYTE; 227 } 228 229 @Override 230 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 231 int outputOffset) throws ShortBufferException { 232 engineUpdate(input, inputOffset, inputLen); 233 return 0; 234 } 235 236 @Override 237 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 238 throws IllegalBlockSizeException, BadPaddingException { 239 if (input != null) { 240 engineUpdate(input, inputOffset, inputLen); 241 } 242 243 if (inputTooLarge) { 244 throw new IllegalBlockSizeException("input must be under " + buffer.length + " bytes"); 245 } 246 247 final byte[] tmpBuf; 248 if (bufferOffset != buffer.length) { 249 if (padding == NativeCrypto.RSA_NO_PADDING) { 250 tmpBuf = new byte[buffer.length]; 251 System.arraycopy(buffer, 0, tmpBuf, buffer.length - bufferOffset, bufferOffset); 252 } else { 253 tmpBuf = Arrays.copyOf(buffer, bufferOffset); 254 } 255 } else { 256 tmpBuf = buffer; 257 } 258 259 byte[] output = new byte[buffer.length]; 260 int resultSize; 261 if (encrypting) { 262 if (usingPrivateKey) { 263 resultSize = NativeCrypto.RSA_private_encrypt(tmpBuf.length, tmpBuf, output, 264 key.getPkeyContext(), padding); 265 } else { 266 resultSize = NativeCrypto.RSA_public_encrypt(tmpBuf.length, tmpBuf, output, 267 key.getPkeyContext(), padding); 268 } 269 } else { 270 try { 271 if (usingPrivateKey) { 272 resultSize = NativeCrypto.RSA_private_decrypt(tmpBuf.length, tmpBuf, output, 273 key.getPkeyContext(), padding); 274 } else { 275 resultSize = NativeCrypto.RSA_public_decrypt(tmpBuf.length, tmpBuf, output, 276 key.getPkeyContext(), padding); 277 } 278 } catch (SignatureException e) { 279 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 280 newE.initCause(e); 281 throw newE; 282 } 283 } 284 if (!encrypting && resultSize != output.length) { 285 output = Arrays.copyOf(output, resultSize); 286 } 287 288 bufferOffset = 0; 289 return output; 290 } 291 292 @Override 293 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 294 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 295 BadPaddingException { 296 byte[] b = engineDoFinal(input, inputOffset, inputLen); 297 298 final int lastOffset = outputOffset + b.length; 299 if (lastOffset > output.length) { 300 throw new ShortBufferException("output buffer is too small " + output.length + " < " 301 + lastOffset); 302 } 303 304 System.arraycopy(b, 0, output, outputOffset, b.length); 305 return b.length; 306 } 307 308 @Override 309 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { 310 try { 311 byte[] encoded = key.getEncoded(); 312 return engineDoFinal(encoded, 0, encoded.length); 313 } catch (BadPaddingException e) { 314 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 315 newE.initCause(e); 316 throw newE; 317 } 318 } 319 320 @Override 321 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, 322 int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { 323 try { 324 byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 325 if (wrappedKeyType == Cipher.PUBLIC_KEY) { 326 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 327 return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); 328 } else if (wrappedKeyType == Cipher.PRIVATE_KEY) { 329 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 330 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 331 } else if (wrappedKeyType == Cipher.SECRET_KEY) { 332 return new SecretKeySpec(encoded, wrappedKeyAlgorithm); 333 } else { 334 throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType); 335 } 336 } catch (IllegalBlockSizeException e) { 337 throw new InvalidKeyException(e); 338 } catch (BadPaddingException e) { 339 throw new InvalidKeyException(e); 340 } catch (InvalidKeySpecException e) { 341 throw new InvalidKeyException(e); 342 } 343 } 344 345 public static class PKCS1 extends OpenSSLCipherRSA { 346 public PKCS1() { 347 super(NativeCrypto.RSA_PKCS1_PADDING); 348 } 349 } 350 351 public static class Raw extends OpenSSLCipherRSA { 352 public Raw() { 353 super(NativeCrypto.RSA_NO_PADDING); 354 } 355 } 356} 357