简单加解密数据算法与储存结构设计:ssir coder
在线应用 ateditors 的 WebRTC 实时通信功能中需要分发诸如信令、ICE 服务的用户名密码等需要保密的编码数据。ssir coder即为此用途而设计。
此加密算法使用 PBKDF2 生成 AES-GCM 密钥。然后对保密信息进行加密,使用 hex 或 base64 编码后进行分发、接收解码解密使用。
使用 ArrayBuffer 编码的简单数据格式如下:
AAAA BBBB CCCC DDDD EEEE
AAAA: 32 字节随机填充数据
BBBB CCCC: 双 32 字节随机 AES 初始变量 iv
DDDD: 32 字节随机 PBKDF2 参数 salt
EEEE: 保密信息使用 AES 加密算法加密后的密文加密实现过程如下:
步骤 操作
1 调用 crypto.getRandomValues 生成固定大小的 PBKDF2 参数 salt
2 设定 hash 算法固定为 SHA-512
3 调用 crypto.subtle.digest 对前述确定的 salt 使用 hash 算法生成 PBKDF2 的密钥数据
4 使用 crypto.subtle.importKey 导入 PBKDF2 密钥数据生成 PBKDF2 初始密钥材料
5 设定 iterations 为一个固定的足够大的自然数
6 设定 AesDerivedKeyParams 密钥长度 length 为固定的 256
7 使用 PBKDF2 密钥材料与 salt/iterations/hash/length 调用 crypto.subtle.deriveKey 生成 AES-GCM 密钥
8 调用 crypto.getRandomValues 生成固定大小的 AesGcmParams 初始变量 iv
9 使用前述密钥与初始变量调用 crypto.subtle.encrypt 加密保密信息为密文 cipher
a 使用 crypto.getRandomValues 生成足够大小的 Uint8Array 以保存前述所有信息数据
b 跳过起始大小的随机填充数据依次填充 iv/salt/cipher 进 Uint8Array中解密实现过程基本就是前述加密实现过程的逆操作:
步骤 操作
1 提取 salt
2 设定 hash 算法固定为 SHA-512
3 调用 crypto.subtle.digest 对提取的 salt 使用 hash 算法生成 PBKDF2 的密钥数据
4 调用 crypto.subtle.importKey 导入 PBKDF2 密钥数据生成 PBKDF2 初始密钥材料
5 设定 iterations 为一个固定的足够大的自然数
6 设定 AesDerivedKeyParams 密钥长度 length 为固定的 256
7 使用 PBKDF2 密钥材料与 salt/iterations/hash/length 调用 crypto.subtle.deriveKey 生成 AES-GCM 密钥
8 提取 AesGcmParams 初始变量 iv
9 提取密文 cipher
a 使用前述密钥与初始变量调用 crypto.subtle.decrypt 解密密文 cipher 为保密信息数据实现的样例代码:
// The ArrayBuffer format of wrap coder:
// AAAA BBBB CCCC DDDD EEEE
// AAAA: 32 bytes random for padding.
// BBBB CCCC: double 32 bytes random AES iv.
// DDDD: 32 bytes random PBKDF2 salt.
// EEEE: AES encryption data of secure message.
export const wrapcoder = async (secureBuffer: ArrayBuffer) => {
let wrap = new Uint8Array(keySize);
if (secureBuffer) {
const keySize = 32;
const salt = crypto.getRandomValues(new Uint8Array(keySize));
const hash = 'SHA-512';
const keyData = await crypto.subtle.digest(hash, salt);
const keyMaterial = await crypto.subtle.importKey(
"raw",
keyData,
{
name: "PBKDF2"
},
false,
[ "deriveKey" ]
);
const iterations = 225519;
const keyLength = keySize * 8;
const wrappingKey = await crypto.subtle.deriveKey(
{
"name": "PBKDF2",
salt,
iterations,
hash
},
keyMaterial,
{
"name": "AES-GCM",
"length": keyLength,
},
true,
[ "encrypt" ]
);
const iv = crypto.getRandomValues(new Uint8Array(keySize * 2));
const cipher = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv
},
wrappingKey,
secureBuffer
);
// AAAA BBBB CCCC DDDD EEEE(dynamic length)
wrap = crypto.getRandomValues(new Uint8Array(keySize * 4 + cipher.byteLength));
// BBBB CCCC: double 32 bytes random AES iv.
wrap.set(iv, keySize);
// DDDD: 32 bytes random PBKDF2 salt.
wrap.set(salt, keySize * 3);
// EEEE: AES encryption data of secure message.
wrap.set(new Uint8Array(cipher), keySize * 4);
}
return wrap;
}
export const unwrapcoder = async (wrappedBuffer: ArrayBuffer) => {
const buffer = new Uint8Array(wrappedBuffer);
const keySize = 32;
const salt = buffer.subarray(keySize * 3, keySize * 4);
const hash = 'SHA-512';
const keyData = await crypto.subtle.digest(hash, salt);
const keyMaterial = await crypto.subtle.importKey(
"raw",
keyData,
{
name: "PBKDF2"
},
false,
[ "deriveBits", "deriveKey" ]
);
const iterations = 225519;
const keyLength = keySize * 8;
const unwrappingKey = await crypto.subtle.deriveKey(
{
"name": "PBKDF2",
salt,
iterations,
hash
},
keyMaterial,
{
"name": "AES-GCM",
"length": keyLength,
},
true,
[ "decrypt" ]
);
const iv = buffer.subarray(keySize, keySize * 3);
const cipher = buffer.subarray(keySize * 4);
const secureBuffer = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv
},
unwrappingKey,
cipher
);
return secureBuffer;
}
应用 ateditors 实时通信保密数据编码 coder 如下:
eyJpY2UiOnsidXJsIjoic3R1bjphbmZsZXh0LmNvbTo1MTIxNSJ9LCJsYWJlbCI6InRlc3QiLCJzaWduYWxpbmciOnsiZ3JvdXAiOiJmaXJlZm94IiwidXJsIjoid3NzOi8vYW5mbGV4dC5jb206NTEyMTcifSwic3NpciI6IjdCT09PWXNUN0VvTE4tY05VSUlZdTZUcFNOQ3ZWVFZtZ2dCSE5DeVY0Tjd6dE1VOXJ4TlJYeWl2LUJtZTlBMzg3WHljYUNSU1d0RWRKNXRBUlY3UFkxZkhEYlV3UW9sNVBLZXg0WHo0anZ4QnRxRGNmSlFDVEh2RnhDWmprdGc0bk9qWGhOdkxjN252cjIwdWtFeFBhWkFhVFhsYjFpVGlLV0RCekZZQzZJTXVHVTVQOXo0MVdaSF83SVZLRFZjWkJfalhDRjVzMmxIQnNDNEZRVUxLbjcyTDVGTmJjZlRGUmxhalFDc0o1eVBtM2pBSnVLTHAtVERLM0UyQmh3by1jUUN5UWd5ME1UdDIxN0YwWW16T3RmWEd4SlgxcC1GTW1CcmctcEFXbFp1ZnBnQld4c0hVUmZSeXAtNTZuMmN0VXl6Z2lrQkZRQ0dhQnUxc0ZEZnowaHVyd0EifQ粘贴以上 coder 到 ateditors 实时通信功能 option 的 code 字段,点击 Apply coder 按钮解码填充参数字段。

然后点击 Connect signaling 按钮。连接信令服务器后,就可以呼叫在线的群组成员进行 WebRTC 通信。

连接信令服务器后选择在线的群组成员:

选择到可用的在线成员后,点击 call 按键进行呼叫:

呼叫成功后,可以进行文字通信与文件传输:

29