简体   繁体   中英

How to use Bouncy Castle lightweight API with AES and PBE

I have a block of ciphertext that was created using the JCE algorithim "PBEWithSHA256And256BitAES-CBC-BC". The provider is BouncyCastle. What I'd like to do it decrypt this ciphertext using the BouncyCastle lightweight API. I don't want to use JCE because that requires installing the Unlimited Strength Jurisdiction Policy Files.

Documentation seems to be thin on the ground when it comes to using BC with PBE and AES.

Here's what I have so far. The decryption code runs without exception but returns rubbish.

The encryption code,

String password = "qwerty";
String plainText = "hello world";

byte[] salt = generateSalt();
byte[] cipherText = encrypt(plainText, password.toCharArray(), salt);

private static byte[] generateSalt() throws NoSuchAlgorithmException {
    byte salt[] = new byte[8];
    SecureRandom saltGen = SecureRandom.getInstance("SHA1PRNG");
    saltGen.nextBytes(salt);
    return salt;
}

private static byte[] encrypt(String plainText, char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
    Security.addProvider(new BouncyCastleProvider());

    PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 20);

    PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
    SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC");
    SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

    Cipher encryptionCipher = Cipher.getInstance("PBEWithSHA256And256BitAES-CBC-BC");
    encryptionCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

    return encryptionCipher.doFinal(plainText.getBytes());
}

The decryption code,

byte[] decryptedText = decrypt(cipherText, password.getBytes(), salt);

private static byte[] decrypt(byte[] cipherText, byte[] password, byte[] salt) throws DataLengthException, IllegalStateException, InvalidCipherTextException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
    BlockCipher engine = new AESEngine();
    CBCBlockCipher cipher = new CBCBlockCipher(engine);

    PKCS5S1ParametersGenerator keyGenerator = new PKCS5S1ParametersGenerator(new SHA256Digest());
    keyGenerator.init(password, salt, 20);

    CipherParameters keyParams = keyGenerator.generateDerivedParameters(256);
    cipher.init(false, keyParams);

    byte[] decryptedBytes = new byte[cipherText.length];
    int numBytesCopied = cipher.processBlock(cipherText, 0, decryptedBytes, 0);

    return decryptedBytes;
}

I tried this and it seemed to work. Borrowed heavily from the BC class org.bouncycastle.jce.provider.test.PBETest

private byte[] decryptWithLWCrypto(byte[] cipher, String password, byte[] salt, final  int iterationCount)
        throws Exception
{
    PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(new SHA256Digest());
    char[] passwordChars = password.toCharArray();
    final byte[] pkcs12PasswordBytes = PBEParametersGenerator
            .PKCS12PasswordToBytes(passwordChars);
    pGen.init(pkcs12PasswordBytes, salt, iterationCount);
    CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
    ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(256, 128);
    aesCBC.init(false, aesCBCParams);
    PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC,
            new PKCS7Padding());
    byte[] plainTemp = new byte[aesCipher.getOutputSize(cipher.length)];
    int offset = aesCipher.processBytes(cipher, 0, cipher.length, plainTemp, 0);
    int last = aesCipher.doFinal(plainTemp, offset);
    final byte[] plain = new byte[offset + last];
    System.arraycopy(plainTemp, 0, plain, 0, plain.length);
    return plain;
}

There were a few problems with your decrypt method:

private static byte[] decrypt(final byte[] bytes, final char[] password, final byte[] salt) throws DataLengthException, IllegalStateException, InvalidCipherTextException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {

    final PBEParametersGenerator keyGenerator = new PKCS12ParametersGenerator(new SHA256Digest());
    keyGenerator.init(PKCS12ParametersGenerator.PKCS12PasswordToBytes(password), salt, 20);
    final CipherParameters keyParams = keyGenerator.generateDerivedParameters(256, 128);

    final BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding());
    cipher.init(false, keyParams);

    final byte[] processed = new byte[cipher.getOutputSize(bytes.length)];
    int outputLength = cipher.processBytes(bytes, 0, bytes.length, processed, 0);
    outputLength += cipher.doFinal(processed, outputLength);

    final byte[] results = new byte[outputLength];
    System.arraycopy(processed, 0, results, 0, outputLength);
    return results;
}

The main problems were the way you were carrying out the decryption without using a block cipher and the missing IV size to the generateDerivedParameters method. I saw the first problem pretty quickly, the 2nd one was much less obvious. I only discovered that one through looking at a Bouncy Castle test called PBETest .

It's not trivial to generate the key exactly as the JCE counterparts. I just briefly browsed your code. Found at least one discrepancy. JCE uses PKCS12 generator but you use PKCS5S1.

I am not surprised if there are other differences. You need to compare your code against BC source.

I noticed that your encryption method accepts a password as a char array but the decrypt accepts password as bytes. In Java chars are 16-bit while bytes are 8-bit. This could result in different keys for encrypt/decrypt and perhaps explain the issues with gibberish decrypted results?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM