加密流程:
- 从网页源码中取出 key_id 和 public_key,形如:key_id = 34,public_key = ”a6918ee2a5688ff4a860644109c7042384698d70d36844ec1d643e2a66052869“
- 生成随机 AES GCM 密钥,用生成的密钥创建一个 AEG GCM 加密,iv 填充 12 字节 0,additionalData 为秒级时间戳,tag 为 16 字节0
- 用前面从网页里面提取出的公钥使用 Sealed Box 算法(Curve25519 + XSalsa20 + Poly1305)加密 AES GCM KEY 得到加密结果
- 最终将前面的结果拼接起来用标准 base64 编码
拼接结构如下:
[header]
[version]
[key length]
[encrypted AES key (sealed box)]
[auth tag]
[ciphertext]
C++ 实现:
#include<Windows.h>
#include <bcrypt.h>
#include <sodium.h>
#include<iostream>
int hex2bin(const char* hex, unsigned char* out) {
int len = strlen(hex) / 2;
for (int i = 0; i < len; i++) {
sscanf(hex + 2 * i, "%2hhx", &out[i]);
}
return len;
}
int aes_gcm_encrypt(
unsigned char* plaintext, int plen,
unsigned char* aad, int aad_len,
unsigned char* key,
unsigned char* ciphertext,
unsigned char* tag
) {
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
BCryptSetProperty(
hAlg,
BCRYPT_CHAINING_MODE,
(PUCHAR)BCRYPT_CHAIN_MODE_GCM,
sizeof(BCRYPT_CHAIN_MODE_GCM),
0
);
BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, key, 32, 0);
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO info;
BCRYPT_INIT_AUTH_MODE_INFO(info);
unsigned char iv[12] = { 0 };
info.pbNonce = iv;
info.cbNonce = 12;
info.pbAuthData = aad;
info.cbAuthData = aad_len;
info.pbTag = tag;
info.cbTag = 16;
ULONG outlen = 0;
BCryptEncrypt(
hKey,
plaintext,
plen,
&info,
NULL,
0,
ciphertext,
plen,
&outlen,
0
);
BCryptDestroyKey(hKey);
BCryptCloseAlgorithmProvider(hAlg, 0);
return outlen;
}
int encrypt_password(
const char* password,
const char* pubkey_hex,
int key_id,
const char* timestamp,
char* out
) {
if (sodium_init() < 0) return -1;
// 公钥
unsigned char pubkey[32];
hex2bin(pubkey_hex, pubkey);
// 随机 AES key
unsigned char aes_key[32];
randombytes_buf(aes_key, 32);
// AES-GCM
unsigned char ciphertext[512];
unsigned char tag[16];
int clen = aes_gcm_encrypt(
(unsigned char*)password,
strlen(password),
(unsigned char*)timestamp,
strlen(timestamp),
aes_key,
ciphertext,
tag
);
// sealed box 加密 AES key
const int sealed_len = 32 + crypto_box_SEALBYTES;
unsigned char sealed[sealed_len];
crypto_box_seal(sealed, aes_key, 32, pubkey);
// 拼接
unsigned char buffer[1024];
int v = 0;
buffer[v++] = 1; // version
buffer[v++] = key_id;
buffer[v++] = sealed_len & 0xff;
buffer[v++] = (sealed_len >> 8) & 0xff;
memcpy(buffer + v, sealed, sealed_len);
v += sealed_len;
memcpy(buffer + v, tag, 16);
v += 16;
memcpy(buffer + v, ciphertext, clen);
v += clen;
// base64
char b64[2048];
sodium_bin2base64(
b64, sizeof(b64),
buffer, v,
sodium_base64_VARIANT_ORIGINAL
);
sprintf(out, "#PWD_BROWSER:5:%s:%s", timestamp, b64);
return 0;
}
int main() {
char output[2048];
char password[] = "asfdqwfqwf";
char timestamp[] = "1774768284";
char pub_key[] = "a6918ee2a5688ff4a860644109c7042384698d70d36844ec1d643e2a66052869";
int key_id = 34;
encrypt_password(password, pub_key, key_id, timestamp, output);
std::cout << output << std::endl;
return 0;
}
输出:
#PWD_BROWSER:5:1774768284:ASJQAPZgxcyJCInlPkPFgNEH9FNaHFkwTTN9TSEPRdghpQ8K8yMIYAjzBXGRXTw/I/ByuZfWq+NWSrhvrNHUW7C5Q622WHxVqFc46CVWxmsAylPqc7ss6h1kg/C2dVpPcBp8JyUKZGKy1hhWOdc=