【问题标题】:How to use Bouncy Castle lightweight API with AES and PBE如何在 AES 和 PBE 中使用 Bouncy Castle 轻量级 API
【发布时间】:2011-02-26 19:15:24
【问题描述】:

我有一个使用 JCE 算法“PBEWithSHA256And256BitAES-CBC-BC”创建的密文块。提供者是 BouncyCastle。我想做的是使用 BouncyCastle 轻量级 API 解密这个密文。我不想使用 JCE,因为这需要安装 Unlimited Strength Jurisdiction Policy Files。

在将 BC 与 PBE 和 AES 结合使用时,文档似乎很少。

这是我目前所拥有的。解密代码运行无异常但返回垃圾。

加密代码,

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());
}

解密代码,

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;
}

【问题讨论】:

    标签: java cryptography aes bouncycastle jce


    【解决方案1】:

    我试过这个,它似乎工作。大量借鉴BC类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;
    }
    

    【讨论】:

    • 行 pGen.generateDerivedParameters(256, 128);这是设置密钥长度吗?
    • @george_h: 256 是密钥长度; 128 是 IV 长度。
    【解决方案2】:

    您的解密方法存在一些问题:

    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;
    }
    

    主要问题是您在不使用分组密码的情况下执行解密的方式以及 generateDerivedParameters 方法缺少 IV 大小。我很快就看到了第一个问题,第二个问题就不那么明显了。我是通过查看名为 PBETest 的 Bouncy Castle 测试才发现的。

    【讨论】:

    • 谢谢拉。您的解决方案完美无缺,但由于 Greg 首先回答,所以我接受他的回答是公平的。
    • 感谢您的反馈。我不知何故错过了 GregS 提供的答案。我有兴趣找出为什么该初始化向量的大小需要为 128 以及人们应该如何知道这是必需的。那是让我挂断电话的部分。
    • 伟大的思想都一样 :) 我知道 AES 是 128 位分组密码,所以 AES 的 IV 将始终是 128 位。我本可以使用 BlockCipher.getBlockSize() * 8 来更通用。
    【解决方案3】:

    生成与 JCE 对应项完全相同的密钥并非易事。我只是简要浏览了您的代码。发现至少一处不一致。 JCE 使用 PKCS12 生成器,但您使用 PKCS5S1。

    如果还有其他差异,我并不感到惊讶。您需要将您的代码与 BC 源代码进行比较。

    【讨论】:

    • 感谢 ZZ。我也尝试使用 PKCS12,但没有任何区别。
    【解决方案4】:

    我注意到您的加密方法接受密码作为字符数组,但解密接受密码作为字节。在 Java 中,字符是 16 位的,而字节是 8 位的。这可能会导致用于加密/解密的密钥不同,并可能解释乱码解密结果的问题?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-12
      • 2011-01-26
      • 1970-01-01
      • 2013-09-16
      • 1970-01-01
      • 2021-10-07
      • 1970-01-01
      相关资源
      最近更新 更多