这是用来做什么的?
定期导出密码管理器的密钥加密备份,发送到邮箱。
所有操作在浏览器本地进行,无服务器参与。
以前搞丢过bitwarden的两步验证,只能删掉账号。
定期导出管理器的密钥
将公钥放到链接里面,方便每次备份,不用每次输入短语
带入参数
通过页面的一对密钥对 解密hash里面的data,解密成一个json
私钥 yNmVrcoS5D4xMTvjAPSkZe57HZqPZoIUxznm+SqWKFo=
公钥 dTj41nmwoLcguLpM9AntyKgg67xx6K4UAxc27CLIcFw=
json结构
{
pubkey: string
salt?: string
ver?: string
kdfHash?: string
kdfIterations?: number
type?: 'phrase' | 'pubkey'
private?: string
}
| 字段 | 说明 | 其他 |
|---|---|---|
| pubkey | 公钥,用于加密和验证 | |
| salt | 随机 salt,用于 PBKDF2 派生密钥 | 可选 |
| ver | KDF 版本,如 "2.0" | 可选 |
| kdfHash | PBKDF2 哈希算法,如 "SHA-512" | 可选 |
| kdfIterations | PBKDF2 迭代次数,如 210000 | 可选 |
| type | 密钥类型:'phrase'(密码短语派生)或 'pubkey'(直接公钥) | 可选 |
| private | 私钥(Base64),保存后可免密码短语解密 | 可选,需用户确认 |
格式说明
结构
| 1字节 | 1字节 | 2字节 | 2字节 | 2字节 | IV | MAC | 随机公钥 | 加密数据 |
|---|---|---|---|---|---|---|---|---|
下面short都是小端模式
第1字节 格式标志
- 旧格式 (Blake2b):
- 4:先gzip后加密 (Blake2b)
- 5:直接加密 (Blake2b)
- 新格式 (SHA-512): 使用位标志
- bit3 (0x08): 固定为1,表示新格式
- bit2 (0x04): 固定为1
- bit1 (0x02): 固定为0,表示 AES-GCM
- bit0 (0x01): 压缩标志,0=先gzip后加密,1=直接加密
- 示例: 0x0C (12) = 新格式+GCM+压缩, 0x0D (13) = 新格式+GCM+不压缩
- 旧格式 (Blake2b):
第2字节: 保留 (0)
- 3-4字节的short值表示 iv的长度 ,新格式12,旧格式16
- 5-6字节的short值表示 mac校验hash的长度 32
- 7-8字节 字节表示临时公钥长度 ,32
- IV // 随机生成
- MAC ,//最后计算生成
- tmpPubKey //随机生成公钥私钥,私钥(tmpSecKey)使用后丢弃
- EncryptData
过程
生成临时公钥私钥 tmpSecKey , tmpPubKey
sharedX(32 byte)= publickKey • tmpSecKey
buffer[96];
copy sharedX => buffer[0..31]
/**
* 下面公钥按照小端模式比较,先比较高位置,再比较低位
*/
if tmpPubKey > publickKey
copy publickKey => buffer[32...63]
copy tmpPubKey => buffer[64...95]
else
copy publickKey => buffer[32...63]
copy tmpPubKey => buffer[64...95]
end
根据格式选择hash算法:
【旧格式 (format=0)】
利用hash算法 blake2b 生成64字节长度digest
digest = blake2b(buffer)
【新格式 (format=1)】
利用hash算法 SHA-512 生成64字节长度digest
digest = sha512(buffer)
其中 digest[0..31] 作为 AES-GCM 的密钥 aesKey,使用IV 一起加密密钥生成 加密数据 EncryptData(密文自带16字节tag)
其中 digest[32,63]作为计算mac的密钥 HKEY,输出32字节数据
【旧格式 MAC计算】
采用blake2b,指定输出32字节,key = HKEY
mac = blake2b(IV + tmpPubKey + EncryptData, key=HKEY)
【新格式 MAC计算】
采用HMAC-SHA-512,截取前32字节
mac = hmac_sha512(HKEY, IV + tmpPubKey + EncryptData)[0..31]
兼容性
- 解码时自动识别格式:通过第1字节判断
- byte0 = 4 或 5:旧格式 (Blake2b)
- byte0 的 bit3 (0x08) = 1:新格式 (SHA-512)
- 其他值:不支持,抛出异常
- 编码时默认使用新格式:format 参数默认值为 1 (SHA-512)
- 向后兼容:可以解密旧格式数据,但新加密默认使用 SHA-512
根据密码短语生成密钥
步骤
- 根据书签 ver 与是否包含 salt 决定 key:
- ver=2.0(新格式)且 hash 内有 salt:key = 密码短语(不拼接 prefix)
- 旧格式(无 ver,或无 salt):key = prefix(如果有) + 密码短语
- PBKDF2(key) 生成32 字作为私钥, secKey = PBKDF2(key)
- 根据私钥生成 密钥对
PBKDF2 参数
| PBKDF2参数 | value |
|---|---|
| salt | 新书签:每次生成书签时随机生成 33 字节 Base64 编码的 salt,存储在书签链接 hash 中。 旧书签:固定值(The California sea lion ...) |
| ver=1.0(旧书签)iteration / hash | 123456 / SHA-256 |
| ver=2.0(新书签)iteration / hash | 210000 / SHA-512 |
| outLen | 256bit |