【问题标题】:When do AES-128-cbc encryption using nodejs and c separately,using the same key and IV but have different results何时分别使用nodejs和c进行AES-128-cbc加密,使用相同的密钥和IV但结果不同
【发布时间】:2018-03-27 17:26:34
【问题描述】:

我想使用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 = 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;
}

nodejs 结果:

bogon:AES_128_encryption zexu$ node encrypt.js 
My name is Harlan Chen!

jERcWr8ZMzSJcKPGB7RYAYRpMftlThxyZcjfbFYlU3g=
My name is Harlan Chen!

以及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个字符一样,后面的不一样,不知道为什么。

【问题讨论】:

  • JS 中这是什么:var iv16='0000000000000000';?这是'0'characters (==0x20) 的字符串还是'\0' 字节的数组?
  • 顺便说一句,您的“加密”并不安全。密码必须与消息验证码相结合,否则它们只会对攻击者造成滋扰。
  • @Gerhardh 它与 C 中的 IV 相同,0x30303030303030303030303030303030。 (顺便说一句,0 确实是0x30,而不是0x20,这是空格......)
  • 两者。加密的第一条规则是:“你不会推出自己的加密”。
  • @HarlanChen cryptopals.com/sets/2cryptopals.com/sets/3 例如:)

标签: c node.js encryption aes


【解决方案1】:

AES 是一个 block 密码,这意味着您始终加密 128 位(即 16 字节)的完整块。

现在,如果您查看明文“我的名字是 Harlan Chen!”,您会注意到这些是 23 个字节。这意味着,仍然剩下 9 个字节,在 nodejs 的情况下用 PKCS#7-Padding(9 个字节的值 09)填充,但在 C 的情况下用零字节填充。此外,我怀疑 file.txt 中有一个带有您的字符串的尾随换行符,这在 C 示例中也不存在。

因此,您应该使用十六进制编辑器检查您的字符串“我的名字是 Harlan Chen!”没有换行符并用零字节填充最多 32 个字节在您的 file.txt 中。 这仍然不会产生完全相同的结果,因为 nodejs 将添加一个完整的填充块(请参阅 PKCS#7-Padding),因为总是添加一个字节作为填充。但是你可以在你的 nodejs-script 中禁用自动填充

cipher.setAutoPadding(0);

decipher.setAutoPadding(0);

那么你应该得到相同的结果。如上所述,请确保您的 file.txt 不包含换行符,并且使用零字节填充到 32 个字节。

或者,您可以更改您的 C 程序以实现 PKCS#7 填充;那么你也应该得到相同的结果。要加密的字符串是

"My name is Harlan Chen!\x09\x09\x09\x09\x09\x09\x09\x09\x09"

然后

【讨论】:

  • 我添加了paddings,结果是一样的。
  • 一个问题。你的解决方法是,当我使用nodejs加密msg时,我得到jERcWr8ZMzSJcKPGB7RYAYRpMftlThxyZcjfbFYlU3g=,但是当使用c语言解密msg时,我会得到“我的名字是Harlan Chen!\ x09\x09\x09\x09\x09\x09\x09\x09\x09",那我怎么知道填充长度?@Ctx
  • @HarlanChen 看看消息的最后一个字节,它总是填充长度,因为填充字节 all 反映了这个长度
  • 一般的解决方案是使用PKCS#7(有时称为PKCS#5)。
猜你喜欢
  • 1970-01-01
  • 2018-11-28
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 2022-04-29
  • 1970-01-01
  • 1970-01-01
  • 2013-08-11
相关资源
最近更新 更多