Home | History | Annotate | Download | only in openssl
      1 package org.bouncycastle.openssl;
      2 
      3 import java.io.IOException;
      4 import java.security.Key;
      5 import java.security.Provider;
      6 import java.security.Security;
      7 import java.security.spec.AlgorithmParameterSpec;
      8 import java.util.HashMap;
      9 import java.util.HashSet;
     10 import java.util.Map;
     11 import java.util.Set;
     12 
     13 import javax.crypto.Cipher;
     14 import javax.crypto.SecretKey;
     15 import javax.crypto.spec.IvParameterSpec;
     16 import javax.crypto.spec.RC2ParameterSpec;
     17 import javax.crypto.spec.SecretKeySpec;
     18 
     19 import org.bouncycastle.asn1.DERObjectIdentifier;
     20 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
     21 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
     22 import org.bouncycastle.crypto.PBEParametersGenerator;
     23 import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
     24 import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
     25 import org.bouncycastle.crypto.params.KeyParameter;
     26 
     27 final class PEMUtilities
     28 {
     29     private static final Map KEYSIZES = new HashMap();
     30     private static final Set PKCS5_SCHEME_1 = new HashSet();
     31     private static final Set PKCS5_SCHEME_2 = new HashSet();
     32 
     33     static
     34     {
     35         PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC);
     36         PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC);
     37         PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC);
     38         PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC);
     39         PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC);
     40         PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC);
     41 
     42         PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2);
     43         PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC);
     44         PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC);
     45         PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC);
     46         PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC);
     47 
     48         // BEGIN android-changed
     49         KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integer.valueOf(192));
     50         KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integer.valueOf(128));
     51         KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integer.valueOf(192));
     52         KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integer.valueOf(256));
     53         // END android-changed
     54     }
     55 
     56     static int getKeySize(String algorithm)
     57     {
     58         if (!KEYSIZES.containsKey(algorithm))
     59         {
     60             throw new IllegalStateException("no key size for algorithm: " + algorithm);
     61         }
     62 
     63         return ((Integer)KEYSIZES.get(algorithm)).intValue();
     64     }
     65 
     66     static boolean isPKCS5Scheme1(DERObjectIdentifier algOid)
     67     {
     68         return PKCS5_SCHEME_1.contains(algOid);
     69     }
     70 
     71     static boolean isPKCS5Scheme2(DERObjectIdentifier algOid)
     72     {
     73         return PKCS5_SCHEME_2.contains(algOid);
     74     }
     75 
     76     static boolean isPKCS12(DERObjectIdentifier algOid)
     77     {
     78         return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId());
     79     }
     80 
     81     static SecretKey generateSecretKeyForPKCS5Scheme2(String algorithm, char[] password, byte[] salt, int iterationCount)
     82     {
     83         PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
     84 
     85         generator.init(
     86             PBEParametersGenerator.PKCS5PasswordToBytes(password),
     87             salt,
     88             iterationCount);
     89 
     90         return new SecretKeySpec(((KeyParameter)generator.generateDerivedParameters(PEMUtilities.getKeySize(algorithm))).getKey(), algorithm);
     91     }
     92 
     93     static byte[] crypt(
     94         boolean encrypt,
     95         String provider,
     96         byte[]  bytes,
     97         char[]  password,
     98         String  dekAlgName,
     99         byte[]  iv)
    100         throws IOException
    101     {
    102         Provider prov = null;
    103         if (provider != null)
    104         {
    105             prov = Security.getProvider(provider);
    106             if (prov == null)
    107             {
    108                 throw new EncryptionException("cannot find provider: " + provider);
    109             }
    110         }
    111 
    112         return crypt(encrypt, prov, bytes, password, dekAlgName, iv);
    113     }
    114 
    115     static byte[] crypt(
    116         boolean encrypt,
    117         Provider provider,
    118         byte[]  bytes,
    119         char[]  password,
    120         String  dekAlgName,
    121         byte[]  iv)
    122         throws IOException
    123     {
    124         AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
    125         String                 alg;
    126         String                 blockMode = "CBC";
    127         String                 padding = "PKCS5Padding";
    128         Key                    sKey;
    129 
    130         // Figure out block mode and padding.
    131         if (dekAlgName.endsWith("-CFB"))
    132         {
    133             blockMode = "CFB";
    134             padding = "NoPadding";
    135         }
    136         if (dekAlgName.endsWith("-ECB") ||
    137             "DES-EDE".equals(dekAlgName) ||
    138             "DES-EDE3".equals(dekAlgName))
    139         {
    140             // ECB is actually the default (though seldom used) when OpenSSL
    141             // uses DES-EDE (des2) or DES-EDE3 (des3).
    142             blockMode = "ECB";
    143             paramSpec = null;
    144         }
    145         if (dekAlgName.endsWith("-OFB"))
    146         {
    147             blockMode = "OFB";
    148             padding = "NoPadding";
    149         }
    150 
    151 
    152         // Figure out algorithm and key size.
    153         if (dekAlgName.startsWith("DES-EDE"))
    154         {
    155             alg = "DESede";
    156             // "DES-EDE" is actually des2 in OpenSSL-speak!
    157             // "DES-EDE3" is des3.
    158             boolean des2 = !dekAlgName.startsWith("DES-EDE3");
    159             sKey = getKey(password, alg, 24, iv, des2);
    160         }
    161         else if (dekAlgName.startsWith("DES-"))
    162         {
    163             alg = "DES";
    164             sKey = getKey(password, alg, 8, iv);
    165         }
    166         else if (dekAlgName.startsWith("BF-"))
    167         {
    168             alg = "Blowfish";
    169             sKey = getKey(password, alg, 16, iv);
    170         }
    171         else if (dekAlgName.startsWith("RC2-"))
    172         {
    173             alg = "RC2";
    174             int keyBits = 128;
    175             if (dekAlgName.startsWith("RC2-40-"))
    176             {
    177                 keyBits = 40;
    178             }
    179             else if (dekAlgName.startsWith("RC2-64-"))
    180             {
    181                 keyBits = 64;
    182             }
    183             sKey = getKey(password, alg, keyBits / 8, iv);
    184             if (paramSpec == null) // ECB block mode
    185             {
    186                 paramSpec = new RC2ParameterSpec(keyBits);
    187             }
    188             else
    189             {
    190                 paramSpec = new RC2ParameterSpec(keyBits, iv);
    191             }
    192         }
    193         else if (dekAlgName.startsWith("AES-"))
    194         {
    195             alg = "AES";
    196             byte[] salt = iv;
    197             if (salt.length > 8)
    198             {
    199                 salt = new byte[8];
    200                 System.arraycopy(iv, 0, salt, 0, 8);
    201             }
    202 
    203             int keyBits;
    204             if (dekAlgName.startsWith("AES-128-"))
    205             {
    206                 keyBits = 128;
    207             }
    208             else if (dekAlgName.startsWith("AES-192-"))
    209             {
    210                 keyBits = 192;
    211             }
    212             else if (dekAlgName.startsWith("AES-256-"))
    213             {
    214                 keyBits = 256;
    215             }
    216             else
    217             {
    218                 throw new EncryptionException("unknown AES encryption with private key");
    219             }
    220             sKey = getKey(password, "AES", keyBits / 8, salt);
    221         }
    222         else
    223         {
    224             throw new EncryptionException("unknown encryption with private key");
    225         }
    226 
    227         String transformation = alg + "/" + blockMode + "/" + padding;
    228 
    229         try
    230         {
    231             Cipher c = Cipher.getInstance(transformation, provider);
    232             int    mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
    233 
    234             if (paramSpec == null) // ECB block mode
    235             {
    236                 c.init(mode, sKey);
    237             }
    238             else
    239             {
    240                 c.init(mode, sKey, paramSpec);
    241             }
    242             return c.doFinal(bytes);
    243         }
    244         catch (Exception e)
    245         {
    246             throw new EncryptionException("exception using cipher - please check password and data.", e);
    247         }
    248     }
    249 
    250     private static SecretKey getKey(
    251         char[]  password,
    252         String  algorithm,
    253         int     keyLength,
    254         byte[]  salt)
    255     {
    256         return getKey(password, algorithm, keyLength, salt, false);
    257     }
    258 
    259     private static SecretKey getKey(
    260         char[]  password,
    261         String  algorithm,
    262         int     keyLength,
    263         byte[]  salt,
    264         boolean des2)
    265     {
    266         OpenSSLPBEParametersGenerator   pGen = new OpenSSLPBEParametersGenerator();
    267 
    268         pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt);
    269 
    270         KeyParameter keyParam;
    271         keyParam = (KeyParameter) pGen.generateDerivedParameters(keyLength * 8);
    272         byte[] key = keyParam.getKey();
    273         if (des2 && key.length >= 24)
    274         {
    275             // For DES2, we must copy first 8 bytes into the last 8 bytes.
    276             System.arraycopy(key, 0, key, 16, 8);
    277         }
    278         return new javax.crypto.spec.SecretKeySpec(key, algorithm);
    279     }
    280 }
    281