| package org.bouncycastle.openssl.bc; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.bouncycastle.asn1.ASN1ObjectIdentifier; |
| import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; |
| import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; |
| import org.bouncycastle.crypto.BlockCipher; |
| import org.bouncycastle.crypto.BufferedBlockCipher; |
| import org.bouncycastle.crypto.PBEParametersGenerator; |
| import org.bouncycastle.crypto.digests.SHA1Digest; |
| import org.bouncycastle.crypto.engines.AESEngine; |
| import org.bouncycastle.crypto.engines.BlowfishEngine; |
| import org.bouncycastle.crypto.engines.DESEngine; |
| import org.bouncycastle.crypto.engines.DESedeEngine; |
| import org.bouncycastle.crypto.engines.RC2Engine; |
| import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator; |
| import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; |
| import org.bouncycastle.crypto.modes.CBCBlockCipher; |
| import org.bouncycastle.crypto.modes.CFBBlockCipher; |
| import org.bouncycastle.crypto.modes.OFBBlockCipher; |
| import org.bouncycastle.crypto.paddings.BlockCipherPadding; |
| import org.bouncycastle.crypto.paddings.PKCS7Padding; |
| import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; |
| import org.bouncycastle.crypto.params.KeyParameter; |
| import org.bouncycastle.crypto.params.ParametersWithIV; |
| import org.bouncycastle.crypto.params.RC2Parameters; |
| import org.bouncycastle.openssl.EncryptionException; |
| import org.bouncycastle.openssl.PEMException; |
| import org.bouncycastle.util.Integers; |
| |
| class PEMUtilities |
| { |
| private static final Map KEYSIZES = new HashMap(); |
| private static final Set PKCS5_SCHEME_1 = new HashSet(); |
| private static final Set PKCS5_SCHEME_2 = new HashSet(); |
| |
| static |
| { |
| PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC); |
| PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC); |
| PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC); |
| PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC); |
| PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC); |
| PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC); |
| |
| PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2); |
| PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC); |
| PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC); |
| PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC); |
| PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC); |
| |
| KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192)); |
| KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integers.valueOf(128)); |
| KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integers.valueOf(192)); |
| KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integers.valueOf(256)); |
| KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4.getId(), Integers.valueOf(128)); |
| KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4, Integers.valueOf(40)); |
| KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, Integers.valueOf(128)); |
| KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, Integers.valueOf(192)); |
| KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, Integers.valueOf(128)); |
| KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, Integers.valueOf(40)); |
| } |
| |
| static int getKeySize(String algorithm) |
| { |
| if (!KEYSIZES.containsKey(algorithm)) |
| { |
| throw new IllegalStateException("no key size for algorithm: " + algorithm); |
| } |
| |
| return ((Integer)KEYSIZES.get(algorithm)).intValue(); |
| } |
| |
| static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid) |
| { |
| return PKCS5_SCHEME_1.contains(algOid); |
| } |
| |
| static boolean isPKCS5Scheme2(ASN1ObjectIdentifier algOid) |
| { |
| return PKCS5_SCHEME_2.contains(algOid); |
| } |
| |
| public static boolean isPKCS12(ASN1ObjectIdentifier algOid) |
| { |
| return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId()); |
| } |
| |
| public static KeyParameter generateSecretKeyForPKCS5Scheme2(String algorithm, char[] password, byte[] salt, int iterationCount) |
| { |
| PBEParametersGenerator paramsGen = new PKCS5S2ParametersGenerator(new SHA1Digest()); |
| |
| paramsGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, iterationCount); |
| |
| return (KeyParameter)paramsGen.generateDerivedParameters(PEMUtilities.getKeySize(algorithm)); |
| } |
| |
| static byte[] crypt( |
| boolean encrypt, |
| byte[] bytes, |
| char[] password, |
| String dekAlgName, |
| byte[] iv) |
| throws PEMException |
| { |
| byte[] ivValue = iv; |
| String blockMode = "CBC"; |
| BlockCipher engine; |
| BlockCipherPadding padding = new PKCS7Padding(); |
| KeyParameter sKey; |
| |
| // Figure out block mode and padding. |
| if (dekAlgName.endsWith("-CFB")) |
| { |
| blockMode = "CFB"; |
| padding = null; |
| } |
| if (dekAlgName.endsWith("-ECB") || |
| "DES-EDE".equals(dekAlgName) || |
| "DES-EDE3".equals(dekAlgName)) |
| { |
| // ECB is actually the default (though seldom used) when OpenSSL |
| // uses DES-EDE (des2) or DES-EDE3 (des3). |
| blockMode = "ECB"; |
| ivValue = null; |
| } |
| if (dekAlgName.endsWith("-OFB")) |
| { |
| blockMode = "OFB"; |
| padding = null; |
| } |
| |
| // Figure out algorithm and key size. |
| if (dekAlgName.startsWith("DES-EDE")) |
| { |
| // "DES-EDE" is actually des2 in OpenSSL-speak! |
| // "DES-EDE3" is des3. |
| boolean des2 = !dekAlgName.startsWith("DES-EDE3"); |
| sKey = getKey(password, 24, iv, des2); |
| engine = new DESedeEngine(); |
| } |
| else if (dekAlgName.startsWith("DES-")) |
| { |
| sKey = getKey(password, 8, iv); |
| engine = new DESEngine(); |
| } |
| else if (dekAlgName.startsWith("BF-")) |
| { |
| sKey = getKey(password, 16, iv); |
| engine = new BlowfishEngine(); |
| } |
| else if (dekAlgName.startsWith("RC2-")) |
| { |
| int keyBits = 128; |
| if (dekAlgName.startsWith("RC2-40-")) |
| { |
| keyBits = 40; |
| } |
| else if (dekAlgName.startsWith("RC2-64-")) |
| { |
| keyBits = 64; |
| } |
| sKey = new RC2Parameters(getKey(password, keyBits / 8, iv).getKey(), keyBits);; |
| engine = new RC2Engine(); |
| } |
| else if (dekAlgName.startsWith("AES-")) |
| { |
| byte[] salt = iv; |
| if (salt.length > 8) |
| { |
| salt = new byte[8]; |
| System.arraycopy(iv, 0, salt, 0, 8); |
| } |
| |
| int keyBits; |
| if (dekAlgName.startsWith("AES-128-")) |
| { |
| keyBits = 128; |
| } |
| else if (dekAlgName.startsWith("AES-192-")) |
| { |
| keyBits = 192; |
| } |
| else if (dekAlgName.startsWith("AES-256-")) |
| { |
| keyBits = 256; |
| } |
| else |
| { |
| throw new EncryptionException("unknown AES encryption with private key: " + dekAlgName); |
| } |
| sKey = getKey(password, keyBits / 8, salt); |
| engine = new AESEngine(); |
| } |
| else |
| { |
| throw new EncryptionException("unknown encryption with private key: " + dekAlgName); |
| } |
| |
| if (blockMode.equals("CBC")) |
| { |
| engine = new CBCBlockCipher(engine); |
| } |
| else if (blockMode.equals("CFB")) |
| { |
| engine = new CFBBlockCipher(engine, engine.getBlockSize() * 8); |
| } |
| else if (blockMode.equals("OFB")) |
| { |
| engine = new OFBBlockCipher(engine, engine.getBlockSize() * 8); |
| } |
| |
| try |
| { |
| BufferedBlockCipher c; |
| if (padding == null) |
| { |
| c = new BufferedBlockCipher(engine); |
| } |
| else |
| { |
| c = new PaddedBufferedBlockCipher(engine, padding); |
| } |
| |
| if (ivValue == null) // ECB block mode |
| { |
| c.init(encrypt, sKey); |
| } |
| else |
| { |
| c.init(encrypt, new ParametersWithIV(sKey, ivValue)); |
| } |
| |
| byte[] out = new byte[c.getOutputSize(bytes.length)]; |
| |
| int procLen = c.processBytes(bytes, 0, bytes.length, out, 0); |
| |
| procLen += c.doFinal(out, procLen); |
| |
| if (procLen == out.length) |
| { |
| return out; |
| } |
| else |
| { |
| byte[] rv = new byte[procLen]; |
| |
| System.arraycopy(out, 0, rv, 0, procLen); |
| |
| return rv; |
| } |
| } |
| catch (Exception e) |
| { |
| throw new EncryptionException("exception using cipher - please check password and data.", e); |
| } |
| } |
| |
| private static KeyParameter getKey( |
| char[] password, |
| int keyLength, |
| byte[] salt) |
| throws PEMException |
| { |
| return getKey(password, keyLength, salt, false); |
| } |
| |
| private static KeyParameter getKey( |
| char[] password, |
| int keyLength, |
| byte[] salt, |
| boolean des2) |
| throws PEMException |
| { |
| PBEParametersGenerator paramsGen = new OpenSSLPBEParametersGenerator(); |
| |
| paramsGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, 1); |
| |
| KeyParameter kp = (KeyParameter)paramsGen.generateDerivedParameters(keyLength * 8); |
| |
| if (des2 && kp.getKey().length == 24) |
| { |
| // For DES2, we must copy first 8 bytes into the last 8 bytes. |
| byte[] key = kp.getKey(); |
| |
| System.arraycopy(key, 0, key, 16, 8); |
| |
| return new KeyParameter(key); |
| } |
| |
| return kp; |
| } |
| } |