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