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