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.io.IOException; 20import java.security.AlgorithmParameters; 21import java.security.InvalidAlgorithmParameterException; 22import java.security.InvalidKeyException; 23import java.security.InvalidParameterException; 24import java.security.Key; 25import java.security.KeyFactory; 26import java.security.NoSuchAlgorithmException; 27import java.security.SecureRandom; 28import java.security.spec.AlgorithmParameterSpec; 29import java.security.spec.InvalidKeySpecException; 30import java.security.spec.InvalidParameterSpecException; 31import java.security.spec.PKCS8EncodedKeySpec; 32import java.security.spec.X509EncodedKeySpec; 33import java.util.Arrays; 34import java.util.Locale; 35import javax.crypto.BadPaddingException; 36import javax.crypto.Cipher; 37import javax.crypto.CipherSpi; 38import javax.crypto.IllegalBlockSizeException; 39import javax.crypto.NoSuchPaddingException; 40import javax.crypto.SecretKey; 41import javax.crypto.ShortBufferException; 42import javax.crypto.spec.IvParameterSpec; 43import javax.crypto.spec.SecretKeySpec; 44import org.conscrypt.util.EmptyArray; 45 46public abstract class OpenSSLCipher extends CipherSpi { 47 48 /** 49 * Modes that a block cipher may support. 50 */ 51 protected static enum Mode { 52 CBC, 53 CFB, CFB1, CFB8, CFB128, 54 CTR, 55 CTS, 56 ECB, 57 OFB, OFB64, OFB128, 58 PCBC, 59 } 60 61 /** 62 * Paddings that a block cipher may support. 63 */ 64 protected static enum Padding { 65 NOPADDING, 66 PKCS5PADDING, 67 ISO10126PADDING, 68 } 69 70 /** 71 * Native pointer for the OpenSSL EVP_CIPHER context. 72 */ 73 private OpenSSLCipherContext cipherCtx = new OpenSSLCipherContext( 74 NativeCrypto.EVP_CIPHER_CTX_new()); 75 76 /** 77 * The current cipher mode. 78 */ 79 private Mode mode = Mode.ECB; 80 81 /** 82 * The current cipher padding. 83 */ 84 private Padding padding = Padding.PKCS5PADDING; 85 86 /** 87 * The Initial Vector (IV) used for the current cipher. 88 */ 89 private byte[] iv; 90 91 /** 92 * Current cipher mode: encrypting or decrypting. 93 */ 94 private boolean encrypting; 95 96 /** 97 * The block size of the current cipher. 98 */ 99 private int blockSize; 100 101 /** 102 * The block size of the current mode. 103 */ 104 private int modeBlockSize; 105 106 /** 107 * Whether the cipher has processed any data yet. OpenSSL doesn't like 108 * calling "doFinal()" in decryption mode without processing any updates. 109 */ 110 private boolean calledUpdate; 111 112 protected OpenSSLCipher() { 113 } 114 115 protected OpenSSLCipher(Mode mode, Padding padding) { 116 this.mode = mode; 117 this.padding = padding; 118 blockSize = getCipherBlockSize(); 119 } 120 121 /** 122 * Returns the standard name for the particular algorithm. 123 */ 124 protected abstract String getBaseCipherName(); 125 126 /** 127 * Returns the OpenSSL cipher name for the particular {@code keySize} and 128 * cipher {@code mode}. 129 */ 130 protected abstract String getCipherName(int keySize, Mode mode); 131 132 /** 133 * Checks whether the cipher supports this particular {@code keySize} (in 134 * bytes) and throws {@code InvalidKeyException} if it doesn't. 135 */ 136 protected abstract void checkSupportedKeySize(int keySize) throws InvalidKeyException; 137 138 /** 139 * Checks whether the cipher supports this particular cipher {@code mode} 140 * and throws {@code NoSuchAlgorithmException} if it doesn't. 141 */ 142 protected abstract void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException; 143 144 /** 145 * Checks whether the cipher supports this particular cipher {@code padding} 146 * and throws {@code NoSuchPaddingException} if it doesn't. 147 */ 148 protected abstract void checkSupportedPadding(Padding padding) throws NoSuchPaddingException; 149 150 protected abstract int getCipherBlockSize(); 151 152 protected boolean supportsVariableSizeKey() { 153 return false; 154 } 155 156 @Override 157 protected void engineSetMode(String modeStr) throws NoSuchAlgorithmException { 158 final Mode mode; 159 try { 160 mode = Mode.valueOf(modeStr.toUpperCase(Locale.US)); 161 } catch (IllegalArgumentException e) { 162 NoSuchAlgorithmException newE = new NoSuchAlgorithmException("No such mode: " 163 + modeStr); 164 newE.initCause(e); 165 throw newE; 166 } 167 checkSupportedMode(mode); 168 this.mode = mode; 169 } 170 171 @Override 172 protected void engineSetPadding(String paddingStr) throws NoSuchPaddingException { 173 final String paddingStrUpper = paddingStr.toUpperCase(Locale.US); 174 final Padding padding; 175 try { 176 padding = Padding.valueOf(paddingStrUpper); 177 } catch (IllegalArgumentException e) { 178 NoSuchPaddingException newE = new NoSuchPaddingException("No such padding: " 179 + paddingStr); 180 newE.initCause(e); 181 throw newE; 182 } 183 checkSupportedPadding(padding); 184 this.padding = padding; 185 } 186 187 @Override 188 protected int engineGetBlockSize() { 189 return blockSize; 190 } 191 192 /** 193 * The size of output if {@code doFinal()} is called with this 194 * {@code inputLen}. If padding is enabled and the size of the input puts it 195 * right at the block size, it will add another block for the padding. 196 */ 197 private int getOutputSize(int inputLen) { 198 if (modeBlockSize == 1) { 199 return inputLen; 200 } else { 201 final int buffered = NativeCrypto.get_EVP_CIPHER_CTX_buf_len(cipherCtx.getContext()); 202 if (padding == Padding.NOPADDING) { 203 return buffered + inputLen; 204 } else { 205 final int totalLen = inputLen + buffered + modeBlockSize; 206 return totalLen - (totalLen % modeBlockSize); 207 } 208 } 209 } 210 211 @Override 212 protected int engineGetOutputSize(int inputLen) { 213 return getOutputSize(inputLen); 214 } 215 216 @Override 217 protected byte[] engineGetIV() { 218 return iv; 219 } 220 221 @Override 222 protected AlgorithmParameters engineGetParameters() { 223 if (iv != null && iv.length > 0) { 224 try { 225 AlgorithmParameters params = AlgorithmParameters.getInstance(getBaseCipherName()); 226 params.init(iv); 227 return params; 228 } catch (NoSuchAlgorithmException e) { 229 return null; 230 } catch (IOException e) { 231 return null; 232 } 233 } 234 return null; 235 } 236 237 private void engineInitInternal(int opmode, Key key, byte[] iv, SecureRandom random) 238 throws InvalidKeyException, InvalidAlgorithmParameterException { 239 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { 240 encrypting = true; 241 } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { 242 encrypting = false; 243 } else { 244 throw new InvalidParameterException("Unsupported opmode " + opmode); 245 } 246 247 if (!(key instanceof SecretKey)) { 248 throw new InvalidKeyException("Only SecretKey is supported"); 249 } 250 251 final byte[] encodedKey = key.getEncoded(); 252 if (encodedKey == null) { 253 throw new InvalidKeyException("key.getEncoded() == null"); 254 } 255 256 checkSupportedKeySize(encodedKey.length); 257 258 final long cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName(encodedKey.length, 259 mode)); 260 if (cipherType == 0) { 261 throw new InvalidAlgorithmParameterException("Cannot find name for key length = " 262 + (encodedKey.length * 8) + " and mode = " + mode); 263 } 264 265 final int ivLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType); 266 if (iv == null && ivLength != 0) { 267 iv = new byte[ivLength]; 268 if (encrypting) { 269 if (random == null) { 270 random = new SecureRandom(); 271 } 272 random.nextBytes(iv); 273 } 274 } else if (iv != null && iv.length != ivLength) { 275 throw new InvalidAlgorithmParameterException("expected IV length of " + ivLength); 276 } 277 278 this.iv = iv; 279 280 if (supportsVariableSizeKey()) { 281 NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), cipherType, null, null, 282 encrypting); 283 NativeCrypto.EVP_CIPHER_CTX_set_key_length(cipherCtx.getContext(), encodedKey.length); 284 NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), 0, encodedKey, iv, encrypting); 285 } else { 286 NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), cipherType, encodedKey, iv, 287 encrypting); 288 } 289 290 // OpenSSL only supports PKCS5 Padding. 291 NativeCrypto.EVP_CIPHER_CTX_set_padding(cipherCtx.getContext(), 292 padding == Padding.PKCS5PADDING); 293 modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx.getContext()); 294 calledUpdate = false; 295 } 296 297 @Override 298 protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { 299 try { 300 engineInitInternal(opmode, key, null, random); 301 } catch (InvalidAlgorithmParameterException e) { 302 throw new RuntimeException(e); 303 } 304 } 305 306 @Override 307 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 308 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 309 final byte[] iv; 310 if (params instanceof IvParameterSpec) { 311 IvParameterSpec ivParams = (IvParameterSpec) params; 312 iv = ivParams.getIV(); 313 } else { 314 iv = null; 315 } 316 317 engineInitInternal(opmode, key, iv, random); 318 } 319 320 @Override 321 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) 322 throws InvalidKeyException, InvalidAlgorithmParameterException { 323 final AlgorithmParameterSpec spec; 324 try { 325 spec = params.getParameterSpec(IvParameterSpec.class); 326 } catch (InvalidParameterSpecException e) { 327 throw new InvalidAlgorithmParameterException(e); 328 } 329 330 engineInit(opmode, key, spec, random); 331 } 332 333 private final int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output, 334 int outputOffset, int maximumLen) throws ShortBufferException { 335 final int intialOutputOffset = outputOffset; 336 337 final int bytesLeft = output.length - outputOffset; 338 if (bytesLeft < maximumLen) { 339 throw new ShortBufferException("output buffer too small during update: " + bytesLeft 340 + " < " + maximumLen); 341 } 342 343 outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx.getContext(), output, outputOffset, 344 input, inputOffset, inputLen); 345 346 calledUpdate = true; 347 348 return outputOffset - intialOutputOffset; 349 } 350 351 @Override 352 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 353 final int maximumLen = getOutputSize(inputLen); 354 355 /* See how large our output buffer would need to be. */ 356 final byte[] output; 357 if (maximumLen > 0) { 358 output = new byte[maximumLen]; 359 } else { 360 output = EmptyArray.BYTE; 361 } 362 363 final int bytesWritten; 364 try { 365 bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen); 366 } catch (ShortBufferException e) { 367 /* This shouldn't happen. */ 368 throw new RuntimeException("calculated buffer size was wrong: " + maximumLen); 369 } 370 371 if (output.length == bytesWritten) { 372 return output; 373 } else if (bytesWritten == 0) { 374 return EmptyArray.BYTE; 375 } else { 376 return Arrays.copyOfRange(output, 0, bytesWritten); 377 } 378 } 379 380 @Override 381 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 382 int outputOffset) throws ShortBufferException { 383 final int maximumLen = getOutputSize(inputLen); 384 return updateInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen); 385 } 386 387 /** 388 * Reset this Cipher instance state to process a new chunk of data. 389 */ 390 private void reset() { 391 NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), 0, null, null, encrypting); 392 calledUpdate = false; 393 } 394 395 private int doFinalInternal(byte[] input, int inputOffset, int inputLen, byte[] output, 396 int outputOffset, int maximumLen) throws IllegalBlockSizeException, 397 BadPaddingException, ShortBufferException { 398 /* Remember this so we can tell how many characters were written. */ 399 final int initialOutputOffset = outputOffset; 400 401 if (inputLen > 0) { 402 final int updateBytesWritten = updateInternal(input, inputOffset, inputLen, output, 403 outputOffset, maximumLen); 404 outputOffset += updateBytesWritten; 405 maximumLen -= updateBytesWritten; 406 } 407 408 /* 409 * If we're decrypting and haven't had any input, we should return null. 410 * Otherwise OpenSSL will complain if we call final. 411 */ 412 if (!encrypting && !calledUpdate) { 413 return 0; 414 } 415 416 /* Allow OpenSSL to pad if necessary and clean up state. */ 417 final int bytesLeft = output.length - outputOffset; 418 final int writtenBytes; 419 if (bytesLeft >= maximumLen) { 420 writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx.getContext(), output, 421 outputOffset); 422 } else { 423 final byte[] lastBlock = new byte[maximumLen]; 424 writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx.getContext(), lastBlock, 0); 425 if (writtenBytes > bytesLeft) { 426 throw new ShortBufferException("buffer is too short: " + writtenBytes + " > " 427 + bytesLeft); 428 } else if (writtenBytes > 0) { 429 System.arraycopy(lastBlock, 0, output, outputOffset, writtenBytes); 430 } 431 } 432 outputOffset += writtenBytes; 433 434 reset(); 435 436 return outputOffset - initialOutputOffset; 437 } 438 439 @Override 440 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 441 throws IllegalBlockSizeException, BadPaddingException { 442 /* 443 * Other implementations return null if we've never called update() 444 * while decrypting. 445 */ 446 if (!encrypting && !calledUpdate && inputLen == 0) { 447 reset(); 448 return null; 449 } 450 451 final int maximumLen = getOutputSize(inputLen); 452 /* Assume that we'll output exactly on a byte boundary. */ 453 final byte[] output = new byte[maximumLen]; 454 final int bytesWritten; 455 try { 456 bytesWritten = doFinalInternal(input, inputOffset, inputLen, output, 0, maximumLen); 457 } catch (ShortBufferException e) { 458 /* This should not happen since we sized our own buffer. */ 459 throw new RuntimeException("our calculated buffer was too small", e); 460 } 461 462 if (bytesWritten == output.length) { 463 return output; 464 } else if (bytesWritten == 0) { 465 return EmptyArray.BYTE; 466 } else { 467 return Arrays.copyOfRange(output, 0, bytesWritten); 468 } 469 } 470 471 @Override 472 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 473 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 474 BadPaddingException { 475 if (output == null) { 476 throw new NullPointerException("output == null"); 477 } 478 479 final int maximumLen = getOutputSize(inputLen); 480 return doFinalInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen); 481 } 482 483 @Override 484 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { 485 try { 486 byte[] encoded = key.getEncoded(); 487 return engineDoFinal(encoded, 0, encoded.length); 488 } catch (BadPaddingException e) { 489 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 490 newE.initCause(e); 491 throw newE; 492 } 493 } 494 495 @Override 496 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) 497 throws InvalidKeyException, NoSuchAlgorithmException { 498 try { 499 byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 500 if (wrappedKeyType == Cipher.PUBLIC_KEY) { 501 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 502 return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); 503 } else if (wrappedKeyType == Cipher.PRIVATE_KEY) { 504 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 505 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 506 } else if (wrappedKeyType == Cipher.SECRET_KEY) { 507 return new SecretKeySpec(encoded, wrappedKeyAlgorithm); 508 } else { 509 throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType); 510 } 511 } catch (IllegalBlockSizeException e) { 512 throw new InvalidKeyException(e); 513 } catch (BadPaddingException e) { 514 throw new InvalidKeyException(e); 515 } catch (InvalidKeySpecException e) { 516 throw new InvalidKeyException(e); 517 } 518 } 519 520 public static class AES extends OpenSSLCipher { 521 private static final int AES_BLOCK_SIZE = 16; 522 523 protected AES(Mode mode, Padding padding) { 524 super(mode, padding); 525 } 526 527 public static class CBC extends AES { 528 public CBC(Padding padding) { 529 super(Mode.CBC, padding); 530 } 531 532 public static class NoPadding extends CBC { 533 public NoPadding() { 534 super(Padding.NOPADDING); 535 } 536 } 537 538 public static class PKCS5Padding extends CBC { 539 public PKCS5Padding() { 540 super(Padding.PKCS5PADDING); 541 } 542 } 543 } 544 545 public static class CFB extends AES { 546 public CFB(Padding padding) { 547 super(Mode.CFB, padding); 548 } 549 550 public static class NoPadding extends CFB { 551 public NoPadding() { 552 super(Padding.NOPADDING); 553 } 554 } 555 556 public static class PKCS5Padding extends CFB { 557 public PKCS5Padding() { 558 super(Padding.PKCS5PADDING); 559 } 560 } 561 } 562 563 public static class CTR extends AES { 564 public CTR(Padding padding) { 565 super(Mode.CTR, padding); 566 } 567 568 public static class NoPadding extends CTR { 569 public NoPadding() { 570 super(Padding.NOPADDING); 571 } 572 } 573 574 public static class PKCS5Padding extends CTR { 575 public PKCS5Padding() { 576 super(Padding.PKCS5PADDING); 577 } 578 } 579 } 580 581 public static class ECB extends AES { 582 public ECB(Padding padding) { 583 super(Mode.ECB, padding); 584 } 585 586 public static class NoPadding extends ECB { 587 public NoPadding() { 588 super(Padding.NOPADDING); 589 } 590 } 591 592 public static class PKCS5Padding extends ECB { 593 public PKCS5Padding() { 594 super(Padding.PKCS5PADDING); 595 } 596 } 597 } 598 599 public static class OFB extends AES { 600 public OFB(Padding padding) { 601 super(Mode.OFB, padding); 602 } 603 604 public static class NoPadding extends OFB { 605 public NoPadding() { 606 super(Padding.NOPADDING); 607 } 608 } 609 610 public static class PKCS5Padding extends OFB { 611 public PKCS5Padding() { 612 super(Padding.PKCS5PADDING); 613 } 614 } 615 } 616 617 @Override 618 protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException { 619 switch (keyLength) { 620 case 16: // AES 128 621 case 24: // AES 192 622 case 32: // AES 256 623 return; 624 default: 625 throw new InvalidKeyException("Unsupported key size: " + keyLength + " bytes"); 626 } 627 } 628 629 @Override 630 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 631 switch (mode) { 632 case CBC: 633 case CFB: 634 case CFB1: 635 case CFB8: 636 case CFB128: 637 case CTR: 638 case ECB: 639 case OFB: 640 return; 641 default: 642 throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString()); 643 } 644 } 645 646 @Override 647 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 648 switch (padding) { 649 case NOPADDING: 650 case PKCS5PADDING: 651 return; 652 default: 653 throw new NoSuchPaddingException("Unsupported padding " + padding.toString()); 654 } 655 } 656 657 @Override 658 protected String getBaseCipherName() { 659 return "AES"; 660 } 661 662 @Override 663 protected String getCipherName(int keyLength, Mode mode) { 664 return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US); 665 } 666 667 @Override 668 protected int getCipherBlockSize() { 669 return AES_BLOCK_SIZE; 670 } 671 } 672 673 public static class DESEDE extends OpenSSLCipher { 674 private static int DES_BLOCK_SIZE = 8; 675 676 public DESEDE(Mode mode, Padding padding) { 677 super(mode, padding); 678 } 679 680 public static class CBC extends DESEDE { 681 public CBC(Padding padding) { 682 super(Mode.CBC, padding); 683 } 684 685 public static class NoPadding extends CBC { 686 public NoPadding() { 687 super(Padding.NOPADDING); 688 } 689 } 690 691 public static class PKCS5Padding extends CBC { 692 public PKCS5Padding() { 693 super(Padding.PKCS5PADDING); 694 } 695 } 696 } 697 698 public static class CFB extends DESEDE { 699 public CFB(Padding padding) { 700 super(Mode.CFB, padding); 701 } 702 703 public static class NoPadding extends CFB { 704 public NoPadding() { 705 super(Padding.NOPADDING); 706 } 707 } 708 709 public static class PKCS5Padding extends CFB { 710 public PKCS5Padding() { 711 super(Padding.PKCS5PADDING); 712 } 713 } 714 } 715 716 public static class ECB extends DESEDE { 717 public ECB(Padding padding) { 718 super(Mode.ECB, padding); 719 } 720 721 public static class NoPadding extends ECB { 722 public NoPadding() { 723 super(Padding.NOPADDING); 724 } 725 } 726 727 public static class PKCS5Padding extends ECB { 728 public PKCS5Padding() { 729 super(Padding.PKCS5PADDING); 730 } 731 } 732 } 733 734 public static class OFB extends DESEDE { 735 public OFB(Padding padding) { 736 super(Mode.OFB, padding); 737 } 738 739 public static class NoPadding extends OFB { 740 public NoPadding() { 741 super(Padding.NOPADDING); 742 } 743 } 744 745 public static class PKCS5Padding extends OFB { 746 public PKCS5Padding() { 747 super(Padding.PKCS5PADDING); 748 } 749 } 750 } 751 752 @Override 753 protected String getBaseCipherName() { 754 return "DESede"; 755 } 756 757 @Override 758 protected String getCipherName(int keySize, Mode mode) { 759 final String baseCipherName; 760 if (keySize == 16) { 761 baseCipherName = "des-ede"; 762 } else { 763 baseCipherName = "des-ede3"; 764 } 765 766 if (mode == Mode.ECB) { 767 return baseCipherName; 768 } else { 769 return baseCipherName + "-" + mode.toString().toLowerCase(Locale.US); 770 } 771 } 772 773 @Override 774 protected void checkSupportedKeySize(int keySize) throws InvalidKeyException { 775 if (keySize != 16 && keySize != 24) { 776 throw new InvalidKeyException("key size must be 128 or 192 bits"); 777 } 778 } 779 780 @Override 781 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 782 switch (mode) { 783 case CBC: 784 case CFB: 785 case CFB1: 786 case CFB8: 787 case ECB: 788 case OFB: 789 return; 790 default: 791 throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString()); 792 } 793 } 794 795 @Override 796 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 797 switch (padding) { 798 case NOPADDING: 799 case PKCS5PADDING: 800 return; 801 default: 802 throw new NoSuchPaddingException("Unsupported padding " + padding.toString()); 803 } 804 } 805 806 @Override 807 protected int getCipherBlockSize() { 808 return DES_BLOCK_SIZE; 809 } 810 } 811 812 public static class ARC4 extends OpenSSLCipher { 813 public ARC4() { 814 } 815 816 @Override 817 protected String getBaseCipherName() { 818 return "ARCFOUR"; 819 } 820 821 @Override 822 protected String getCipherName(int keySize, Mode mode) { 823 return "rc4"; 824 } 825 826 @Override 827 protected void checkSupportedKeySize(int keySize) throws InvalidKeyException { 828 } 829 830 @Override 831 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 832 throw new NoSuchAlgorithmException("ARC4 does not support modes"); 833 } 834 835 @Override 836 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 837 throw new NoSuchPaddingException("ARC4 does not support padding"); 838 } 839 840 @Override 841 protected int getCipherBlockSize() { 842 return 0; 843 } 844 845 @Override 846 protected boolean supportsVariableSizeKey() { 847 return true; 848 } 849 } 850} 851