Python读取Chrome密码时CryptProtectData报错87的解决方法咨询
报错原因
这个报错触发在win32crypt.CryptUnprotectData(pwd)调用阶段,核心原因有3个:
- Chrome 80及以上版本已经弃用了原生DPAPI加密密码,改为AES-256-GCM加密,密钥单独存放在
Local State配置文件中,旧解密逻辑直接调用CryptUnprotectData会识别密码密文格式错误,抛出参数异常 - 运行脚本时Chrome处于运行状态,会对
Login Data数据库加独占锁,导致读取到的password_value字段残缺,传入解密函数参数不合法 - 当前Windows登录账号和保存Chrome密码时的账号不一致,DPAPI加密绑定用户上下文,跨用户解密会报错
解决方案
- 第一步:运行脚本前完全关闭Chrome,打开任务管理器结束所有
chrome.exe后台进程,解除数据库占用 - 第二步:安装加密依赖库,执行命令:
pip install pycryptodome - 第三步:替换为适配新版Chrome加密逻辑的代码,完整可运行代码如下:
import os import sqlite3 import win32crypt import json import base64 from Crypto.Cipher import AES def get_chrome(): # 读取Chrome加密密钥 local_state_path = os.path.expanduser('~') + r'\AppData\Local\Google\Chrome\User Data\Local State' with open(local_state_path, 'r', encoding='utf-8') as f: local_state = json.loads(f.read()) encrypted_key = base64.b64decode(local_state['os_crypt']['encrypted_key']) # 去掉开头的DPAPI标识前缀 encrypted_key = encrypted_key[5:] # 解密得到AES密钥 key = win32crypt.CryptUnprotectData(encrypted_key, None, None, None, 0)[1] # 复制登录凭证数据库避免占用报错 data_path = os.path.expanduser('~') + r'\AppData\Local\Google\Chrome\User Data\Default\Login Data' temp_db_path = 'temp_login_data.db' with open(data_path, 'rb') as f_in, open(temp_db_path, 'wb') as f_out: f_out.write(f_in.read()) c = sqlite3.connect(temp_db_path) cursor = c.cursor() select_statement = 'SELECT origin_url, username_value, password_value FROM logins' cursor.execute(select_statement) login_data = cursor.fetchall() c.close() os.remove(temp_db_path) # 解密密码并写入文件 filename = 'chromeData.csv' with open(filename, "w+", encoding='utf-8') as f: headers = "links, username, password\n" f.write(headers) for url, user_name, pwd in login_data: # 提取加密参数:去掉v10/v11标识头,取12位IV iv = pwd[3:15] encrypted_pwd = pwd[15:-16] cipher = AES.new(key, AES.MODE_GCM, iv) decrypted_pwd = cipher.decrypt(encrypted_pwd).decode('utf-8') line = f'{url}, {user_name}, {decrypted_pwd}\n' f.write(line) # 自动匹配火狐配置文件夹,无需手动修改路径 firefox_profile_dir = os.path.expanduser('~') + r'\AppData\Roaming\Mozilla\Firefox\Profiles' src = '' for profile in os.listdir(firefox_profile_dir): if profile.endswith('.default-release') or profile.endswith('.default'): src = os.path.join(firefox_profile_dir, profile, 'logins.json') break with open(src, encoding='utf-8') as fd: df = json.load(fd) filename2 = 'firefox.csv' with open(filename2, "w+", encoding='utf-8') as fd: headers = "hostname,encryptedPass, encryptedUser\n" fd.write(headers) for each in df['logins']: a = each['hostname'] b = each['encryptedPassword'] c = each['encryptedUsername'] fd.write(f'{a},{b},{c}\n') if __name__=='__main__': get_chrome()
- 若修改后仍然报错,检查是否使用微软账户切换登录Windows,改用本地账户登录后再运行脚本即可。
内容的提问来源于stack exchange,提问作者MaJoR
相关产品推荐
相关产品推荐

