| package org.bouncycastle.jcajce.provider.symmetric.util; |
| |
| // BEGIN Android-added: Needed for compatibility helper |
| import java.lang.reflect.Method; |
| // END Android-added: Needed for compatibility helper |
| import java.security.InvalidAlgorithmParameterException; |
| import java.security.spec.AlgorithmParameterSpec; |
| |
| import javax.crypto.SecretKey; |
| // BEGIN Android-added: Allow IVs specified in parameters. |
| import javax.crypto.spec.IvParameterSpec; |
| // END Android-added: Allow IVs specified in parameters. |
| import javax.crypto.spec.PBEKeySpec; |
| import javax.crypto.spec.PBEParameterSpec; |
| |
| import org.bouncycastle.crypto.CipherParameters; |
| import org.bouncycastle.crypto.PBEParametersGenerator; |
| // Android-removed: Unsupported algorithms |
| // import org.bouncycastle.crypto.digests.GOST3411Digest; |
| // import org.bouncycastle.crypto.digests.MD2Digest; |
| // import org.bouncycastle.crypto.digests.RIPEMD160Digest; |
| // import org.bouncycastle.crypto.digests.TigerDigest; |
| import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator; |
| import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator; |
| import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator; |
| import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; |
| import org.bouncycastle.crypto.params.DESParameters; |
| import org.bouncycastle.crypto.params.KeyParameter; |
| import org.bouncycastle.crypto.params.ParametersWithIV; |
| // BEGIN Android-changed: Use Android digests |
| // import org.bouncycastle.crypto.util.DigestFactory; |
| import org.bouncycastle.crypto.digests.AndroidDigestFactory; |
| // END Android-changed: Use Android digests |
| |
| public interface PBE |
| { |
| // |
| // PBE Based encryption constants - by default we do PKCS12 with SHA-1 |
| // |
| static final int MD5 = 0; |
| static final int SHA1 = 1; |
| // Android-removed: Unsupported algorithms |
| // static final int RIPEMD160 = 2; |
| // static final int TIGER = 3; |
| static final int SHA256 = 4; |
| // Android-removed: Unsupported algorithms |
| // static final int MD2 = 5; |
| // static final int GOST3411 = 6; |
| static final int SHA224 = 7; |
| static final int SHA384 = 8; |
| static final int SHA512 = 9; |
| static final int SHA3_224 = 10; |
| static final int SHA3_256 = 11; |
| static final int SHA3_384 = 12; |
| static final int SHA3_512 = 13; |
| |
| static final int PKCS5S1 = 0; |
| static final int PKCS5S2 = 1; |
| static final int PKCS12 = 2; |
| static final int OPENSSL = 3; |
| static final int PKCS5S1_UTF8 = 4; |
| static final int PKCS5S2_UTF8 = 5; |
| |
| /** |
| * uses the appropriate mixer to generate the key and IV if necessary. |
| */ |
| static class Util |
| { |
| static private PBEParametersGenerator makePBEGenerator( |
| int type, |
| int hash) |
| { |
| PBEParametersGenerator generator; |
| |
| if (type == PKCS5S1 || type == PKCS5S1_UTF8) |
| { |
| switch (hash) |
| { |
| // Android-removed: Unsupported algorithms |
| // case MD2: |
| // generator = new PKCS5S1ParametersGenerator(new MD2Digest()); |
| // break; |
| case MD5: |
| // Android-changed: Use Android digests |
| // generator = new PKCS5S1ParametersGenerator(DigestFactory.createMD5()); |
| generator = new PKCS5S1ParametersGenerator(AndroidDigestFactory.getMD5()); |
| break; |
| case SHA1: |
| // Android-changed: Use Android digests |
| // generator = new PKCS5S1ParametersGenerator(DigestFactory.createSHA1()); |
| generator = new PKCS5S1ParametersGenerator(AndroidDigestFactory.getSHA1()); |
| break; |
| default: |
| throw new IllegalStateException("PKCS5 scheme 1 only supports MD2, MD5 and SHA1."); |
| } |
| } |
| else if (type == PKCS5S2 || type == PKCS5S2_UTF8) |
| { |
| switch (hash) |
| { |
| // Android-removed: Unsupported algorithms |
| // case MD2: |
| // generator = new PKCS5S2ParametersGenerator(new MD2Digest()); |
| // break; |
| case MD5: |
| // Android-changed: Use Android digests |
| // generator = new PKCS5S2ParametersGenerator(DigestFactory.createMD5()); |
| generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getMD5()); |
| break; |
| case SHA1: |
| // Android-changed: Use Android digests |
| // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA1()); |
| generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA1()); |
| break; |
| // BEGIN Android-removed: Unsupported algorithms |
| /* |
| case RIPEMD160: |
| generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest()); |
| break; |
| case TIGER: |
| generator = new PKCS5S2ParametersGenerator(new TigerDigest()); |
| break; |
| */ |
| // END Android-removed: Unsupported algorithms |
| case SHA256: |
| // Android-changed: Use Android digests |
| // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA256()); |
| generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA256()); |
| break; |
| // Android-removed: Unsupported algorithms |
| // case GOST3411: |
| // generator = new PKCS5S2ParametersGenerator(new GOST3411Digest()); |
| // break; |
| case SHA224: |
| // Android-changed: Use Android digests |
| // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA224()); |
| generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA224()); |
| break; |
| case SHA384: |
| // Android-changed: Use Android digests |
| // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA384()); |
| generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA384()); |
| break; |
| case SHA512: |
| // Android-changed: Use Android digests |
| // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512()); |
| generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA512()); |
| break; |
| // BEGIN Android-removed: Unsupported algorithms |
| /* |
| case SHA3_224: |
| generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_224()); |
| break; |
| case SHA3_256: |
| generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_256()); |
| break; |
| case SHA3_384: |
| generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_384()); |
| break; |
| case SHA3_512: |
| generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_512()); |
| break; |
| */ |
| // END Android-removed: Unsupported algorithms |
| default: |
| throw new IllegalStateException("unknown digest scheme for PBE PKCS5S2 encryption."); |
| } |
| } |
| else if (type == PKCS12) |
| { |
| switch (hash) |
| { |
| // Android-removed: Unsupported algorithms |
| // case MD2: |
| // generator = new PKCS12ParametersGenerator(new MD2Digest()); |
| // break; |
| case MD5: |
| // Android-changed: Use Android digests |
| // generator = new PKCS12ParametersGenerator(DigestFactory.createMD5()); |
| generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getMD5()); |
| break; |
| case SHA1: |
| // Android-changed: Use Android digests |
| // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA1()); |
| generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1()); |
| break; |
| // BEGIN Android-removed: Unsupported algorithms |
| /* |
| case RIPEMD160: |
| generator = new PKCS12ParametersGenerator(new RIPEMD160Digest()); |
| break; |
| case TIGER: |
| generator = new PKCS12ParametersGenerator(new TigerDigest()); |
| break; |
| */ |
| // END Android-removed: Unsupported algorithms |
| case SHA256: |
| // Android-changed: Use Android digests |
| // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA256()); |
| generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA256()); |
| break; |
| // Android-removed: Unsupported algorithms |
| // case GOST3411: |
| // generator = new PKCS12ParametersGenerator(new GOST3411Digest()); |
| // break; |
| case SHA224: |
| // Android-changed: Use Android digests |
| // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA224()); |
| generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA224()); |
| break; |
| case SHA384: |
| // Android-changed: Use Android digests |
| // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA384()); |
| generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA384()); |
| break; |
| case SHA512: |
| // Android-changed: Use Android digests |
| // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA512()); |
| generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA512()); |
| break; |
| default: |
| throw new IllegalStateException("unknown digest scheme for PBE encryption."); |
| } |
| } |
| else |
| { |
| generator = new OpenSSLPBEParametersGenerator(); |
| } |
| |
| return generator; |
| } |
| |
| /** |
| * construct a key and iv (if necessary) suitable for use with a |
| * Cipher. |
| */ |
| public static CipherParameters makePBEParameters( |
| byte[] pbeKey, |
| int scheme, |
| int digest, |
| int keySize, |
| int ivSize, |
| AlgorithmParameterSpec spec, |
| String targetAlgorithm) |
| throws InvalidAlgorithmParameterException |
| { |
| if ((spec == null) || !(spec instanceof PBEParameterSpec)) |
| { |
| throw new InvalidAlgorithmParameterException("Need a PBEParameter spec with a PBE key."); |
| } |
| |
| PBEParameterSpec pbeParam = (PBEParameterSpec)spec; |
| PBEParametersGenerator generator = makePBEGenerator(scheme, digest); |
| byte[] key = pbeKey; |
| CipherParameters param; |
| |
| // if (pbeKey.shouldTryWrongPKCS12()) |
| // { |
| // key = new byte[2]; |
| // } |
| |
| generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); |
| |
| if (ivSize != 0) |
| { |
| param = generator.generateDerivedParameters(keySize, ivSize); |
| // BEGIN Android-added: Allow IVs specified in parameters. |
| // PKCS5S2 doesn't specify that the IV must be generated from the password. If the |
| // IV is passed as a parameter, use it. |
| AlgorithmParameterSpec parameterSpecFromPBEParameterSpec = |
| getParameterSpecFromPBEParameterSpec(pbeParam); |
| if ((scheme == PKCS5S2 || scheme == PKCS5S2_UTF8) |
| && parameterSpecFromPBEParameterSpec instanceof IvParameterSpec) { |
| ParametersWithIV parametersWithIV = (ParametersWithIV) param; |
| IvParameterSpec ivParameterSpec = |
| (IvParameterSpec) parameterSpecFromPBEParameterSpec; |
| param = new ParametersWithIV( |
| (KeyParameter) parametersWithIV.getParameters(), |
| ivParameterSpec.getIV()); |
| } |
| // END Android-added: Allow IVs specified in parameters. |
| } |
| else |
| { |
| param = generator.generateDerivedParameters(keySize); |
| } |
| |
| if (targetAlgorithm.startsWith("DES")) |
| { |
| if (param instanceof ParametersWithIV) |
| { |
| KeyParameter kParam = (KeyParameter)((ParametersWithIV)param).getParameters(); |
| |
| DESParameters.setOddParity(kParam.getKey()); |
| } |
| else |
| { |
| KeyParameter kParam = (KeyParameter)param; |
| |
| DESParameters.setOddParity(kParam.getKey()); |
| } |
| } |
| |
| return param; |
| } |
| |
| /** |
| * construct a key and iv (if necessary) suitable for use with a |
| * Cipher. |
| */ |
| public static CipherParameters makePBEParameters( |
| BCPBEKey pbeKey, |
| AlgorithmParameterSpec spec, |
| String targetAlgorithm) |
| { |
| if ((spec == null) || !(spec instanceof PBEParameterSpec)) |
| { |
| throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); |
| } |
| |
| PBEParameterSpec pbeParam = (PBEParameterSpec)spec; |
| PBEParametersGenerator generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest()); |
| byte[] key = pbeKey.getEncoded(); |
| CipherParameters param; |
| |
| if (pbeKey.shouldTryWrongPKCS12()) |
| { |
| key = new byte[2]; |
| } |
| |
| generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); |
| |
| if (pbeKey.getIvSize() != 0) |
| { |
| param = generator.generateDerivedParameters(pbeKey.getKeySize(), pbeKey.getIvSize()); |
| // BEGIN Android-added: Allow IVs specified in parameters. |
| // PKCS5S2 doesn't specify that the IV must be generated from the password. If the |
| // IV is passed as a parameter, use it. |
| AlgorithmParameterSpec parameterSpecFromPBEParameterSpec = |
| getParameterSpecFromPBEParameterSpec(pbeParam); |
| if ((pbeKey.getType() == PKCS5S2 || pbeKey.getType() == PKCS5S2_UTF8) |
| && parameterSpecFromPBEParameterSpec instanceof IvParameterSpec) { |
| ParametersWithIV parametersWithIV = (ParametersWithIV) param; |
| IvParameterSpec ivParameterSpec = |
| (IvParameterSpec) parameterSpecFromPBEParameterSpec; |
| param = new ParametersWithIV( |
| (KeyParameter) parametersWithIV.getParameters(), |
| ivParameterSpec.getIV()); |
| } |
| // END Android-added: Allow IVs specified in parameters. |
| } |
| else |
| { |
| param = generator.generateDerivedParameters(pbeKey.getKeySize()); |
| } |
| |
| if (targetAlgorithm.startsWith("DES")) |
| { |
| if (param instanceof ParametersWithIV) |
| { |
| KeyParameter kParam = (KeyParameter)((ParametersWithIV)param).getParameters(); |
| |
| DESParameters.setOddParity(kParam.getKey()); |
| } |
| else |
| { |
| KeyParameter kParam = (KeyParameter)param; |
| |
| DESParameters.setOddParity(kParam.getKey()); |
| } |
| } |
| |
| return param; |
| } |
| |
| /** |
| * generate a PBE based key suitable for a MAC algorithm, the |
| * key size is chosen according the MAC size, or the hashing algorithm, |
| * whichever is greater. |
| */ |
| public static CipherParameters makePBEMacParameters( |
| BCPBEKey pbeKey, |
| AlgorithmParameterSpec spec) |
| { |
| if ((spec == null) || !(spec instanceof PBEParameterSpec)) |
| { |
| throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); |
| } |
| |
| PBEParameterSpec pbeParam = (PBEParameterSpec)spec; |
| PBEParametersGenerator generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest()); |
| byte[] key = pbeKey.getEncoded(); |
| CipherParameters param; |
| |
| generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); |
| |
| param = generator.generateDerivedMacParameters(pbeKey.getKeySize()); |
| |
| return param; |
| } |
| |
| /** |
| * generate a PBE based key suitable for a MAC algorithm, the |
| * key size is chosen according the MAC size, or the hashing algorithm, |
| * whichever is greater. |
| */ |
| public static CipherParameters makePBEMacParameters( |
| PBEKeySpec keySpec, |
| int type, |
| int hash, |
| int keySize) |
| { |
| PBEParametersGenerator generator = makePBEGenerator(type, hash); |
| byte[] key; |
| CipherParameters param; |
| |
| key = convertPassword(type, keySpec); |
| |
| generator.init(key, keySpec.getSalt(), keySpec.getIterationCount()); |
| |
| param = generator.generateDerivedMacParameters(keySize); |
| |
| for (int i = 0; i != key.length; i++) |
| { |
| key[i] = 0; |
| } |
| |
| return param; |
| } |
| |
| /** |
| * construct a key and iv (if necessary) suitable for use with a |
| * Cipher. |
| */ |
| public static CipherParameters makePBEParameters( |
| PBEKeySpec keySpec, |
| int type, |
| int hash, |
| int keySize, |
| int ivSize) |
| { |
| PBEParametersGenerator generator = makePBEGenerator(type, hash); |
| byte[] key; |
| CipherParameters param; |
| |
| key = convertPassword(type, keySpec); |
| |
| generator.init(key, keySpec.getSalt(), keySpec.getIterationCount()); |
| |
| if (ivSize != 0) |
| { |
| param = generator.generateDerivedParameters(keySize, ivSize); |
| } |
| else |
| { |
| param = generator.generateDerivedParameters(keySize); |
| } |
| |
| for (int i = 0; i != key.length; i++) |
| { |
| key[i] = 0; |
| } |
| |
| return param; |
| } |
| |
| /** |
| * generate a PBE based key suitable for a MAC algorithm, the |
| * key size is chosen according the MAC size, or the hashing algorithm, |
| * whichever is greater. |
| */ |
| public static CipherParameters makePBEMacParameters( |
| SecretKey key, |
| int type, |
| int hash, |
| int keySize, |
| PBEParameterSpec pbeSpec) |
| { |
| PBEParametersGenerator generator = makePBEGenerator(type, hash); |
| CipherParameters param; |
| |
| byte[] keyBytes = key.getEncoded(); |
| |
| generator.init(key.getEncoded(), pbeSpec.getSalt(), pbeSpec.getIterationCount()); |
| |
| param = generator.generateDerivedMacParameters(keySize); |
| |
| for (int i = 0; i != keyBytes.length; i++) |
| { |
| keyBytes[i] = 0; |
| } |
| |
| return param; |
| } |
| |
| // BEGIN Android-added: Add helper for 1.8 compatibility. |
| /** |
| * Invokes the method {@link PBEParameterSpec#getParameterSpec()} via reflection. |
| * |
| * Needed as the method was introduced in Java 1.8 and Bouncycastle level is 1.5. |
| * |
| * @return the parameter spec, or null if the method is not available. |
| */ |
| public static AlgorithmParameterSpec getParameterSpecFromPBEParameterSpec( |
| PBEParameterSpec pbeParameterSpec) { |
| try { |
| Method getParameterSpecMethod = PBE.class.getClassLoader() |
| .loadClass("javax.crypto.spec.PBEParameterSpec") |
| .getMethod("getParameterSpec"); |
| return (AlgorithmParameterSpec) getParameterSpecMethod.invoke(pbeParameterSpec); |
| } catch (Exception e) { |
| return null; |
| } |
| } |
| // END Android-added: Add helper for 1.8 compatibility. |
| |
| |
| private static byte[] convertPassword(int type, PBEKeySpec keySpec) |
| { |
| byte[] key; |
| |
| if (type == PKCS12) |
| { |
| key = PBEParametersGenerator.PKCS12PasswordToBytes(keySpec.getPassword()); |
| } |
| else if (type == PKCS5S2_UTF8 || type == PKCS5S1_UTF8) |
| { |
| key = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(keySpec.getPassword()); |
| } |
| else |
| { |
| key = PBEParametersGenerator.PKCS5PasswordToBytes(keySpec.getPassword()); |
| } |
| return key; |
| } |
| } |
| } |