英文:
How to use MySQL AES_DECRYPT to decrypt data encrypted with BouncyCastles PBEWITHSHA256AND256BITAES-CBC-BC Algorithm
问题
我们使用BouncyCastle库中的PBEWITHSHA256AND256BITAES-CBC-BC算法来加密数据,并将加密结果存储在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个字节(Base64解码后)是盐(salt),但我不确定初始化向量(IV)存储在剩余的加密文本数据中的哪里。
如果我们可以解析出IV、盐和加密值,那么就可以使用外部工具解密数据。
MySQL中AES的示例用法如下:
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, @key_str, @init_vector);
输出:
A Test String!
我想知道如何解析BouncyCastle的加密文本以获取其组成部分,以及如何使用盐生成正确的密钥哈希,并结合MySql中指定的迭代次数来解密数据。
非常感谢您的帮助!
英文:
We use BouncyCastle PBEWITHSHA256AND256BITAES-CBC-BC to encrypt data with our java application and store the encrypted result in a MySql Database.
Example Code:
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);
Output:
Input:A Test String! cypher:DhCSPbCWcZ76TUD/dDeGczlHbI9dQJyB2lKAiL7dDEk= plain:A Test String!
The cypher string above is a base64 encoded string which we store in our database.
I would now like to attempt to decrypt the cypher string stored in our database using the AES utilities provided by MySql.
I am trying to understand how the BC provider concatenates the encrypted data so that I can split it up and recreate the required parameters to enable me to decrypt the data with other tools - in this case MySql's AES_DECRYPT function.
Inspecting the code I can see that the first 16bytes of the cypher text (when base 64 decoded) is the salt, I am unsure where the init vector (IV) is stored in the remainder of the cypher text data.
If we can parse out the IV, salt and encrypted value from the string, then it should be possible to use external tools to decrypt the data.
A sample Mysql AES usage is as follows:
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,@key_str,@init_vector);
Output:
A Test String!
I would like to know how to parse the BouncyCastle cypher text to obtain its component parts, and also how to use the salt to generate the correct key hash with the number of iterations specified for use by Mysql to decrypt the data.
Any help much appreciated!
答案1
得分: 1
这个答案不是代码的解决方案,但会帮助你找到代码。
首先:你并没有直接使用Bouncy Castle进行加密/解密,当然了,密码是用作加密/解密提供者的。
完成整个加密/解密的库是JASYPT,在这里我们可以找到你问题的答案。
我的研究基于GitHub上的https://github.com/jboss-fuse/jasypt/tree/master/jasypt/src/main/java/org/jasypt/encryption/pbe,我从"StandardPBEStringEncryptor.java"开始:
由于我们正在尝试理解所使用的加密,我发现
// 将在内部使用的StandardPBEByteEncryptor。
private final StandardPBEByteEncryptor byteEncryptor;
稍后是加密方法:
...
// StandardPBEByteEncryptor在执行其工作。
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编码返回encryptedMessage。
让我们看一下基类"StandardPBEByteEncryptor.java":
搜索ivInUse:
// 用于加密和解密的初始化向量。
private byte[] ivInUse = null;
...
// 初始化初始化向量
this.ivInUse = new byte[algorithmBlockSize];
这意味着我们有一个静态的16字节长度的IV(AES的块长度),用"x00"填充。
盐:
默认的盐长度设置为8,但是当使用块密码时,盐长度等于密码块大小(对于AES为16):
// 为所选算法设置的盐大小等于算法的块大小(如果它是块算法)。
final int algorithmBlockSize = this.encryptCipher.getBlockSize();
if (algorithmBlockSize > 0) {
this.saltSizeBytes = algorithmBlockSize;
}
盐是使用盐生成器生成的,在加密后,它与密文以salt|encryptedMessage
的形式连接在一起:
加密:
...
// 最后,我们构建一个数组,其中包含未加密的盐和加密结果。
// 只有在所使用的盐生成器指定要这样做时才会这样做。
if (this.saltGenerator.includePlainSaltInEncryptionResults()) {
// 在加密结果之前插入未经散列的盐
return CommonUtils.appendArrays(salt, encryptedMessage);
}
迭代次数由初始化给出(1000)。
解决的最后一部分是密码初始化的算法,当使用OpenJava 11时,我找到:
PBEWithHmacSHA256AndAES_256
这在(希望如此)CBC模式下工作。
英文:
This answer is not a solution in code but will help you in finding the code.
First: you are NOT using Bouncy Castle to en-/decrypt directly - of course the cipher is used as provider for the en-/decryption.
The library that does the complete en-/decryption is JASYPT and here we can find answers for your question.
Base for my research is the GitHub https://github.com/jboss-fuse/jasypt/tree/master/jasypt/src/main/java/org/jasypt/encryption/pbe and I'm starting with "StandardPBEStringEncryptor.java":
As we are trying to understand the encryption in use I found
// The StandardPBEByteEncryptor that will be internally used.
private final StandardPBEByteEncryptor byteEncryptor;
and later the encrypt-method:
...
// 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);
}
As you get a Base64-encoded string this class just returns the encryptedMessage in Base64-encding.
Let's see the base class "StandardPBEByteEncryptor.java":
Searching for ivInUse:
// Initialization Vector to be used for encryption and decryption.
private byte[] ivInUse = null;
...
// Initialize Initialization Vector
this.ivInUse = new byte[algorithmBlockSize];
That means we do have a static IV of 16 bytes length (blocklength for AES) filled with "x00".
salt:
The DefaultSaltLength is set to 8 but when using a block cipher the salt length equals to the cipher block size (for 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;
}
The salt is generated with the saltGenerator and after encryption it is concatenated with the ciphertext in the form salt|encryptedMessage
:
encrypt:
...
// 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);
}
The number of iterations is given by initialization (1000).
Last part to solve is the algorithm for the cipher-init and when using OpenJava 11 I find:
PBEWithHmacSHA256AndAES_256
that (hopefully) works in CBC-mode.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论