/* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.pbe; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.pkcs.PBES2Parameters; import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.PBEParametersGenerator; import org.bouncycastle.crypto.engines.RC532Engine; import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; import org.bouncycastle.crypto.modes.CBCBlockCipher; 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.crypto.params.RC5Parameters; /** * Implements the PBES2 encryption scheme defined in PKCS#5v2. * * @author Middleware Services * @version $Revision: 2744 $ */ public class PBES2EncryptionScheme extends AbstractEncryptionScheme { /** Size of derived key in bits. */ private int keyLength; /** * Creates a new instance with the given parameters. * * @param params PBES2 parameters describing the key derivation function and encryption scheme. * @param password Password used to derive key. */ public PBES2EncryptionScheme(final PBES2Parameters params, final char[] password) { final PBKDF2Params kdfParams = PBKDF2Params.getInstance(params.getKeyDerivationFunc().getParameters()); final byte[] salt = kdfParams.getSalt(); final int iterations = kdfParams.getIterationCount().intValue(); if (kdfParams.getKeyLength() != null) { keyLength = kdfParams.getKeyLength().intValue() * 8; } final PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(); generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password), salt, iterations); initCipher(generator, params.getEncryptionScheme()); } /** * Initializes the block cipher and sets up its initialization parameters. * * @param generator Derived key generator. * @param scheme PKCS#5 encryption scheme. */ private void initCipher( final PKCS5S2ParametersGenerator generator, final org.bouncycastle.asn1.pkcs.EncryptionScheme scheme) { final PBES2Algorithm alg = PBES2Algorithm.fromOid(scheme.getAlgorithm().getId()); if (keyLength == 0) { keyLength = alg.getKeySize(); } byte[] iv = null; CipherParameters cipherParameters = generator.generateDerivedParameters(keyLength); switch (alg) { case RC2: setCipher(alg.getCipherSpec().newInstance()); final ASN1Sequence rc2Params = ASN1Sequence.getInstance(scheme.getParameters()); if (rc2Params.size() > 1) { cipherParameters = new RC2Parameters( ((KeyParameter) cipherParameters).getKey(), ASN1Integer.getInstance(rc2Params.getObjectAt(0)).getValue().intValue()); iv = ASN1OctetString.getInstance(rc2Params.getObjectAt(0)).getOctets(); } break; case RC5: final ASN1Sequence rc5Params = ASN1Sequence.getInstance(scheme.getParameters()); final int rounds = ASN1Integer.getInstance(rc5Params.getObjectAt(1)).getValue().intValue(); final int blockSize = ASN1Integer.getInstance(rc5Params.getObjectAt(2)).getValue().intValue(); if (blockSize == 32) { setCipher(new PaddedBufferedBlockCipher(new CBCBlockCipher(new RC532Engine()), new PKCS7Padding())); } cipherParameters = new RC5Parameters(((KeyParameter) cipherParameters).getKey(), rounds); if (rc5Params.size() > 3) { iv = ASN1OctetString.getInstance(rc5Params.getObjectAt(3)).getOctets(); } break; default: setCipher(alg.getCipherSpec().newInstance()); iv = ASN1OctetString.getInstance(scheme.getParameters()).getOctets(); } if (iv != null) { cipherParameters = new ParametersWithIV(cipherParameters, iv); } setCipherParameters(cipherParameters); } }