C - tiny-aes-c 和 Javascript CryptoJS 互操作性
C - tiny-aes-c and Javascript CryptoJS interoperability
使用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;
}
代码加密消息并将结果与预期输出进行比较。代码运行正常,输出如下:
idx encrypted expected
[0] 17 17
[1] 8d 8d
[2] c3 c3
[3] a1 a1
[4] 56 56
[5] 34 34
我有另一个服务,一个使用 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);
使用 CryptoJS 对同一消息 + secret 的加密结果是:U2FsdGVkX1/TAYUIFnXzC76zb+sd8ol+2DfKCkwITfY=
(JS Fiddle) 并且在每个 运行.
上发生变化
更新二:
感谢@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;
}
加密的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());
解密结果:a47172dfe151c7
,不是预期结果"message"。
我错过了什么?
你在做两件事
- 加密
- 转换为 base64
接收后,必须按照发送前应用的相反顺序执行这两个操作的相反操作
- 从 base64 转换
- 解密
你也可以交换步骤的顺序,但是必须在发送端和接收端交换顺序。
还要确保双方的密钥格式相同。
您应该更仔细地阅读 CryptoJS 文档。默认情况下,它使用 CBC 模式进行加密,因此您应该更改您的微型 AES 实现以使用它。
CryptoJS supports the following modes:
- CBC (the default)
另请注意,CryptoJS 默认启用填充,而 tiny-AES 根本没有。所以messages必须是16的倍数。(或者你可以手动使用自己的padding实现)
No padding is provided so for CBC and ECB all buffers should be mutiples of 16 bytes. For padding PKCS7 is recommendable.
然后,请注意 CryptoJS 通过密钥大小自动选择 AES 变体:
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the variant by the size of the key you pass in. If you use a passphrase, then it will generate a 256-bit key.
因此,您必须在微型 AES 代码中考虑所有这些因素。
感谢@MDTech.us_MAN ,我找到了一个解决方案,在修复模式和填充之后,不同之处在于我在 JS 端解析秘密的方式。在以下示例中,机密被解析为 HEX 字符串:
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
使用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;
}
代码加密消息并将结果与预期输出进行比较。代码运行正常,输出如下:
idx encrypted expected
[0] 17 17
[1] 8d 8d
[2] c3 c3
[3] a1 a1
[4] 56 56
[5] 34 34
我有另一个服务,一个使用 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);
使用 CryptoJS 对同一消息 + secret 的加密结果是:U2FsdGVkX1/TAYUIFnXzC76zb+sd8ol+2DfKCkwITfY=
(JS Fiddle) 并且在每个 运行.
更新二:
感谢@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;
}
加密的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());
解密结果:a47172dfe151c7
,不是预期结果"message"。
我错过了什么?
你在做两件事
- 加密
- 转换为 base64
接收后,必须按照发送前应用的相反顺序执行这两个操作的相反操作
- 从 base64 转换
- 解密
你也可以交换步骤的顺序,但是必须在发送端和接收端交换顺序。
还要确保双方的密钥格式相同。
您应该更仔细地阅读 CryptoJS 文档。默认情况下,它使用 CBC 模式进行加密,因此您应该更改您的微型 AES 实现以使用它。
CryptoJS supports the following modes:
- CBC (the default)
另请注意,CryptoJS 默认启用填充,而 tiny-AES 根本没有。所以messages必须是16的倍数。(或者你可以手动使用自己的padding实现)
No padding is provided so for CBC and ECB all buffers should be mutiples of 16 bytes. For padding PKCS7 is recommendable.
然后,请注意 CryptoJS 通过密钥大小自动选择 AES 变体:
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the variant by the size of the key you pass in. If you use a passphrase, then it will generate a 256-bit key.
因此,您必须在微型 AES 代码中考虑所有这些因素。
感谢@MDTech.us_MAN
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