问题描述
我们使用BouncyCastle PBEWITHSHA256AND256BITAES-CBC-BC通过我们的Java应用程序对数据进行加密,并将加密后的结果存储在MysqL数据库中。
示例代码:
StandardPBEStringEncryptor configurationEncryptor = new StandardPBEStringEncryptor();
configurationEncryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC");
configurationEncryptor.setProviderName("BC");
configurationEncryptor.setSaltGenerator(new RandomSaltGenerator());
configurationEncryptor.setKeyObtentionIterations(1000);
configurationEncryptor.setPassword("aTestPassword");
String input = "A Test String!";
String cypherText = configurationEncryptor.encrypt(input);
String plainText = configurationEncryptor.decrypt(cypherText);
System.out.println("Input:" + input + " cypher:" + cypherText + " plain:" + plainText);
输出:
Input:A Test String! cypher:DhCSPbCWcZ76TUD/dDeGczlHbI9dQJyB2lKAiL7dDEk= plain:A Test String!
上面的密码字符串是我们存储在数据库中的base64编码的字符串。
我现在想尝试使用MysqL提供的AES实用程序解密存储在我们数据库中的密码字符串。
我试图了解BC提供程序如何连接加密的数据,以便可以将其拆分并重新创建所需的参数,以使我能够使用其他工具(在本例中为MysqL的AES_DECRYPT函数)解密数据。
检查代码,我可以看到密文的前16个字节(解码基数为64时是盐),我不确定初始化向量(IV)在其余的密文数据中的存储位置。
如果我们可以从字符串中解析出IV,盐和加密值,那么应该可以使用外部工具来解密数据。
SET block_encryption_mode = 'aes-256-cbc';
SET @key_str = SHA2('aTestPassword',256);
SET @init_vector = RANDOM_BYTES(16);
SET @crypt_str = AES_ENCRYPT('A Test String!',@key_str,@init_vector);
SELECT AES_DECRYPT(@crypt_str,@init_vector);
输出:
A Test String!
我想知道如何解析BouncyCastle密码文本以获取其组成部分,以及如何使用salt以指定的迭代次数生成正确的密钥哈希,供MysqL用于解密数据。
任何帮助,不胜感激!
解决方法
此答案不是代码中的解决方案,但可以帮助您找到代码。
首先:您不是在使用Bouncy Castle直接加密/解密-当然,密码被用作加密/解密的提供者。
进行完整加密/解密的库为 JASYPT ,我们可以在此处找到您问题的答案。
我研究的基础是GitHub https://github.com/jboss-fuse/jasypt/tree/master/jasypt/src/main/java/org/jasypt/encryption/pbe,我从“ StandardPBEStringEncryptor.java”开始:
当我们试图了解我发现的使用中的加密方式
// The StandardPBEByteEncryptor that will be internally used.
private final StandardPBEByteEncryptor byteEncryptor;
,然后是加密方法:
...
// The StandardPBEByteEncryptor does its job.
byte[] encryptedMessage = this.byteEncryptor.encrypt(messageBytes);
...
if (this.stringOutputTypeBase64) {
encryptedMessage = this.base64.encode(encryptedMessage);
result = new String(encryptedMessage,ENCRYPTED_MESSAGE_CHARSET);
} else {
result = CommonUtils.toHexadecimal(encryptedMessage);
}
当您获得Base64编码的字符串时,该类将仅以Base64编码返回加密消息。
让我们看一下基类“ StandardPBEByteEncryptor.java”:
搜索 iv 使用中:
// Initialization Vector to be used for encryption and decryption.
private byte[] ivInUse = null;
...
// Initialize Initialization Vector
this.ivInUse = new byte[algorithmBlockSize];
这意味着我们确实具有 16字节长的静态IV (AES的块长度),填充有“ x00” 。
盐:
DefaultSaltLength设置为8,但是当使用分组密码时,盐长度等于密码块大小(对于AES 16):
// The salt size for the chosen algorithm is set to be equal
// to the algorithm's block size (if it is a block algorithm).
final int algorithmBlockSize = this.encryptCipher.getBlockSize();
if (algorithmBlockSize > 0) {
this.saltSizeBytes = algorithmBlockSize;
}
盐是由saltGenerator生成的,加密后将其与密文并置,格式为salt|encryptedMessage
:
加密:
...
// Finally we build an array containing both the unencrypted salt
// and the result of the encryption. This is done only
// if the salt generator we are using specifies to do so.
if (this.saltGenerator.includePlainSaltInEncryptionResults()) {
// Insert unhashed salt before the encryption result
return CommonUtils.appendArrays(salt,encryptedMessage);
}
迭代次数是通过初始化(1000)给出的。
最后要解决的是cipher-init的算法,使用OpenJava 11时,我发现:
PBEWithHmacSHA256AndAES_256
(希望)在CBC模式下工作。