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.security.AlgorithmParameters; 20 import java.security.InvalidAlgorithmParameterException; 21 import java.security.InvalidKeyException; 22 import java.security.InvalidParameterException; 23 import java.security.Key; 24 import java.security.KeyFactory; 25 import java.security.NoSuchAlgorithmException; 26 import java.security.PrivateKey; 27 import java.security.PublicKey; 28 import java.security.SecureRandom; 29 import java.security.SignatureException; 30 import java.security.interfaces.RSAPrivateCrtKey; 31 import java.security.interfaces.RSAPrivateKey; 32 import java.security.interfaces.RSAPublicKey; 33 import java.security.spec.AlgorithmParameterSpec; 34 import java.security.spec.InvalidKeySpecException; 35 import java.security.spec.InvalidParameterSpecException; 36 import java.security.spec.MGF1ParameterSpec; 37 import java.security.spec.PKCS8EncodedKeySpec; 38 import java.security.spec.X509EncodedKeySpec; 39 import java.util.Arrays; 40 import java.util.Locale; 41 import javax.crypto.BadPaddingException; 42 import javax.crypto.Cipher; 43 import javax.crypto.CipherSpi; 44 import javax.crypto.IllegalBlockSizeException; 45 import javax.crypto.NoSuchPaddingException; 46 import javax.crypto.ShortBufferException; 47 import javax.crypto.spec.OAEPParameterSpec; 48 import javax.crypto.spec.PSource; 49 import javax.crypto.spec.SecretKeySpec; 50 51 /** 52 * @hide 53 */ 54 @Internal 55 abstract 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, InvalidKeyException {} 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 int engineGetKeySize(Key key) throws InvalidKeyException { 220 if (key instanceof OpenSSLRSAPrivateKey) { 221 return ((OpenSSLRSAPrivateKey) key).getModulus().bitLength(); 222 } 223 if (key instanceof RSAPrivateCrtKey) { 224 return ((RSAPrivateCrtKey) key).getModulus().bitLength(); 225 } 226 if (key instanceof RSAPrivateKey) { 227 return ((RSAPrivateKey) key).getModulus().bitLength(); 228 } 229 if (key instanceof OpenSSLRSAPublicKey) { 230 return ((OpenSSLRSAPublicKey) key).getModulus().bitLength(); 231 } 232 if (key instanceof RSAPublicKey) { 233 return ((RSAPublicKey) key).getModulus().bitLength(); 234 } 235 if (null == key) { 236 throw new InvalidKeyException("RSA private or public key is null"); 237 } 238 throw new InvalidKeyException("Need RSA private or public key"); 239 } 240 241 @Override 242 protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { 243 try { 244 engineInitInternal(opmode, key, null); 245 } catch (InvalidAlgorithmParameterException e) { 246 throw new InvalidKeyException("Algorithm parameters rejected when none supplied", e); 247 } 248 } 249 250 @Override 251 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 252 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 253 if (params != null) { 254 throw new InvalidAlgorithmParameterException("unknown param type: " 255 + params.getClass().getName()); 256 } 257 258 engineInitInternal(opmode, key, params); 259 } 260 261 @Override 262 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) 263 throws InvalidKeyException, InvalidAlgorithmParameterException { 264 if (params != null) { 265 throw new InvalidAlgorithmParameterException("unknown param type: " 266 + params.getClass().getName()); 267 } 268 269 engineInitInternal(opmode, key, null); 270 } 271 272 @Override 273 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 274 if (bufferOffset + inputLen > buffer.length) { 275 inputTooLarge = true; 276 return EmptyArray.BYTE; 277 } 278 279 System.arraycopy(input, inputOffset, buffer, bufferOffset, inputLen); 280 bufferOffset += inputLen; 281 return EmptyArray.BYTE; 282 } 283 284 @Override 285 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 286 int outputOffset) throws ShortBufferException { 287 engineUpdate(input, inputOffset, inputLen); 288 return 0; 289 } 290 291 @Override 292 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 293 throws IllegalBlockSizeException, BadPaddingException { 294 if (input != null) { 295 engineUpdate(input, inputOffset, inputLen); 296 } 297 298 if (inputTooLarge) { 299 throw new IllegalBlockSizeException("input must be under " + buffer.length + " bytes"); 300 } 301 302 final byte[] tmpBuf; 303 if (bufferOffset != buffer.length) { 304 if (padding == NativeConstants.RSA_NO_PADDING) { 305 tmpBuf = new byte[buffer.length]; 306 System.arraycopy(buffer, 0, tmpBuf, buffer.length - bufferOffset, bufferOffset); 307 } else { 308 tmpBuf = Arrays.copyOf(buffer, bufferOffset); 309 } 310 } else { 311 tmpBuf = buffer; 312 } 313 314 byte[] output = new byte[buffer.length]; 315 int resultSize = doCryptoOperation(tmpBuf, output); 316 if (!encrypting && resultSize != output.length) { 317 output = Arrays.copyOf(output, resultSize); 318 } 319 320 bufferOffset = 0; 321 return output; 322 } 323 324 abstract int doCryptoOperation(final byte[] tmpBuf, byte[] output) 325 throws BadPaddingException, IllegalBlockSizeException; 326 327 @Override 328 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 329 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 330 BadPaddingException { 331 byte[] b = engineDoFinal(input, inputOffset, inputLen); 332 333 final int lastOffset = outputOffset + b.length; 334 if (lastOffset > output.length) { 335 throw new ShortBufferException("output buffer is too small " + output.length + " < " 336 + lastOffset); 337 } 338 339 System.arraycopy(b, 0, output, outputOffset, b.length); 340 return b.length; 341 } 342 343 @Override 344 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { 345 try { 346 byte[] encoded = key.getEncoded(); 347 return engineDoFinal(encoded, 0, encoded.length); 348 } catch (BadPaddingException e) { 349 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 350 newE.initCause(e); 351 throw newE; 352 } 353 } 354 355 @Override 356 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, 357 int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { 358 try { 359 byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 360 if (wrappedKeyType == Cipher.PUBLIC_KEY) { 361 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 362 return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); 363 } else if (wrappedKeyType == Cipher.PRIVATE_KEY) { 364 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 365 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 366 } else if (wrappedKeyType == Cipher.SECRET_KEY) { 367 return new SecretKeySpec(encoded, wrappedKeyAlgorithm); 368 } else { 369 throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType); 370 } 371 } catch (IllegalBlockSizeException e) { 372 throw new InvalidKeyException(e); 373 } catch (BadPaddingException e) { 374 throw new InvalidKeyException(e); 375 } catch (InvalidKeySpecException e) { 376 throw new InvalidKeyException(e); 377 } 378 } 379 380 public abstract static class DirectRSA extends OpenSSLCipherRSA { 381 public DirectRSA(int padding) { 382 super(padding); 383 } 384 385 @Override 386 int doCryptoOperation(final byte[] tmpBuf, byte[] output) 387 throws BadPaddingException, IllegalBlockSizeException { 388 int resultSize; 389 if (encrypting) { 390 if (usingPrivateKey) { 391 resultSize = NativeCrypto.RSA_private_encrypt( 392 tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding); 393 } else { 394 resultSize = NativeCrypto.RSA_public_encrypt( 395 tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding); 396 } 397 } else { 398 try { 399 if (usingPrivateKey) { 400 resultSize = NativeCrypto.RSA_private_decrypt( 401 tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding); 402 } else { 403 resultSize = NativeCrypto.RSA_public_decrypt( 404 tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding); 405 } 406 } catch (SignatureException e) { 407 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 408 newE.initCause(e); 409 throw newE; 410 } 411 } 412 return resultSize; 413 } 414 } 415 416 public static final class PKCS1 extends DirectRSA { 417 public PKCS1() { 418 super(NativeConstants.RSA_PKCS1_PADDING); 419 } 420 } 421 422 public static final class Raw extends DirectRSA { 423 public Raw() { 424 super(NativeConstants.RSA_NO_PADDING); 425 } 426 } 427 428 static class OAEP extends OpenSSLCipherRSA { 429 private long oaepMd; 430 private int oaepMdSizeBytes; 431 432 private long mgf1Md; 433 434 private byte[] label; 435 436 private NativeRef.EVP_PKEY_CTX pkeyCtx; 437 438 public OAEP(long defaultMd, int defaultMdSizeBytes) { 439 super(NativeConstants.RSA_PKCS1_OAEP_PADDING); 440 oaepMd = mgf1Md = defaultMd; 441 oaepMdSizeBytes = defaultMdSizeBytes; 442 } 443 444 @Override 445 protected AlgorithmParameters engineGetParameters() { 446 if (!isInitialized()) { 447 return null; 448 } 449 450 try { 451 AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP"); 452 453 final PSource pSrc; 454 if (label == null) { 455 pSrc = PSource.PSpecified.DEFAULT; 456 } else { 457 pSrc = new PSource.PSpecified(label); 458 } 459 460 params.init(new OAEPParameterSpec( 461 EvpMdRef.getJcaDigestAlgorithmStandardNameFromEVP_MD(oaepMd), 462 EvpMdRef.MGF1_ALGORITHM_NAME, 463 new MGF1ParameterSpec( 464 EvpMdRef.getJcaDigestAlgorithmStandardNameFromEVP_MD(mgf1Md)), 465 pSrc)); 466 return params; 467 } catch (NoSuchAlgorithmException e) { 468 // We should not get here. 469 throw (Error) new AssertionError("OAEP not supported").initCause(e); 470 } catch (InvalidParameterSpecException e) { 471 throw new RuntimeException("No providers of AlgorithmParameters.OAEP available"); 472 } 473 } 474 475 @Override 476 protected void engineSetPadding(String padding) throws NoSuchPaddingException { 477 String paddingUpper = padding.toUpperCase(Locale.US); 478 if (paddingUpper.equals("OAEPPADDING")) { 479 this.padding = NativeConstants.RSA_PKCS1_OAEP_PADDING; 480 return; 481 } 482 483 throw new NoSuchPaddingException("Only OAEP padding is supported"); 484 } 485 486 @Override 487 protected void engineInit( 488 int opmode, Key key, AlgorithmParameterSpec spec, SecureRandom random) 489 throws InvalidKeyException, InvalidAlgorithmParameterException { 490 if (spec != null && !(spec instanceof OAEPParameterSpec)) { 491 throw new InvalidAlgorithmParameterException( 492 "Only OAEPParameterSpec accepted in OAEP mode"); 493 } 494 495 engineInitInternal(opmode, key, spec); 496 } 497 498 @Override 499 protected void engineInit( 500 int opmode, Key key, AlgorithmParameters params, SecureRandom random) 501 throws InvalidKeyException, InvalidAlgorithmParameterException { 502 OAEPParameterSpec spec = null; 503 if (params != null) { 504 try { 505 spec = params.getParameterSpec(OAEPParameterSpec.class); 506 } catch (InvalidParameterSpecException e) { 507 throw new InvalidAlgorithmParameterException( 508 "Only OAEP parameters are supported", e); 509 } 510 } 511 512 engineInitInternal(opmode, key, spec); 513 } 514 515 @Override 516 void engineInitInternal(int opmode, Key key, AlgorithmParameterSpec spec) 517 throws InvalidKeyException, InvalidAlgorithmParameterException { 518 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { 519 if (!(key instanceof PublicKey)) { 520 throw new InvalidKeyException("Only public keys may be used to encrypt"); 521 } 522 } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { 523 if (!(key instanceof PrivateKey)) { 524 throw new InvalidKeyException("Only private keys may be used to decrypt"); 525 } 526 } 527 super.engineInitInternal(opmode, key, spec); 528 } 529 530 @Override 531 void doCryptoInit(AlgorithmParameterSpec spec) 532 throws InvalidAlgorithmParameterException, InvalidKeyException { 533 pkeyCtx = new NativeRef.EVP_PKEY_CTX(encrypting 534 ? NativeCrypto.EVP_PKEY_encrypt_init(key.getNativeRef()) 535 : NativeCrypto.EVP_PKEY_decrypt_init(key.getNativeRef())); 536 537 if (spec instanceof OAEPParameterSpec) { 538 readOAEPParameters((OAEPParameterSpec) spec); 539 } 540 541 NativeCrypto.EVP_PKEY_CTX_set_rsa_padding( 542 pkeyCtx.context, NativeConstants.RSA_PKCS1_OAEP_PADDING); 543 NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx.context, oaepMd); 544 NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx.context, mgf1Md); 545 if (label != null && label.length > 0) { 546 NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_label(pkeyCtx.context, label); 547 } 548 } 549 550 @Override 551 int paddedBlockSizeBytes() { 552 int paddedBlockSizeBytes = keySizeBytes(); 553 // Size described in step 2 of decoding algorithm, but extra byte 554 // needed to make sure it's smaller than the RSA key modulus size. 555 // https://tools.ietf.org/html/rfc2437#section-9.1.1.2 556 return paddedBlockSizeBytes - (2 * oaepMdSizeBytes + 2); 557 } 558 559 private void readOAEPParameters(OAEPParameterSpec spec) 560 throws InvalidAlgorithmParameterException { 561 String mgfAlgUpper = spec.getMGFAlgorithm().toUpperCase(Locale.US); 562 AlgorithmParameterSpec mgfSpec = spec.getMGFParameters(); 563 if ((!EvpMdRef.MGF1_ALGORITHM_NAME.equals(mgfAlgUpper) 564 && !EvpMdRef.MGF1_OID.equals(mgfAlgUpper)) 565 || !(mgfSpec instanceof MGF1ParameterSpec)) { 566 throw new InvalidAlgorithmParameterException( 567 "Only MGF1 supported as mask generation function"); 568 } 569 570 MGF1ParameterSpec mgf1spec = (MGF1ParameterSpec) mgfSpec; 571 String oaepAlgUpper = spec.getDigestAlgorithm().toUpperCase(Locale.US); 572 try { 573 oaepMd = EvpMdRef.getEVP_MDByJcaDigestAlgorithmStandardName(oaepAlgUpper); 574 oaepMdSizeBytes = 575 EvpMdRef.getDigestSizeBytesByJcaDigestAlgorithmStandardName(oaepAlgUpper); 576 mgf1Md = EvpMdRef.getEVP_MDByJcaDigestAlgorithmStandardName( 577 mgf1spec.getDigestAlgorithm()); 578 } catch (NoSuchAlgorithmException e) { 579 throw new InvalidAlgorithmParameterException(e); 580 } 581 582 PSource pSource = spec.getPSource(); 583 if (!"PSpecified".equals(pSource.getAlgorithm()) 584 || !(pSource instanceof PSource.PSpecified)) { 585 throw new InvalidAlgorithmParameterException( 586 "Only PSpecified accepted for PSource"); 587 } 588 label = ((PSource.PSpecified) pSource).getValue(); 589 } 590 591 @Override 592 int doCryptoOperation(byte[] tmpBuf, byte[] output) 593 throws BadPaddingException, IllegalBlockSizeException { 594 if (encrypting) { 595 return NativeCrypto.EVP_PKEY_encrypt(pkeyCtx, output, 0, tmpBuf, 0, tmpBuf.length); 596 } else { 597 return NativeCrypto.EVP_PKEY_decrypt(pkeyCtx, output, 0, tmpBuf, 0, tmpBuf.length); 598 } 599 } 600 601 public static final class SHA1 extends OAEP { 602 public SHA1() { 603 super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES); 604 } 605 } 606 607 public static final class SHA224 extends OAEP { 608 public SHA224() { 609 super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES); 610 } 611 } 612 613 public static final class SHA256 extends OAEP { 614 public SHA256() { 615 super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES); 616 } 617 } 618 619 public static final class SHA384 extends OAEP { 620 public SHA384() { 621 super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES); 622 } 623 } 624 625 public static final class SHA512 extends OAEP { 626 public SHA512() { 627 super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES); 628 } 629 } 630 } 631 } 632