4
votes

I want to use AES-128-cbc encryption/decryption algorithm to encrypt/decrypt some data,and use nodejs to encrypt the data and use c to decrypt them.But found that using the same key and IV,the two languages have different encryption results.See follows:

nodes js code:

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 codes:

#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 results:

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

jERcWr8ZMzSJcKPGB7RYAYRpMftlThxyZcjfbFYlU3g=
My name is Harlan Chen!

and the c language results:

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!

Compare the two base64 strings :

jERcWr8ZMzSJcKPGB7RYAYRpMftlThxyZcjfbFYlU3g=
jERcWr8ZMzSJcKPGB7RYAf6kEOmjJgUksDtrttx4r3k=

The first 21 characters are the same,the following ones are different.I don't know why.

1
What is this in JS: var iv16='0000000000000000'; ? Is this a string of '0'characters (==0x20) or an array of '\0' bytes?Gerhardh
Btw your "crypto" isn't secure. The ciphers must be coupled with message authentication code, otherwise they're just a nuisance to an attacker.Antti Haapala
@Gerhardh it is the same as the IV in C, 0x30303030303030303030303030303030. (and btw, 0 is indeed 0x30, not 0x20, which is space...)Antti Haapala
Both. The first rule of crypto is: "you don't roll out your own crypto".Antti Haapala
@HarlanChen cryptopals.com/sets/2 and cryptopals.com/sets/3 for example :)Antti Haapala

1 Answers

4
votes

AES is a block cipher, which means, that you always encrypt full blocks of 128 bit (i.e. 16 byte).

Now if you look at your plaintext "My name is Harlan Chen!", you will notice, that these are 23 bytes. This means, that 9 bytes are still left, which is filled with PKCS#7-Padding (9 bytes of value 09) in the nodejs-case, but with zero-bytes in the C case. Furthermore, I suspect there is a trailing newline in the file.txt with your string, which isn't present in the C example, too.

So, you should check with a hex editor, that your string "My name is Harlan Chen!" without a newline and padded with zero bytes up to 32 bytes is inside your file.txt. This will still not yield exactly the same result, since one full block of padding will be added by nodejs (see PKCS#7-Padding) because one byte is always added as padding. But you can disable the automatic padding in your nodejs-script wih

cipher.setAutoPadding(0);

and

decipher.setAutoPadding(0);

Then you should get the same results. As mentioned above, make sure your file.txt does not contain a newline and is padded to 32 bytes with zero-bytes.

Alternatively, you can change your C program to implement PKCS#7 padding; then you should also get the same results. The string to encrypt would be

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

then