Facebook Web 端 PWD_BROWSER 密码加密算法还原

加密流程:

  1. 从网页源码中取出 key_id 和 public_key,形如:key_id = 34,public_key = ”a6918ee2a5688ff4a860644109c7042384698d70d36844ec1d643e2a66052869“
  2. 生成随机 AES GCM 密钥,用生成的密钥创建一个 AEG GCM 加密,iv 填充 12 字节 0,additionalData 为秒级时间戳,tag 为 16 字节0
  3. 用前面从网页里面提取出的公钥使用 Sealed Box 算法(Curve25519 + XSalsa20 + Poly1305)加密 AES GCM KEY 得到加密结果
  4. 最终将前面的结果拼接起来用标准 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=

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注