C - tiny-aes-c和Javascript CryptoJS互操作性

Shl*_*rtz 8 javascript c aes node.js cryptojs

使用tiny-aes-c.考虑以下C代码:

int main(int argc, char const *argv[])
{
    uint8_t key[6] = { 's','e','c','r','e','t' };
    uint8_t iv[16]  = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };

    uint8_t in[6]  = { 'm','e','s','a','g','e'};

    uint8_t out[6] = {0x17, 0x8d, 0xc3, 0xa1, 0x56, 0x34};
    struct AES_ctx ctx;

    AES_init_ctx_iv(&ctx, key, iv);
    AES_CTR_xcrypt_buffer(&ctx, in, 6);    

    printf("idx\t encrypted\t expected");
    for(int i=0 ; i<6 ; i++){
        printf("\n[%i]\t %.2x\t\t %.2x" , i , in[i], out[i]);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

代码对消息进行加密,并将结果与​​预期输出进行比较.代码工作正常,输出如下:

idx      encrypted       expected
[0]      17              17
[1]      8d              8d
[2]      c3              c3
[3]      a1              a1
[4]      56              56
[5]      34              34
Run Code Online (Sandbox Code Playgroud)

我有另一个服务,一个使用CryptoJS的NodeJS服务器.
我的问题是:如何转换C结果({0x17, 0x8d, 0xc3, 0xa1, 0x56, 0x34}),以便它能匹配CryptoJS可以处理的内容?


编辑: 详细说明.出于本讨论的目的,C结果通过网络传输,因此应将其转换为String.据我所知,CryptoJS使用base64作为其AES方法的输入,解密为以后可以转换为纯文本的字节:

var bytes  = CryptoJS.AES.decrypt(BASE_64_STRING, SECRET);
var plaintext = bytes.toString(CryptoJS.enc.Utf8);
Run Code Online (Sandbox Code Playgroud)

与CryptoJS相同的消息+秘密的加密结果是:U2FsdGVkX1/TAYUIFnXzC76zb+sd8ol+2DfKCkwITfY=(JS Fiddle)和每次运行时的更改.

更新2:
感谢@ MDTech.us_MAN回答我对JS和C代码进行了一些更改,但我仍然缺少一个谜题.

C:

int main(int argc, char const *argv[])
{
    uint8_t key[16] = { 's','e','c','r','e','t','s','e','c','r','e','t','1','2','3','4' };
    uint8_t iv[16]  = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
    uint8_t in[7]  = { 'm','e','s','s','a','g','e'};

    struct AES_ctx ctx;

    AES_init_ctx_iv(&ctx, key, iv);
    AES_CTR_xcrypt_buffer(&ctx, in, 7);

    printf("Encrypted: ");
    for(int i=0 ; i<7 ; i++){
        printf("%.2x" , in[i]);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

加密的HEX字符串C输出:cba9d5bc84113c,当转换为Base64结果时:y6nVvIQRPA==

在JS方面,我明确地使用没有填充的CTR模式,并启动(希望)相同的iv如下:

const CryptoJS = require("crypto-js");
let iv = CryptoJS.enc.Hex.parse('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); // 16 Bytes (same as the C code)
let message = CryptoJS.AES.decrypt("y6nVvIQRPA==", "secretsecret1234", { iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding });
console.log(message.toString());
Run Code Online (Sandbox Code Playgroud)

解密的结果:a47172dfe151c7而不是预期的结果"消息".

我错过了什么?

MDT*_*MAN 6

您应该仔细阅读CryptoJS文档.默认情况下,它使用CBC模式进行加密,因此您应该更改您的tiny-AES实现以使用它.

CryptoJS支持以下模式:

  • CBC(默认)

另请注意,CryptoJS默认启用填充,而tiny-AES根本没有填充.因此,消息必须是16的倍数.(或者您可以手动使用自己的填充实现)

没有提供填充,因此对于CBC和ECB,所有缓冲区应该是16字节的多个.对于填充PKCS7是值得推荐的.

然后,请注意CryptoJS按键大小自动选择AES变体:

CryptoJS支持AES-128,AES-192和AES-256.它将根据您传入的密钥的大小选择变体.如果使用密码,则它将生成256位密钥.

因此,您必须在微型AES代码中考虑所有这些因素.


Shl*_*rtz 2

感谢@MDTech.us_MAN和这个堆栈溢出问题,我找到了一个解决方案,在修复模式和填充后,不同之处在于我在JS端解析秘密的方式。在以下示例中,秘密被解析为十六进制字符串:

const CryptoJS = require("crypto-js");
let iv = CryptoJS.enc.Hex.parse('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); // 16 Bytes
let secret = CryptoJS.enc.Hex.parse('73656372657473656372657431323334'); // 16 Bytes == "secretsecret1234"
let message = CryptoJS.AES.decrypt("y6nVvIQRPA==", secret, { iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding });
console.log(message.toString(CryptoJS.enc.Utf8)); // -> message
Run Code Online (Sandbox Code Playgroud)