AES-128-cbc加密何时分别使用nodejs和c,使用相同的密钥和IV,但结果不同
我想用AES-128-cbc加密/解密算法对一些数据进行加密/解密,用nodejs对数据进行加密,用c进行解密,但发现使用相同的密钥和IV,两种语言的加密结果不同,见下图: 节点js代码:AES-128-cbc加密何时分别使用nodejs和c,使用相同的密钥和IV,但结果不同,c,node.js,encryption,aes,C,Node.js,Encryption,Aes,我想用AES-128-cbc加密/解密算法对一些数据进行加密/解密,用nodejs对数据进行加密,用c进行解密,但发现使用相同的密钥和IV,两种语言的加密结果不同,见下图: 节点js代码: var crypto = require('crypto'); var encrypt = function (key, iv, data) { var cipher = crypto.createCipheriv('aes-128-cbc', key, iv); var crypted =
var crypto = require('crypto');
var encrypt = function (key, iv, data) {
var cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
var crypted = cipher.update(data, 'utf8', 'binary');
var cbase64 = new Buffer(crypted, 'binary').toString('base64');
//console.log(crypted);
//console.log(cbase64);
crypted += cipher.final('binary');
//console.log("hahahaaaaaa:"+crypted.toString('hex'));
crypted = new Buffer(crypted, 'binary').toString('base64');
//var c16 = new Buffer(crypted, 'binary').toString(16);
//console.log(crypted);
//console.log(c16);
return crypted;
};
var decrypt = function (key, iv, crypted) {
crypted = new Buffer(crypted, 'base64').toString('binary');
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
var decoded = decipher.update(crypted, 'binary', 'utf8');
//console.log(decoded);
decoded += decipher.final('utf8');
//console.log(decoded);
return decoded;
};
var key='ABCDEFGHIJKLMNOP';
//var iv = new Buffer(crypto.randomBytes(16));
//var iv16 = iv.toString('hex').slice(0,16);
var iv16='0000000000000000';
var fs = require('fs');
fs.readFile('file.txt','utf8',function(err,data){
console.log(data);
var encrypted = encrypt(key,iv16,data);
console.log(encrypted);
var decrypted = decrypt(key,iv16,encrypted);
console.log(decrypted);
fs.writeFile('encrypted.txt',encrypted,function(err){});
});
c代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#define AES_BITS 128
#define MSG_LEN 128
int base64_encode(char *in_str, int in_len, char *out_str)
{
BIO *b64, *bio;
BUF_MEM *bptr = NULL;
size_t size = 0;
if (in_str == NULL || out_str == NULL)
return -1;
b64 = BIO_new(BIO_f_base64());
bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);
BIO_write(bio, in_str, in_len);
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bptr);
memcpy(out_str, bptr->data, bptr->length);
out_str[bptr->length] = '\0';
size = bptr->length;
BIO_free_all(bio);
return size;
}
int aes_encrypt(char* in, char* key, char* out)//, int olen)
{
if(!in || !key || !out) return 0;
unsigned char iv[16];
for(int i=0; i<16; ++i)
iv[i]='0';
//printf("size:%d",AES_BLOCK_SIZE);
//unsigned char *iv = (unsigned char *)"0123456789ABCDEF";
printf("iv: %s\n",iv);
AES_KEY aes;
if(AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
{
return 0;
}
int len=strlen(in);
AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_ENCRYPT);
return 1;
}
int aes_decrypt(char* in, char* key, char* out)
{
if(!in || !key || !out) return 0;
unsigned char iv[16];
for(int i=0; i<16; ++i)
iv[i]='0';
//unsigned char *iv = (unsigned char *)"0123456789ABCDEF";
AES_KEY aes;
if(AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0)
{
return 0;
}
int len=strlen(in);
AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
return 1;
}
int main(int argc,char *argv[])
{
char sourceStringTemp[MSG_LEN];
char dstStringTemp[MSG_LEN];
char dstStringTemp_base64[MSG_LEN];
memset((char*)sourceStringTemp, 0 ,MSG_LEN);
memset((char*)dstStringTemp, 0 ,MSG_LEN);
strcpy((char*)sourceStringTemp, "My name is Harlan Chen!");
//strcpy((char*)sourceStringTemp, argv[1]);
char key[AES_BLOCK_SIZE]={0};
int i;
for(i = 0; i < 16; i++)
{
key[i] = 'A' + i;
}
printf("keys:%s\n",key);
if(!aes_encrypt(sourceStringTemp,key,dstStringTemp))
{
printf("encrypt error\n");
return -1;
}
/*To Base64 encrypted data */
base64_encode(dstStringTemp, strlen(dstStringTemp),dstStringTemp_base64);
printf("Base64 Encrypted data: %s\n",dstStringTemp_base64);
printf("encrypted:%s\n",dstStringTemp);
printf("enc %lu:",strlen((char*)dstStringTemp));
for(i= 0;dstStringTemp[i];i+=1){
printf("%x",(unsigned char)dstStringTemp[i]);
}
memset((char*)sourceStringTemp, 0 ,MSG_LEN);
if(!aes_decrypt(dstStringTemp,key,sourceStringTemp))
{
printf("decrypt error\n");
return -1;
}
printf("\n");
printf("dec %lu:",strlen((char*)sourceStringTemp));
printf("%s\n",sourceStringTemp);
//for(i= 0;sourceStringTemp[i];i+=1){
// printf("%x",(unsigned char)sourceStringTemp[i]);
//}
printf("\n");
return 0;
}
c语言的结果是:
bogon:AES_128_encryption zexu$ ./a.out
keys:ABCDEFGHIJKLMNOP
iv: 0000000000000000
Base64 Encrypted data: jERcWr8ZMzSJcKPGB7RYAf6kEOmjJgUksDtrttx4r3k=
encrypted:?D\Z?34?p???X???&$?;k??x?y
enc 32:8c445c5abf1933348970a3c67b4581fea410e9a326524b03b6bb6dc78af79
dec 23:My name is Harlan Chen!
比较两个base64字符串:
jERcWr8ZMzSJcKPGB7RYAYRpMftlThxyZcjfbFYlU3g=
jERcWr8ZMzSJcKPGB7RYAf6kEOmjJgUksDtrttx4r3k=
前21个字符相同,下面的字符不同。我不知道为什么。AES是一种分组密码,这意味着您总是加密128位(即16字节)的完整块
现在,如果你看你的纯文本“我的名字是Harlan Chen!”,你会注意到,这是23个字节。这意味着,还剩下9个字节,在nodejs情况下用PKCS#7-Padding(9个字节的值09
)填充,但在C情况下为零字节。此外,我怀疑file.txt中有一个带有字符串的尾随换行符,这在C示例中也不存在
因此,您应该使用十六进制编辑器检查您的字符串“My name is Harlan Chen!”是否在file.txt中,该字符串没有换行符且填充了零字节,最多32字节。
这仍然不会产生完全相同的结果,因为nodejs将添加一个完整的填充块(请参见PKCS#7-padding),因为始终添加一个字节作为填充。但是您可以使用以下命令禁用nodejs脚本中的自动填充
cipher.setAutoPadding(0);
及
那么你应该得到同样的结果。如上所述,请确保file.txt不包含换行符,并用零字节填充到32字节
或者,您可以更改C程序以实现PKCS#7填充;那么你也应该得到同样的结果。要加密的字符串将是
"My name is Harlan Chen!\x09\x09\x09\x09\x09\x09\x09\x09\x09"
那么这在JS中是什么:
var iv16='0000000000000000'代码>?这是由'0'
字符(=0x20)组成的字符串还是由'\0'
字节组成的数组?顺便说一句,您的“加密”不安全。密码必须与消息身份验证代码耦合,否则它们只会对攻击者造成滋扰。@Gerhardh它与C中的IV相同,0x30303030
。(顺便说一句,0
实际上是0x30
,而不是0x20
,即空格…)两者都是。加密的第一条规则是:“你不推出你自己的加密”。@HarlanChen和例如:)我添加填充,结果是一样的。一个问题。你的解决方案意味着当我使用nodejs加密消息时,我得到了jercwr8zmzzjckpkp7ryayrpmftlthxyzjfbfylu3g=,但当使用c语言解密消息时,我会得到“我叫陈哈兰\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09”,那么我如何知道填充物的长度@Ctx@HarlanChen只要看一下消息的最后一个字节,它总是填充长度,因为填充字节都反映了这个长度。一般的解决方案是使用PKCS#7(有时称为PKCS#5)。
decipher.setAutoPadding(0);
"My name is Harlan Chen!\x09\x09\x09\x09\x09\x09\x09\x09\x09"