package org.bouncycastle.jce.provider;

import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.spec.PBEParameterSpec;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;

Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0, with a bug affecting 180 bit plus keys - this class is only here to allow smooth migration of the version 0 keystore to version 1. Don't use it (it won't be staying around).

The document this implementation is based on can be found at RSA's PKCS12 Page

/** * Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0, * with a bug affecting 180 bit plus keys - this class is only here to * allow smooth migration of the version 0 keystore to version 1. Don't * use it (it won't be staying around). * <p> * The document this implementation is based on can be found at * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12/index.html> * RSA's PKCS12 Page</a> */
class OldPKCS12ParametersGenerator extends PBEParametersGenerator { public static final int KEY_MATERIAL = 1; public static final int IV_MATERIAL = 2; public static final int MAC_MATERIAL = 3; private Digest digest; private int u; private int v;
Construct a PKCS 12 Parameters generator. This constructor will accept MD5, SHA1, and RIPEMD160.
Params:
  • digest – the digest to be used as the source of derived keys.
Throws:
/** * Construct a PKCS 12 Parameters generator. This constructor will * accept MD5, SHA1, and RIPEMD160. * * @param digest the digest to be used as the source of derived keys. * @exception IllegalArgumentException if an unknown digest is passed in. */
public OldPKCS12ParametersGenerator( Digest digest) { this.digest = digest; if (digest instanceof MD5Digest) { u = 128 / 8; v = 512 / 8; } else if (digest instanceof SHA1Digest) { u = 160 / 8; v = 512 / 8; } else if (digest instanceof RIPEMD160Digest) { u = 160 / 8; v = 512 / 8; } else { throw new IllegalArgumentException("Digest " + digest.getAlgorithmName() + " unsupported"); } }
add a + b + 1, returning the result in a. The a value is treated as a BigInteger of length (b.length * 8) bits. The result is modulo 2^b.length in case of overflow.
/** * add a + b + 1, returning the result in a. The a value is treated * as a BigInteger of length (b.length * 8) bits. The result is * modulo 2^b.length in case of overflow. */
private void adjust( byte[] a, int aOff, byte[] b) { int x = (b[b.length - 1] & 0xff) + (a[aOff + b.length - 1] & 0xff) + 1; a[aOff + b.length - 1] = (byte)x; x >>>= 8; for (int i = b.length - 2; i >= 0; i--) { x += (b[i] & 0xff) + (a[aOff + i] & 0xff); a[aOff + i] = (byte)x; x >>>= 8; } }
generation of a derived key ala PKCS12 V1.0.
/** * generation of a derived key ala PKCS12 V1.0. */
private byte[] generateDerivedKey( int idByte, int n) { byte[] D = new byte[v]; byte[] dKey = new byte[n]; for (int i = 0; i != D.length; i++) { D[i] = (byte)idByte; } byte[] S; if ((salt != null) && (salt.length != 0)) { S = new byte[v * ((salt.length + v - 1) / v)]; for (int i = 0; i != S.length; i++) { S[i] = salt[i % salt.length]; } } else { S = new byte[0]; } byte[] P; if ((password != null) && (password.length != 0)) { P = new byte[v * ((password.length + v - 1) / v)]; for (int i = 0; i != P.length; i++) { P[i] = password[i % password.length]; } } else { P = new byte[0]; } byte[] I = new byte[S.length + P.length]; System.arraycopy(S, 0, I, 0, S.length); System.arraycopy(P, 0, I, S.length, P.length); byte[] B = new byte[v]; int c = (n + u - 1) / u; for (int i = 1; i <= c; i++) { byte[] A = new byte[u]; digest.update(D, 0, D.length); digest.update(I, 0, I.length); digest.doFinal(A, 0); for (int j = 1; j != iterationCount; j++) { digest.update(A, 0, A.length); digest.doFinal(A, 0); } for (int j = 0; j != B.length; j++) { B[i] = A[j % A.length]; } for (int j = 0; j != I.length / v; j++) { adjust(I, j * v, B); } if (i == c) { System.arraycopy(A, 0, dKey, (i - 1) * u, dKey.length - ((i - 1) * u)); } else { System.arraycopy(A, 0, dKey, (i - 1) * u, A.length); } } return dKey; }
Generate a key parameter derived from the password, salt, and iteration count we are currently initialised with.
Params:
  • keySize – the size of the key we want (in bits)
Returns:a KeyParameter object.
/** * Generate a key parameter derived from the password, salt, and iteration * count we are currently initialised with. * * @param keySize the size of the key we want (in bits) * @return a KeyParameter object. */
public CipherParameters generateDerivedParameters( int keySize) { keySize = keySize / 8; byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize); return new KeyParameter(dKey, 0, keySize); }
Generate a key with initialisation vector parameter derived from the password, salt, and iteration count we are currently initialised with.
Params:
  • keySize – the size of the key we want (in bits)
  • ivSize – the size of the iv we want (in bits)
Returns:a ParametersWithIV object.
/** * Generate a key with initialisation vector parameter derived from * the password, salt, and iteration count we are currently initialised * with. * * @param keySize the size of the key we want (in bits) * @param ivSize the size of the iv we want (in bits) * @return a ParametersWithIV object. */
public CipherParameters generateDerivedParameters( int keySize, int ivSize) { keySize = keySize / 8; ivSize = ivSize / 8; byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize); byte[] iv = generateDerivedKey(IV_MATERIAL, ivSize); return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize); }
Generate a key parameter for use with a MAC derived from the password, salt, and iteration count we are currently initialised with.
Params:
  • keySize – the size of the key we want (in bits)
Returns:a KeyParameter object.
/** * Generate a key parameter for use with a MAC derived from the password, * salt, and iteration count we are currently initialised with. * * @param keySize the size of the key we want (in bits) * @return a KeyParameter object. */
public CipherParameters generateDerivedMacParameters( int keySize) { keySize = keySize / 8; byte[] dKey = generateDerivedKey(MAC_MATERIAL, keySize); return new KeyParameter(dKey, 0, keySize); } } public interface BrokenPBE { // // PBE Based encryption constants - by default we do PKCS12 with SHA-1 // static final int MD5 = 0; static final int SHA1 = 1; static final int RIPEMD160 = 2; static final int PKCS5S1 = 0; static final int PKCS5S2 = 1; static final int PKCS12 = 2; static final int OLD_PKCS12 = 3;
uses the appropriate mixer to generate the key and IV if neccessary.
/** * uses the appropriate mixer to generate the key and IV if neccessary. */
static class Util {
a faulty parity routine...
Params:
  • bytes – the byte array to set the parity on.
/** * a faulty parity routine... * * @param bytes the byte array to set the parity on. */
static private void setOddParity( byte[] bytes) { for (int i = 0; i < bytes.length; i++) { int b = bytes[i]; bytes[i] = (byte)((b & 0xfe) | (((b >> 1) ^ (b >> 2) ^ (b >> 3) ^ (b >> 4) ^ (b >> 5) ^ (b >> 6) ^ (b >> 7)) ^ 0x01)); } } static private PBEParametersGenerator makePBEGenerator( int type, int hash) { PBEParametersGenerator generator; if (type == PKCS5S1) { switch (hash) { case MD5: generator = new PKCS5S1ParametersGenerator(new MD5Digest()); break; case SHA1: generator = new PKCS5S1ParametersGenerator(new SHA1Digest()); break; default: throw new IllegalStateException("PKCS5 scheme 1 only supports only MD5 and SHA1."); } } else if (type == PKCS5S2) { generator = new PKCS5S2ParametersGenerator(); } else if (type == OLD_PKCS12) { switch (hash) { case MD5: generator = new OldPKCS12ParametersGenerator(new MD5Digest()); break; case SHA1: generator = new OldPKCS12ParametersGenerator(new SHA1Digest()); break; case RIPEMD160: generator = new OldPKCS12ParametersGenerator(new RIPEMD160Digest()); break; default: throw new IllegalStateException("unknown digest scheme for PBE encryption."); } } else { switch (hash) { case MD5: generator = new PKCS12ParametersGenerator(new MD5Digest()); break; case SHA1: generator = new PKCS12ParametersGenerator(new SHA1Digest()); break; case RIPEMD160: generator = new PKCS12ParametersGenerator(new RIPEMD160Digest()); break; default: throw new IllegalStateException("unknown digest scheme for PBE encryption."); } } return generator; }
construct a key and iv (if neccessary) suitable for use with a Cipher.
/** * construct a key and iv (if neccessary) suitable for use with a * Cipher. */
static CipherParameters makePBEParameters( BCPBEKey pbeKey, AlgorithmParameterSpec spec, int type, int hash, String targetAlgorithm, int keySize, int ivSize) { if ((spec == null) || !(spec instanceof PBEParameterSpec)) { throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); } PBEParameterSpec pbeParam = (PBEParameterSpec)spec; PBEParametersGenerator generator = makePBEGenerator(type, hash); byte[] key = pbeKey.getEncoded(); CipherParameters param; generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); if (ivSize != 0) { param = generator.generateDerivedParameters(keySize, ivSize); } else { param = generator.generateDerivedParameters(keySize); } if (targetAlgorithm.startsWith("DES")) { if (param instanceof ParametersWithIV) { KeyParameter kParam = (KeyParameter)((ParametersWithIV)param).getParameters(); setOddParity(kParam.getKey()); } else { KeyParameter kParam = (KeyParameter)param; setOddParity(kParam.getKey()); } } 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.
/** * 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. */
static CipherParameters makePBEMacParameters( BCPBEKey pbeKey, AlgorithmParameterSpec spec, int type, int hash, int keySize) { if ((spec == null) || !(spec instanceof PBEParameterSpec)) { throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); } PBEParameterSpec pbeParam = (PBEParameterSpec)spec; PBEParametersGenerator generator = makePBEGenerator(type, hash); byte[] key = pbeKey.getEncoded(); CipherParameters param; generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); param = generator.generateDerivedMacParameters(keySize); for (int i = 0; i != key.length; i++) { key[i] = 0; } return param; } } }