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.PrivateKey; 27import java.security.PublicKey; 28import java.security.SecureRandom; 29import java.security.SignatureException; 30import java.security.interfaces.RSAPrivateCrtKey; 31import java.security.interfaces.RSAPrivateKey; 32import java.security.interfaces.RSAPublicKey; 33import java.security.spec.AlgorithmParameterSpec; 34import java.security.spec.InvalidKeySpecException; 35import java.security.spec.InvalidParameterSpecException; 36import java.security.spec.MGF1ParameterSpec; 37import java.security.spec.PKCS8EncodedKeySpec; 38import java.security.spec.X509EncodedKeySpec; 39import java.util.Arrays; 40import java.util.Locale; 41import javax.crypto.BadPaddingException; 42import javax.crypto.Cipher; 43import javax.crypto.CipherSpi; 44import javax.crypto.IllegalBlockSizeException; 45import javax.crypto.NoSuchPaddingException; 46import javax.crypto.ShortBufferException; 47import javax.crypto.spec.OAEPParameterSpec; 48import javax.crypto.spec.PSource; 49import javax.crypto.spec.SecretKeySpec; 50 51/** 52 * @hide 53 */ 54@Internal 55abstract class OpenSSLCipherRSA extends CipherSpi { 56 /** 57 * The current OpenSSL key we're operating on. 58 */ 59 OpenSSLKey key; 60 61 /** 62 * Current key type: private or public. 63 */ 64 boolean usingPrivateKey; 65 66 /** 67 * Current cipher mode: encrypting or decrypting. 68 */ 69 boolean encrypting; 70 71 /** 72 * Buffer for operations 73 */ 74 private byte[] buffer; 75 76 /** 77 * Current offset in the buffer. 78 */ 79 private int bufferOffset; 80 81 /** 82 * Flag that indicates an exception should be thrown when the input is too 83 * large during doFinal. 84 */ 85 private boolean inputTooLarge; 86 87 /** 88 * Current padding mode 89 */ 90 int padding = NativeConstants.RSA_PKCS1_PADDING; 91 92 OpenSSLCipherRSA(int padding) { 93 this.padding = padding; 94 } 95 96 @Override 97 protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 98 final String modeUpper = mode.toUpperCase(Locale.ROOT); 99 if ("NONE".equals(modeUpper) || "ECB".equals(modeUpper)) { 100 return; 101 } 102 103 throw new NoSuchAlgorithmException("mode not supported: " + mode); 104 } 105 106 @Override 107 protected void engineSetPadding(String padding) throws NoSuchPaddingException { 108 final String paddingUpper = padding.toUpperCase(Locale.ROOT); 109 if ("PKCS1PADDING".equals(paddingUpper)) { 110 this.padding = NativeConstants.RSA_PKCS1_PADDING; 111 return; 112 } 113 if ("NOPADDING".equals(paddingUpper)) { 114 this.padding = NativeConstants.RSA_NO_PADDING; 115 return; 116 } 117 118 throw new NoSuchPaddingException("padding not supported: " + padding); 119 } 120 121 @Override 122 protected int engineGetBlockSize() { 123 if (encrypting) { 124 return paddedBlockSizeBytes(); 125 } 126 return keySizeBytes(); 127 } 128 129 @Override 130 protected int engineGetOutputSize(int inputLen) { 131 if (encrypting) { 132 return keySizeBytes(); 133 } 134 return paddedBlockSizeBytes(); 135 } 136 137 int paddedBlockSizeBytes() { 138 int paddedBlockSizeBytes = keySizeBytes(); 139 if (padding == NativeConstants.RSA_PKCS1_PADDING) { 140 paddedBlockSizeBytes--; // for 0 prefix 141 paddedBlockSizeBytes -= 10; // PKCS1 padding header length 142 } 143 return paddedBlockSizeBytes; 144 } 145 146 int keySizeBytes() { 147 if (!isInitialized()) { 148 throw new IllegalStateException("cipher is not initialized"); 149 } 150 return NativeCrypto.RSA_size(this.key.getNativeRef()); 151 } 152 153 /** 154 * Returns {@code true} if the cipher has been initialized. 155 */ 156 boolean isInitialized() { 157 return key != null; 158 } 159 160 @Override 161 protected byte[] engineGetIV() { 162 return null; 163 } 164 165 @Override 166 protected AlgorithmParameters engineGetParameters() { 167 return null; 168 } 169 170 void doCryptoInit(AlgorithmParameterSpec spec) 171 throws InvalidAlgorithmParameterException {} 172 173 void engineInitInternal(int opmode, Key key, AlgorithmParameterSpec spec) 174 throws InvalidKeyException, InvalidAlgorithmParameterException { 175 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { 176 encrypting = true; 177 } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { 178 encrypting = false; 179 } else { 180 throw new InvalidParameterException("Unsupported opmode " + opmode); 181 } 182 183 if (key instanceof OpenSSLRSAPrivateKey) { 184 OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) key; 185 usingPrivateKey = true; 186 this.key = rsaPrivateKey.getOpenSSLKey(); 187 } else if (key instanceof RSAPrivateCrtKey) { 188 RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) key; 189 usingPrivateKey = true; 190 this.key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey); 191 } else if (key instanceof RSAPrivateKey) { 192 RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) key; 193 usingPrivateKey = true; 194 this.key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey); 195 } else if (key instanceof OpenSSLRSAPublicKey) { 196 OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) key; 197 usingPrivateKey = false; 198 this.key = rsaPublicKey.getOpenSSLKey(); 199 } else if (key instanceof RSAPublicKey) { 200 RSAPublicKey rsaPublicKey = (RSAPublicKey) key; 201 usingPrivateKey = false; 202 this.key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey); 203 } else { 204 if (null == key) { 205 throw new InvalidKeyException("RSA private or public key is null"); 206 } 207 208 throw new InvalidKeyException("Need RSA private or public key"); 209 } 210 211 buffer = new byte[NativeCrypto.RSA_size(this.key.getNativeRef())]; 212 bufferOffset = 0; 213 inputTooLarge = false; 214 215 doCryptoInit(spec); 216 } 217 218 @Override 219 protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { 220 try { 221 engineInitInternal(opmode, key, null); 222 } catch (InvalidAlgorithmParameterException e) { 223 throw new InvalidKeyException("Algorithm parameters rejected when none supplied", e); 224 } 225 } 226 227 @Override 228 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 229 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 230 if (params != null) { 231 throw new InvalidAlgorithmParameterException("unknown param type: " 232 + params.getClass().getName()); 233 } 234 235 engineInitInternal(opmode, key, params); 236 } 237 238 @Override 239 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) 240 throws InvalidKeyException, InvalidAlgorithmParameterException { 241 if (params != null) { 242 throw new InvalidAlgorithmParameterException("unknown param type: " 243 + params.getClass().getName()); 244 } 245 246 engineInitInternal(opmode, key, null); 247 } 248 249 @Override 250 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 251 if (bufferOffset + inputLen > buffer.length) { 252 inputTooLarge = true; 253 return EmptyArray.BYTE; 254 } 255 256 System.arraycopy(input, inputOffset, buffer, bufferOffset, inputLen); 257 bufferOffset += inputLen; 258 return EmptyArray.BYTE; 259 } 260 261 @Override 262 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 263 int outputOffset) throws ShortBufferException { 264 engineUpdate(input, inputOffset, inputLen); 265 return 0; 266 } 267 268 @Override 269 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 270 throws IllegalBlockSizeException, BadPaddingException { 271 if (input != null) { 272 engineUpdate(input, inputOffset, inputLen); 273 } 274 275 if (inputTooLarge) { 276 throw new IllegalBlockSizeException("input must be under " + buffer.length + " bytes"); 277 } 278 279 final byte[] tmpBuf; 280 if (bufferOffset != buffer.length) { 281 if (padding == NativeConstants.RSA_NO_PADDING) { 282 tmpBuf = new byte[buffer.length]; 283 System.arraycopy(buffer, 0, tmpBuf, buffer.length - bufferOffset, bufferOffset); 284 } else { 285 tmpBuf = Arrays.copyOf(buffer, bufferOffset); 286 } 287 } else { 288 tmpBuf = buffer; 289 } 290 291 byte[] output = new byte[buffer.length]; 292 int resultSize = doCryptoOperation(tmpBuf, output); 293 if (!encrypting && resultSize != output.length) { 294 output = Arrays.copyOf(output, resultSize); 295 } 296 297 bufferOffset = 0; 298 return output; 299 } 300 301 abstract int doCryptoOperation(final byte[] tmpBuf, byte[] output) 302 throws BadPaddingException, IllegalBlockSizeException; 303 304 @Override 305 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 306 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 307 BadPaddingException { 308 byte[] b = engineDoFinal(input, inputOffset, inputLen); 309 310 final int lastOffset = outputOffset + b.length; 311 if (lastOffset > output.length) { 312 throw new ShortBufferException("output buffer is too small " + output.length + " < " 313 + lastOffset); 314 } 315 316 System.arraycopy(b, 0, output, outputOffset, b.length); 317 return b.length; 318 } 319 320 @Override 321 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { 322 try { 323 byte[] encoded = key.getEncoded(); 324 return engineDoFinal(encoded, 0, encoded.length); 325 } catch (BadPaddingException e) { 326 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 327 newE.initCause(e); 328 throw newE; 329 } 330 } 331 332 @Override 333 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, 334 int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { 335 try { 336 byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 337 if (wrappedKeyType == Cipher.PUBLIC_KEY) { 338 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 339 return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); 340 } else if (wrappedKeyType == Cipher.PRIVATE_KEY) { 341 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 342 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 343 } else if (wrappedKeyType == Cipher.SECRET_KEY) { 344 return new SecretKeySpec(encoded, wrappedKeyAlgorithm); 345 } else { 346 throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType); 347 } 348 } catch (IllegalBlockSizeException e) { 349 throw new InvalidKeyException(e); 350 } catch (BadPaddingException e) { 351 throw new InvalidKeyException(e); 352 } catch (InvalidKeySpecException e) { 353 throw new InvalidKeyException(e); 354 } 355 } 356 357 public abstract static class DirectRSA extends OpenSSLCipherRSA { 358 public DirectRSA(int padding) { 359 super(padding); 360 } 361 362 @Override 363 int doCryptoOperation(final byte[] tmpBuf, byte[] output) 364 throws BadPaddingException, IllegalBlockSizeException { 365 int resultSize; 366 if (encrypting) { 367 if (usingPrivateKey) { 368 resultSize = NativeCrypto.RSA_private_encrypt( 369 tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding); 370 } else { 371 resultSize = NativeCrypto.RSA_public_encrypt( 372 tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding); 373 } 374 } else { 375 try { 376 if (usingPrivateKey) { 377 resultSize = NativeCrypto.RSA_private_decrypt( 378 tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding); 379 } else { 380 resultSize = NativeCrypto.RSA_public_decrypt( 381 tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding); 382 } 383 } catch (SignatureException e) { 384 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 385 newE.initCause(e); 386 throw newE; 387 } 388 } 389 return resultSize; 390 } 391 } 392 393 public static final class PKCS1 extends DirectRSA { 394 public PKCS1() { 395 super(NativeConstants.RSA_PKCS1_PADDING); 396 } 397 } 398 399 public static final class Raw extends DirectRSA { 400 public Raw() { 401 super(NativeConstants.RSA_NO_PADDING); 402 } 403 } 404 405 static class OAEP extends OpenSSLCipherRSA { 406 private long oaepMd; 407 private int oaepMdSizeBytes; 408 409 private long mgf1Md; 410 411 private byte[] label; 412 413 private NativeRef.EVP_PKEY_CTX pkeyCtx; 414 415 public OAEP(long defaultMd, int defaultMdSizeBytes) { 416 super(NativeConstants.RSA_PKCS1_OAEP_PADDING); 417 oaepMd = mgf1Md = defaultMd; 418 oaepMdSizeBytes = defaultMdSizeBytes; 419 } 420 421 @Override 422 protected AlgorithmParameters engineGetParameters() { 423 if (!isInitialized()) { 424 return null; 425 } 426 427 try { 428 AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP"); 429 430 final PSource pSrc; 431 if (label == null) { 432 pSrc = PSource.PSpecified.DEFAULT; 433 } else { 434 pSrc = new PSource.PSpecified(label); 435 } 436 437 params.init(new OAEPParameterSpec( 438 EvpMdRef.getJcaDigestAlgorithmStandardNameFromEVP_MD(oaepMd), 439 EvpMdRef.MGF1_ALGORITHM_NAME, 440 new MGF1ParameterSpec( 441 EvpMdRef.getJcaDigestAlgorithmStandardNameFromEVP_MD(mgf1Md)), 442 pSrc)); 443 return params; 444 } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { 445 throw new RuntimeException("No providers of AlgorithmParameters.OAEP available"); 446 } 447 } 448 449 @Override 450 protected void engineSetPadding(String padding) throws NoSuchPaddingException { 451 String paddingUpper = padding.toUpperCase(Locale.US); 452 if (paddingUpper.equals("OAEPPadding")) { 453 this.padding = NativeConstants.RSA_PKCS1_OAEP_PADDING; 454 return; 455 } 456 457 throw new NoSuchPaddingException("Only OAEP padding is supported"); 458 } 459 460 @Override 461 protected void engineInit( 462 int opmode, Key key, AlgorithmParameterSpec spec, SecureRandom random) 463 throws InvalidKeyException, InvalidAlgorithmParameterException { 464 if (spec != null && !(spec instanceof OAEPParameterSpec)) { 465 throw new InvalidAlgorithmParameterException( 466 "Only OAEPParameterSpec accepted in OAEP mode"); 467 } 468 469 engineInitInternal(opmode, key, spec); 470 } 471 472 @Override 473 protected void engineInit( 474 int opmode, Key key, AlgorithmParameters params, SecureRandom random) 475 throws InvalidKeyException, InvalidAlgorithmParameterException { 476 OAEPParameterSpec spec = null; 477 if (params != null) { 478 try { 479 spec = params.getParameterSpec(OAEPParameterSpec.class); 480 } catch (InvalidParameterSpecException e) { 481 throw new InvalidAlgorithmParameterException( 482 "Only OAEP parameters are supported", e); 483 } 484 } 485 486 engineInitInternal(opmode, key, spec); 487 } 488 489 @Override 490 void engineInitInternal(int opmode, Key key, AlgorithmParameterSpec spec) 491 throws InvalidKeyException, InvalidAlgorithmParameterException { 492 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { 493 if (!(key instanceof PublicKey)) { 494 throw new InvalidKeyException("Only public keys may be used to encrypt"); 495 } 496 } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { 497 if (!(key instanceof PrivateKey)) { 498 throw new InvalidKeyException("Only private keys may be used to decrypt"); 499 } 500 } 501 super.engineInitInternal(opmode, key, spec); 502 } 503 504 @Override 505 void doCryptoInit(AlgorithmParameterSpec spec) 506 throws InvalidAlgorithmParameterException { 507 pkeyCtx = new NativeRef.EVP_PKEY_CTX(encrypting 508 ? NativeCrypto.EVP_PKEY_encrypt_init(key.getNativeRef()) 509 : NativeCrypto.EVP_PKEY_decrypt_init(key.getNativeRef())); 510 511 if (spec instanceof OAEPParameterSpec) { 512 readOAEPParameters((OAEPParameterSpec) spec); 513 } 514 515 NativeCrypto.EVP_PKEY_CTX_set_rsa_padding( 516 pkeyCtx.context, NativeConstants.RSA_PKCS1_OAEP_PADDING); 517 NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx.context, oaepMd); 518 NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx.context, mgf1Md); 519 if (label != null && label.length > 0) { 520 NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_label(pkeyCtx.context, label); 521 } 522 } 523 524 @Override 525 int paddedBlockSizeBytes() { 526 int paddedBlockSizeBytes = keySizeBytes(); 527 // Size described in step 2 of decoding algorithm, but extra byte 528 // needed to make sure it's smaller than the RSA key modulus size. 529 // https://tools.ietf.org/html/rfc2437#section-9.1.1.2 530 return paddedBlockSizeBytes - (2 * oaepMdSizeBytes + 2); 531 } 532 533 private void readOAEPParameters(OAEPParameterSpec spec) 534 throws InvalidAlgorithmParameterException { 535 String mgfAlgUpper = spec.getMGFAlgorithm().toUpperCase(Locale.US); 536 AlgorithmParameterSpec mgfSpec = spec.getMGFParameters(); 537 if ((!EvpMdRef.MGF1_ALGORITHM_NAME.equals(mgfAlgUpper) 538 && !EvpMdRef.MGF1_OID.equals(mgfAlgUpper)) 539 || !(mgfSpec instanceof MGF1ParameterSpec)) { 540 throw new InvalidAlgorithmParameterException( 541 "Only MGF1 supported as mask generation function"); 542 } 543 544 MGF1ParameterSpec mgf1spec = (MGF1ParameterSpec) mgfSpec; 545 String oaepAlgUpper = spec.getDigestAlgorithm().toUpperCase(Locale.US); 546 try { 547 oaepMd = EvpMdRef.getEVP_MDByJcaDigestAlgorithmStandardName(oaepAlgUpper); 548 oaepMdSizeBytes = 549 EvpMdRef.getDigestSizeBytesByJcaDigestAlgorithmStandardName(oaepAlgUpper); 550 mgf1Md = EvpMdRef.getEVP_MDByJcaDigestAlgorithmStandardName( 551 mgf1spec.getDigestAlgorithm()); 552 } catch (NoSuchAlgorithmException e) { 553 throw new InvalidAlgorithmParameterException(e); 554 } 555 556 PSource pSource = spec.getPSource(); 557 if (!"PSpecified".equals(pSource.getAlgorithm()) 558 || !(pSource instanceof PSource.PSpecified)) { 559 throw new InvalidAlgorithmParameterException( 560 "Only PSpecified accepted for PSource"); 561 } 562 label = ((PSource.PSpecified) pSource).getValue(); 563 } 564 565 @Override 566 int doCryptoOperation(byte[] tmpBuf, byte[] output) 567 throws BadPaddingException, IllegalBlockSizeException { 568 if (encrypting) { 569 return NativeCrypto.EVP_PKEY_encrypt(pkeyCtx, output, 0, tmpBuf, 0, tmpBuf.length); 570 } else { 571 return NativeCrypto.EVP_PKEY_decrypt(pkeyCtx, output, 0, tmpBuf, 0, tmpBuf.length); 572 } 573 } 574 575 public static final class SHA1 extends OAEP { 576 public SHA1() { 577 super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES); 578 } 579 } 580 581 public static final class SHA224 extends OAEP { 582 public SHA224() { 583 super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES); 584 } 585 } 586 587 public static final class SHA256 extends OAEP { 588 public SHA256() { 589 super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES); 590 } 591 } 592 593 public static final class SHA384 extends OAEP { 594 public SHA384() { 595 super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES); 596 } 597 } 598 599 public static final class SHA512 extends OAEP { 600 public SHA512() { 601 super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES); 602 } 603 } 604 } 605} 606