Home | History | Annotate | Download | only in jsse
      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