summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergio Giro <sgiro@google.com>2016-10-13 14:07:05 +0100
committerSergio Giro <sgiro@google.com>2016-10-20 15:20:19 +0000
commit64fe4be2b2c98b1231314c785da2c050eccfe99b (patch)
tree8ffc9669ccf64a7db706034ead8838c546722292
parent48408df5b5da2549208b2a1af26099f80b181f95 (diff)
downloadbouncycastle-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
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java114
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBES2AlgorithmParameters.java330
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java56
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java4
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 =