我的 AES 加密/解密函数不适用于随机 ivec
我很无聊,写了一个 openSSL 的包装器来用更少的工作进行 AES 加密。如果我这样做: http://pastebin.com/V1eqz4jp (ivec = 0)
一切工作正常,但默认的ivec全为0,这存在一些安全问题。既然我无论如何都将数据作为字符串传递回来,我想,为什么不生成一个随机的 ivec 并将其粘贴到前面,当我解密字符串时将其取回呢?由于某种原因它不起作用。
事实上,它几乎可以工作。它似乎解密了字符串的中间,但不是开头 或结尾:
String is: 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
Encrypting..
���l%%1u���B!
�����`pN)�ɶ���[l�ӏ��{�Q�?�2�/�HԵ�y"�=Z�Cu����l%%1u���B!
Decrypting..
String is: �%���G*�5J�0��0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
老实说,我不知道出了什么问题。也许是一些愚蠢的错误,或者也许我遗漏了一些关于 AES 的东西?
这是代码:(经过编辑以将 Steve Jessop 的解决方案纳入我的第一个问题)
/*!
* Simple AES
* Brendan Long
* March 29, 2010
*
* Simplified encryption and decryption using OpenSSL's AES library.
* Remember to compile with -lcrypto and link against the library
* g++ (your stuff) -lcrypto simpleAes.cpp (or simpleAes.o)
*
* Implementation note: Using the default ivec (0) is not secure. For
* the full security that AES offers, use a different
* ivec each time (it does not need to be secret,
* just different.
*
* This code is released into the public domain. Yada yada..
* Read this for details: http://creativecommons.org/licenses/publicdomain/
*
* If for some reason public domain isn't good enough, you may use, alter,
* distribute or do anything else you want with this code with no restrictions.
*/
#include <openssl/aes.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
bool seed = true;
/*!
* Encrypts a string using AES with a 256 bit key
* Note: If the key is less than 32 bytes, it will be null padded.
* If the key is greater than 32 bytes, it will be truncated
* \param in The string to encrypt
* \param key The key to encrypt with
* \return The encrypted data
*/
std::string aes_encrypt(std::string in, std::string key){
// Seed the random number generator once
if(seed){
srand( (unsigned int) time(NULL));
seed = false;
}
// Generate a random ivec
unsigned char ivec[16];
for(int i=0; i<16; i++){
ivec[i] = (unsigned char) rand();
}
// Round up to AES_BLOCK_SIZE
size_t textLength = ((in.length() / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
// Always pad the key to 32 bits.. because we can
if(key.length() < 32){
key.append(32 - key.length(), '\0');
}
// Get some space ready for the output
unsigned char *output = new unsigned char[textLength];
// Generate a key
AES_KEY *aesKey = new AES_KEY;
AES_set_encrypt_key((unsigned char*)key.c_str(), 256, aesKey);
// Encrypt the data
AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length() + 1, aesKey, ivec, AES_ENCRYPT);
// Make the data into a string
std::string ret((char*) output, textLength);
// Add the ivec to the front
ret = std::string((char*)ivec, 16) + ret;
// Clean up
delete output;
delete aesKey;
return ret;
}
/*!
* Decrypts a string using AES with a 256 bit key
* Note: If the key is less than 32 bytes, it will be null padded.
* If the key is greater than 32 bytes, it will be truncated
* \param in The string to decrypt
* \param key The key to decrypt with
* \return The decrypted data
*/
std::string aes_decrypt(std::string in, std::string key){
// Get the ivec from the front
unsigned char ivec[16];
for(int i=0;i<16; i++){
ivec[i] = in[i];
}
in = in.substr(16);
// Always pad the key to 32 bits.. because we can
if(key.length() < 32){
key.append(32 - key.length(), '\0');
}
// Create some space for output
unsigned char *output = new unsigned char[in.length()];
// Generate a key
AES_KEY *aesKey = new AES_KEY;
AES_set_decrypt_key((unsigned char*)key.c_str(), 256, aesKey); // key length is in bits, so 32 * 8 = 256
// Decrypt the data
AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length(), aesKey, ivec, AES_DECRYPT);
// Make the output into a string
std::string ret((char*) output);
// Clean up
delete output;
delete aesKey;
return ret;
}
I was bored and wrote a wrapper around openSSL to do AES encryption with less work. If I do it like this:
http://pastebin.com/V1eqz4jp (ivec = 0)
Everything works fine, but the default ivec is all 0's, which has some security problems. Since I'm passing the data back as a string anyway, I figured, why not generate a random ivec and stick it to the front, the take it back off when I decrypt the string? For some reason it doesn't work though.
Well actually, it almost works. It seems to decrypt the middle of the string, but not the beginning or end:
String is: 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
Encrypting..
���l%%1u���B!
�����`pN)�ɶ���[l�ӏ��{�Q�?�2�/�HԵ�y"�=Z�Cu����l%%1u���B!
Decrypting..
String is: �%���G*�5J�0��0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
I honestly have no idea what's going wrong. Maybe some stupid mistake, or maybe I'm missing something about AES?
Here's the code: (Edited to incorporate Steve Jessop's solution to my first problem)
/*!
* Simple AES
* Brendan Long
* March 29, 2010
*
* Simplified encryption and decryption using OpenSSL's AES library.
* Remember to compile with -lcrypto and link against the library
* g++ (your stuff) -lcrypto simpleAes.cpp (or simpleAes.o)
*
* Implementation note: Using the default ivec (0) is not secure. For
* the full security that AES offers, use a different
* ivec each time (it does not need to be secret,
* just different.
*
* This code is released into the public domain. Yada yada..
* Read this for details: http://creativecommons.org/licenses/publicdomain/
*
* If for some reason public domain isn't good enough, you may use, alter,
* distribute or do anything else you want with this code with no restrictions.
*/
#include <openssl/aes.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
bool seed = true;
/*!
* Encrypts a string using AES with a 256 bit key
* Note: If the key is less than 32 bytes, it will be null padded.
* If the key is greater than 32 bytes, it will be truncated
* \param in The string to encrypt
* \param key The key to encrypt with
* \return The encrypted data
*/
std::string aes_encrypt(std::string in, std::string key){
// Seed the random number generator once
if(seed){
srand( (unsigned int) time(NULL));
seed = false;
}
// Generate a random ivec
unsigned char ivec[16];
for(int i=0; i<16; i++){
ivec[i] = (unsigned char) rand();
}
// Round up to AES_BLOCK_SIZE
size_t textLength = ((in.length() / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
// Always pad the key to 32 bits.. because we can
if(key.length() < 32){
key.append(32 - key.length(), '\0');
}
// Get some space ready for the output
unsigned char *output = new unsigned char[textLength];
// Generate a key
AES_KEY *aesKey = new AES_KEY;
AES_set_encrypt_key((unsigned char*)key.c_str(), 256, aesKey);
// Encrypt the data
AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length() + 1, aesKey, ivec, AES_ENCRYPT);
// Make the data into a string
std::string ret((char*) output, textLength);
// Add the ivec to the front
ret = std::string((char*)ivec, 16) + ret;
// Clean up
delete output;
delete aesKey;
return ret;
}
/*!
* Decrypts a string using AES with a 256 bit key
* Note: If the key is less than 32 bytes, it will be null padded.
* If the key is greater than 32 bytes, it will be truncated
* \param in The string to decrypt
* \param key The key to decrypt with
* \return The decrypted data
*/
std::string aes_decrypt(std::string in, std::string key){
// Get the ivec from the front
unsigned char ivec[16];
for(int i=0;i<16; i++){
ivec[i] = in[i];
}
in = in.substr(16);
// Always pad the key to 32 bits.. because we can
if(key.length() < 32){
key.append(32 - key.length(), '\0');
}
// Create some space for output
unsigned char *output = new unsigned char[in.length()];
// Generate a key
AES_KEY *aesKey = new AES_KEY;
AES_set_decrypt_key((unsigned char*)key.c_str(), 256, aesKey); // key length is in bits, so 32 * 8 = 256
// Decrypt the data
AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length(), aesKey, ivec, AES_DECRYPT);
// Make the output into a string
std::string ret((char*) output);
// Clean up
delete output;
delete aesKey;
return ret;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您应该在加密之前将 ivec[16] 保存到“输出”中。
就是这样......
我还想补充一点,使用 char* 而不是字符串会更简单。
You should save the ivec[16] into 'output' BEFORE encrypting.
That's it...
I'd also like to add that it'll be much simpler to work with char* instead of string.
此行是错误的:
解密的数据没有 nul 终止符,因为您加密了
in.length()
字节。这说明了最后的垃圾,但没有解释开头的垃圾。还可能存在其他问题。This line is wrong:
The decrypted data doesn't have a nul terminator, since you encrypted
in.length()
bytes. That accounts for the garbage at the end, but not the garbage at the beginning. There may be other problems as well.我的一个朋友发现了这个问题。我正在这样做:
ivec
中ivec
加密数据问题是步骤 2 更改了 ivec 的内容。我基本上是在字符串的开头存储随机数。解决方案是添加以下内容:
A friend of mine figured out the problem. I'm doing this:
ivec
ivec
The problem is that step 2 changes the contents of ivec. I was basically storing random numbers at the beginning of my string. The solution was to add this:
通常,您不能将加密阶段的输出视为字符串,除非您执行附加步骤,例如对输出进行 Base 64 编码。任何输出字节都可以是 null。
In general, you cannot treat the output of the encryption stage as a string, unless you perform an additional step, such as Base 64 encoding the output. Any output byte could be a nul.