Ados

a fullstack game worker

0%

Websocket 二进制数据流 AES 加密

Preface

最近在对 protobuf 二进制数据进行aes加密操作,前端用的 crypto-js,服务端用的 openssl 。解密之后的数据老是无法用 protobuf 对应的格式解析。

Cause

经调查发现前端只能对字符串格式的数据进行aes加密,无法对二进制数据进行加密,所以前端对二进制数据做了一些特殊处理。而服务端 char是字符,也是 int8,所以影响不大。

所以前端基本是照抄了这个:

WebSocket 二进制传输 AES加密和解密

Content

想法一:直接将二进制数据转成string

由于前端二进制数据是 uint8array, 一个uint8对应的其实就是一个charcode,那么这个Uint8Array其实是可以直接字符串化的,所以可以先对二进制数据字符化,然后对这个字符串进行加解密,解密之后的数据再以charcode化就可以转成二进制数据了,试验如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var CryptoJS = require("crypto-js");

var uint8arr = new Uint8Array(10);
for(var i = 0; i <= 9; i++)
{
uint8arr[i] = Math.random()*0xff >> 0;
}
console.log("raw:",uint8arr);
console.log("======encrypt===============");
var str = "";
for(var i = 0 ;i <= 9; i++){
str += String.fromCharCode(uint8arr[i]);
}
console.log("toString:",str);
console.log("length:",str.length)
var ciphertext = CryptoJS.AES.encrypt(str,"0000000000000000");
var enctext = ciphertext.toString();
console.log(enctext);
console.log("Length After Encrypto:",enctext.length);
console.log("======decrypt===============");
var rawStr = CryptoJS.AES.decrypt(enctext,"0000000000000000").toString(CryptoJS.enc.Utf8);
console.log("First Descrypt:",rawStr);
console.log("Equal to raw:",rawStr == str);
var rlen = rawStr.length;
var result = new Uint8Array(rlen);
for(var i = 0; i <= 9; i++)
{
result[i] = rawStr.charCodeAt(i);
}
console.log("result:",result);

试验结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS E:\workspace\node\crypto_test> npm start

> crypto_test@1.0.0 start E:\workspace\node\crypto_test
> node index.js

raw: Uint8Array [ 144, 246, 86, 140, 101, 132, 21, 240, 222, 249 ]
======encrypt===============
toString: öVŒe„ðÞù
length: 10
U2FsdGVkX19mC04Dy64U7xF5clPwdAA+qzpBCoFG6h/5msXIimk8lnur9vBDd5mB
Length After Encrypto: 64
======decrypt===============
First Descrypt: öVŒe„ðÞù
Equal to raw: true
result: Uint8Array [ 144, 246, 86, 140, 101, 132, 21, 240, 222, 249 ]

行是行得同,唯一要调查得是:

CryptoJS.AES.decrypt(enctext,”0000000000000000”).toString(CryptoJS.enc.Utf8);

其中 toString 传入的是 CryptoJS.enc.Utf8

但是一个长度为 10 的字节数组加密后变成了一个长度为 64 的字符串,这个就有点可怕了。

想法二:老老实实的反解前端进行的一层封装

Updates

2019-5-29

今天在Go群里看到有大佬使用 CryptoJS 加密 openssl解不了的问题,后面据他调查的结果是:

是因为CryptoJS里面是用的evpkdf(这个鬼东西会生成一个salt 然后用salt来处理iv

openssl 是最近的更新 hash 从 md5 变成了 SHA256 新版要指定-md md5(然鹅我是Liberssl啊

嗯,有时间还是要去研究一下。

我当初调试还是很肤浅的。