diff options
author | Sergio Giro <sgiro@google.com> | 2016-10-13 14:07:05 +0100 |
---|---|---|
committer | Sergio Giro <sgiro@google.com> | 2016-10-20 15:20:19 +0000 |
commit | 64fe4be2b2c98b1231314c785da2c050eccfe99b (patch) | |
tree | 8ffc9669ccf64a7db706034ead8838c546722292 | |
parent | 48408df5b5da2549208b2a1af26099f80b181f95 (diff) | |
download | bouncycastle-64fe4be2b2c98b1231314c785da2c050eccfe99b.tar.gz |
bouncycastle: add support for PKCS5S2 algorithm parameters
Java 8 allows to specify a PBE key using only the password (as opposed
to password + salt + iteration count) and generate the encryption key
later by specifying the rest of the parameters in an AlgorithmParameters
object.
Adding these AlgorithmParameters in BouncyCastle together with support
in ciphers.
Bug: 29631070
Test: run CtsLibcoreTestCases
Change-Id: I0edb36e51374e3e60d8beb10d6178a304f022520
6 files changed, 519 insertions, 4 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java index 7e26156b..9c99e0b5 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java @@ -145,11 +145,14 @@ public class SHA1 private int scheme; // BEGIN ANDROID-ADDED private int digest; + private int keySizeInBits; + private int ivSizeInBits; // END ANDROID-ADDED // BEGIN ANDROID-CHANGED - // Was: public static class BasePBKDF2WithHmacSHA1 - private BasePBKDF2WithHmacSHA_Variant(String name, int scheme, int digest) + // Was: public BasePBKDF2WithHmacSHA1(String name, int scheme) + private BasePBKDF2WithHmacSHA_Variant( + String name, int scheme, int digest, int keySizeInBits, int ivSizeInBits) // END ANDROID-CHANGED { super(name, PKCSObjectIdentifiers.id_PBKDF2); @@ -157,8 +160,16 @@ public class SHA1 this.scheme = scheme; // BEGIN ANDROID-ADDED this.digest = digest; - // BEGIN ANDROID-ADDED + this.keySizeInBits = keySizeInBits; + this.ivSizeInBits = ivSizeInBits; + // END ANDROID-ADDED + } + + // BEGIN android-added + private BasePBKDF2WithHmacSHA_Variant(String name, int scheme, int digest) { + this(name, scheme, digest, 0, 0); } + // END android-added protected SecretKey engineGenerateSecret( KeySpec keySpec) @@ -168,6 +179,22 @@ public class SHA1 { PBEKeySpec pbeSpec = (PBEKeySpec)keySpec; + // BEGIN ANDROID-ADDED + // Allow to specify a key using only the password. The key will be generated later + // when other parameters are known. + if (pbeSpec.getSalt() == null + && pbeSpec.getIterationCount() == 0 + && pbeSpec.getKeyLength() == 0 + && pbeSpec.getPassword().length > 0 + && keySizeInBits != 0) { + return new BCPBEKey( + this.algName, this.algOid, scheme, digest, keySizeInBits, ivSizeInBits, + pbeSpec, + // cipherParameters, to be generated when the PBE parameters are known. + null); + } + // END ANDROID-ADDED + if (pbeSpec.getSalt() == null) { throw new InvalidKeySpecException("missing required salt"); @@ -296,6 +323,77 @@ public class SHA1 super("PBKDF2WithHmacSHA512", PKCS5S2_UTF8); } } + + public static class PBEWithHmacSHA1AndAES_128 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA1AndAES_128() { + super("PBEWithHmacSHA1AndAES_128", PKCS5S2_UTF8, SHA1, 128, 128); + } + } + + public static class PBEWithHmacSHA224AndAES_128 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA224AndAES_128() { + super("PBEWithHmacSHA224AndAES_128", PKCS5S2_UTF8, SHA224, 128, 128); + } + } + + public static class PBEWithHmacSHA256AndAES_128 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA256AndAES_128() { + super("PBEWithHmacSHA256AndAES_128", PKCS5S2_UTF8, SHA256, 128, 128); + } + } + + public static class PBEWithHmacSHA384AndAES_128 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA384AndAES_128() { + super("PBEWithHmacSHA384AndAES_128", PKCS5S2_UTF8, SHA384, 128, 128); + } + } + + public static class PBEWithHmacSHA512AndAES_128 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA512AndAES_128() { + super("PBEWithHmacSHA512AndAES_128", PKCS5S2_UTF8, SHA512, 128, 128); + } + } + + + public static class PBEWithHmacSHA1AndAES_256 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA1AndAES_256() { + super("PBEWithHmacSHA1AndAES_256", PKCS5S2_UTF8, SHA1, 256, 128); + } + } + + public static class PBEWithHmacSHA224AndAES_256 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA224AndAES_256() { + super("PBEWithHmacSHA224AndAES_256", PKCS5S2_UTF8, SHA224, 256, 128); + } + } + + public static class PBEWithHmacSHA256AndAES_256 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA256AndAES_256() { + super("PBEWithHmacSHA256AndAES_256", PKCS5S2_UTF8, SHA256, 256, 128); + } + } + + public static class PBEWithHmacSHA384AndAES_256 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA384AndAES_256() { + super("PBEWithHmacSHA384AndAES_256", PKCS5S2_UTF8, SHA384, 256, 128); + } + } + + public static class PBEWithHmacSHA512AndAES_256 + extends BasePBKDF2WithHmacSHA_Variant { + public PBEWithHmacSHA512AndAES_256() { + super("PBEWithHmacSHA512AndAES_256", PKCS5S2_UTF8, SHA512, 256, 128); + } + } // END ANDROID-ADDED @@ -338,6 +436,16 @@ public class SHA1 provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA256", PREFIX + "$PBKDF2WithHmacSHA256UTF8"); provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA384", PREFIX + "$PBKDF2WithHmacSHA384UTF8"); provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA512", PREFIX + "$PBKDF2WithHmacSHA512UTF8"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA1AndAES_128", PREFIX + "$PBEWithHmacSHA1AndAES_128"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA224AndAES_128", PREFIX + "$PBEWithHmacSHA224AndAES_128"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA256AndAES_128", PREFIX + "$PBEWithHmacSHA256AndAES_128"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA384AndAES_128", PREFIX + "$PBEWithHmacSHA384AndAES_128"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA512AndAES_128", PREFIX + "$PBEWithHmacSHA512AndAES_128"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA1AndAES_256", PREFIX + "$PBEWithHmacSHA1AndAES_256"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA224AndAES_256", PREFIX + "$PBEWithHmacSHA224AndAES_256"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA256AndAES_256", PREFIX + "$PBEWithHmacSHA256AndAES_256"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA384AndAES_256", PREFIX + "$PBEWithHmacSHA384AndAES_256"); + provider.addAlgorithm("SecretKeyFactory.PBEWithHmacSHA512AndAES_256", PREFIX + "$PBEWithHmacSHA512AndAES_256"); // END android-added provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBKDF2WithHmacSHA1AndUTF8", "PBKDF2WithHmacSHA1"); provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA1And8BIT", PREFIX + "$PBKDF2WithHmacSHA18BIT"); diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBES2AlgorithmParameters.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBES2AlgorithmParameters.java new file mode 100644 index 00000000..9f2262f3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBES2AlgorithmParameters.java @@ -0,0 +1,330 @@ +// BEGIN android-added +// Based on org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12 + +package org.bouncycastle.jcajce.provider.symmetric; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.EncryptionScheme; +import org.bouncycastle.asn1.pkcs.KeyDerivationFunc; +import org.bouncycastle.asn1.pkcs.PBES2Parameters; +import org.bouncycastle.asn1.pkcs.PBKDF2Params; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters; +import org.bouncycastle.jcajce.provider.symmetric.util.PBE; +import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.util.Enumeration; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; + +public class PBES2AlgorithmParameters +{ + private PBES2AlgorithmParameters() + { + + } + + private static abstract class BasePBEWithHmacAlgorithmParameters + extends BaseAlgorithmParameters + { + private final AlgorithmIdentifier kdf; + private final String kdfShortName; + private final int keySize; + private final ASN1ObjectIdentifier cipherAlgorithm; + private final String cipherAlgorithmShortName; + + private PBES2Parameters params; + + private BasePBEWithHmacAlgorithmParameters( + ASN1ObjectIdentifier kdf, + String kdfShortName, + int keySize, + ASN1ObjectIdentifier cipherAlgorithm, + String cipherAlgorithmShortName) { + this.kdf = new AlgorithmIdentifier(kdf, DERNull.INSTANCE); + this.kdfShortName = kdfShortName; + this.keySize = keySize; + this.cipherAlgorithm = cipherAlgorithm; + this.cipherAlgorithmShortName = cipherAlgorithmShortName; + } + + protected byte[] engineGetEncoded() + { + try + { + return new DERSequence(new ASN1Encodable[] { + PKCSObjectIdentifiers.id_PBES2, + params + }).getEncoded(); + } + catch (IOException e) + { + throw new RuntimeException("Unable to read PBES2 parameters: " + e.toString()); + } + } + + protected byte[] engineGetEncoded( + String format) + { + if (this.isASN1FormatString(format)) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class parameterSpec) + throws InvalidParameterSpecException + { + if (parameterSpec == PBEParameterSpec.class) + { + PBKDF2Params pbeParamSpec = + (PBKDF2Params) params.getKeyDerivationFunc().getParameters(); + byte[] iv = ((ASN1OctetString) params.getEncryptionScheme().getParameters()) + .getOctets(); + return createPBEParameterSpec(pbeParamSpec.getSalt(), + pbeParamSpec.getIterationCount().intValue(), + iv); + } + + throw new InvalidParameterSpecException( + "unknown parameter spec passed to PBES2 parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof PBEParameterSpec)) + { + throw new InvalidParameterSpecException( + "PBEParameterSpec required to initialise PBES2 algorithm parameters"); + } + + PBEParameterSpec pbeSpec = (PBEParameterSpec)paramSpec; + + byte[] iv; + + AlgorithmParameterSpec algorithmParameterSpec = + PBE.Util.getParameterSpecFromPBEParameterSpec(pbeSpec); + if (algorithmParameterSpec instanceof IvParameterSpec) { + iv = ((IvParameterSpec) algorithmParameterSpec).getIV(); + } else { + throw new IllegalArgumentException("Expecting an IV as a parameter"); + } + + this.params = new PBES2Parameters( + new KeyDerivationFunc( + PKCSObjectIdentifiers.id_PBKDF2, + new PBKDF2Params( + pbeSpec.getSalt(), pbeSpec.getIterationCount(), keySize, kdf)), + new EncryptionScheme(cipherAlgorithm, new DEROctetString(iv))); + } + + protected void engineInit( + byte[] params) + throws IOException + { + // Dual of engineGetEncoded() + ASN1Sequence seq = ASN1Sequence.getInstance(ASN1Primitive.fromByteArray(params)); + Enumeration seqObjects = seq.getObjects(); + ASN1ObjectIdentifier id = (ASN1ObjectIdentifier) seqObjects.nextElement(); + if (!id.getId().equals(PKCSObjectIdentifiers.id_PBES2.getId())) { + throw new IllegalArgumentException("Invalid PBES2 parameters"); + } + this.params = PBES2Parameters.getInstance(seqObjects.nextElement()); + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (this.isASN1FormatString(format)) + { + engineInit(params); + return; + } + + throw new IOException("Unknown parameters format in PBES2 parameters object"); + } + + protected String engineToString() + { + return "PBES2 " + kdfShortName + " " + cipherAlgorithmShortName + " Parameters"; + } + } + + public static class PBEWithHmacSHA1AES128AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA1AES128AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA1, + "HmacSHA1", + 16, /* keySize */ + NISTObjectIdentifiers.id_aes128_CBC, + "AES128"); + } + } + + public static class PBEWithHmacSHA224AES128AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA224AES128AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA224, + "HmacSHA224", + 16, /* keySize */ + NISTObjectIdentifiers.id_aes128_CBC, + "AES128"); + } + } + + public static class PBEWithHmacSHA256AES128AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA256AES128AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA256, + "HmacSHA256", + 16, /* keySize */ + NISTObjectIdentifiers.id_aes128_CBC, + "AES128"); + } + } + + public static class PBEWithHmacSHA384AES128AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA384AES128AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA384, + "HmacSHA384", + 16, /* keySize */ + NISTObjectIdentifiers.id_aes128_CBC, + "AES128"); + } + } + + public static class PBEWithHmacSHA512AES128AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA512AES128AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA512, + "HmacSHA512", + 16, /* keySize */ + NISTObjectIdentifiers.id_aes128_CBC, + "AES128"); + } + } + + public static class PBEWithHmacSHA1AES256AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA1AES256AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA1, + "HmacSHA1", + 32, /* keySize */ + NISTObjectIdentifiers.id_aes256_CBC, + "AES256"); + } + } + + public static class PBEWithHmacSHA224AES256AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA224AES256AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA224, + "HmacSHA224", + 32, /* keySize */ + NISTObjectIdentifiers.id_aes256_CBC, + "AES256"); + } + } + + public static class PBEWithHmacSHA256AES256AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA256AES256AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA256, + "HmacSHA256", + 32, /* keySize */ + NISTObjectIdentifiers.id_aes256_CBC, + "AES256"); + } + } + + public static class PBEWithHmacSHA384AES256AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA384AES256AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA384, + "HmacSHA384", + 32, /* keySize */ + NISTObjectIdentifiers.id_aes256_CBC, + "AES256"); + } + } + + public static class PBEWithHmacSHA512AES256AlgorithmParameters + extends BasePBEWithHmacAlgorithmParameters { + public PBEWithHmacSHA512AES256AlgorithmParameters() { + super(PKCSObjectIdentifiers.id_hmacWithSHA512, + "HmacSHA512", + 32, /* keySize */ + NISTObjectIdentifiers.id_aes256_CBC, + "AES256"); + } + } + + + public static class Mappings + extends AlgorithmProvider + { + private static final String PREFIX = PBES2AlgorithmParameters.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + int[] keySizes = { 128, 256 }; + int[] shaVariants = { 1, 224, 256, 384, 512 }; + for (int keySize : keySizes) { + for (int shaVariant : shaVariants) { + provider.addAlgorithm( + "AlgorithmParameters.PBEWithHmacSHA" + shaVariant + "AndAES_" + keySize, + PREFIX + "$PBEWithHmacSHA" + shaVariant + "AES" + keySize + + "AlgorithmParameters"); + } + } + } + } + + /** + * Helper method to create a PBEParameterSpec with a parameter specification via reflection, as + * the constructor became available in Java 1.8 and Bouncycastle is at level 1.5. + */ + private static PBEParameterSpec createPBEParameterSpec( + byte[] salt, int iterationCount, byte[] iv) { + try { + Class<PBEParameterSpec> pbeParameterSpecClass = + (Class<PBEParameterSpec>) PBES2AlgorithmParameters.class.getClassLoader() + .loadClass("javax.crypto.spec.PBEParameterSpec"); + Constructor<PBEParameterSpec> constructor = + pbeParameterSpecClass.getConstructor(new Class[]{ + byte[].class, int.class, AlgorithmParameterSpec.class }); + return constructor.newInstance(salt, iterationCount, new IvParameterSpec(iv)); + } catch (Exception e) { + throw new IllegalStateException( + "Requested creation PBES2 parameters in an SDK that doesn't support them", e); + } + } +} +// END android-added
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java index a4719729..1486d711 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java @@ -152,4 +152,10 @@ public class BCPBEKey { return tryWrong; } + + // BEGIN android-added + public PBEKeySpec getPbeKeySpec() { + return pbeKeySpec; + } + // END android-added } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index fd9b9a9f..fd99c41e 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -20,6 +20,9 @@ import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.interfaces.PBEKey; import javax.crypto.spec.IvParameterSpec; +// BEGIN android-added +import javax.crypto.spec.PBEKeySpec; +// END android-added import javax.crypto.spec.PBEParameterSpec; // BEGIN android-removed // import javax.crypto.spec.RC2ParameterSpec; @@ -638,6 +641,16 @@ public class BaseBlockCipher else if (params instanceof PBEParameterSpec) { pbeSpec = (PBEParameterSpec)params; + // BEGIN android-added + // At this point, k.getParam() == null, so the key hasn't been generated. Recreate + // the BCPBEKey with specs from algorithm parameters as to generate the key. + k = new BCPBEKey(k.getAlgorithm(), k.getOID(), k.getType(), k.getDigest(), + k.getKeySize(), k.getIvSize(), + new PBEKeySpec( + k.getPassword(), pbeSpec.getSalt(), pbeSpec.getIterationCount(), + k.getKeySize()), + null /* CipherParameters */); + // END android-added param = PBE.Util.makePBEParameters(k, params, cipher.getUnderlyingCipher().getAlgorithmName()); } else diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java index d81ad32d..be131f4e 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java @@ -1,9 +1,13 @@ package org.bouncycastle.jcajce.provider.symmetric.util; +import java.lang.reflect.Method; import java.security.InvalidAlgorithmParameterException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.SecretKey; +// BEGIN android-added +import javax.crypto.spec.IvParameterSpec; +// END android-added import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; @@ -243,6 +247,21 @@ public interface PBE if (ivSize != 0) { param = generator.generateDerivedParameters(keySize, ivSize); + // BEGIN ANDROID-ADDED + // 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 } else { @@ -302,6 +321,21 @@ public interface PBE if (pbeKey.getIvSize() != 0) { param = generator.generateDerivedParameters(pbeKey.getKeySize(), pbeKey.getIvSize()); + // BEGIN ANDROID-ADDED + // 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 } else { @@ -457,6 +491,28 @@ public interface PBE return param; } + // BEGIN android-added + /** + * 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 + + private static byte[] convertPassword(int type, PBEKeySpec keySpec) { byte[] key; diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index 00f23e8d..e5463aa0 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -59,7 +59,9 @@ public final class BouncyCastleProvider extends Provider private static final String[] SYMMETRIC_GENERIC = { - "PBEPBKDF2", "PBEPKCS12" + // BEGIN android-changed + // Was: "PBEPBKDF2", "PBEPKCS12" + "PBEPBKDF2", "PBEPKCS12", "PBES2AlgorithmParameters" }; private static final String[] SYMMETRIC_MACS = |