解密chrome cookies文件的encrypted_value

1.环境

.windows 8 (x64)
.chrome cookies文件(sqlite 3):%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default\Cookies
 

2.事由

解密chrome cookies数据的场景.
一个后台程序需要从3个平台抓取数据,每个平台的用户验证方式各不相同.利用浏览器登录后导出cookies实现自动登录.
开发一个工具程序,提供给用户,用户在浏览器登录后,运行该工具程序,导出相关网站的cookies,上传给后台服务程序.
浏览器选用chrome.
 

3.关于chrome的cookies

.保存在%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default\Cookies文件中
.该文件是sqlite 3数据库
.encrypted_value是加密后的blob内容
.Windows下加密采用DPAPI,
Decrypting Chrome's cookies on windows
https://stackoverflow.com/questions/24353620/decrypting-chromes-cookies-on-windows
文中有Chromium加密函数源代码.用CryptProtectData加密
.ChromeCookiesView程序可以查看内容
ChromeCookiesView v1.46 
https://nirsoft.net/utils/chrome_cookies_view.html
.浏览器运行期间,其它进程无法打开文件,报以下错误
java.sql.SQLException: [SQLITE_BUSY]  The database file is locked (database is locked)
 

4.Java DPAP

官网
Java Data Protection API
http://jdpapi.sourceforge.net/

下载的包有2个文件:
--jdpapi-java-1.0.jar
--jdpapi-native-1.0.dll
jdpapi-native-1.0.dll是IA 32-bit版本,直接使用报以下错误:
Exception in thread "main" java.lang.UnsatisfiedLinkError: E:\tool\jdpapi\jdpapi-native-1.0.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform  

需要自己编译AMD64-bit版本.
 

获取源代码:

svn checkout https://svn.code.sf.net/p/jdpapi/code/ jdpapi-code
保存目录E:\tool\jdpapi\jdpapi-code(工程主目录,以下目录都是相对主目录)
其中,
-jdpapi\BUILD.txt: 编译说明.
-jdpapi\jdpapi-native\pom.xml:mvn文件

VC编译器采用Visaul Studio 2010,编译x64版本.
<compilerStartOption>增加jni.h,jni_md.h的路径.

<configuration>做以下修改: 

 

	 <envFactoryName>
		org.codehaus.mojo.natives.msvc.MSVC2010x86AMD64EnvFactory
	</envFactoryName>
	<compilerStartOptions>
		<compilerStartOption>/LD /I"C:\Java\jdk1.8.0_91\include" /I"C:\Java\jdk1.8.0_91\include\win32"</compilerStartOption>
	</compilerStartOptions>

	<javahOS>x64</javahOS

 

由于使用msvc2010编译x64位程序,需要设置环境变量(搜索预期的cl.exe)

进入目录c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64>
运行:

vcvars64.bat

执行命令: 

cd jdpapi
mvn clean package assembly:assembly

执行后会生成jdpapi-java-1.0.1.jar.但编译jdpapi-native失败.
 
生成C++头文件:
执行命令:

cd jdpapi\jdpapi-java\target\classes
javah net.sourceforge.jdpapi.DPAPI


当前目录生成文件net_sourceforge_jdpapi_DPAPI.h,把头文件复制到目录:jdpapi\jdpapi-native\src\main\native


执行命令: 

 

cd jdpapi
mvn clean package assembly:assembly


编译成功后,生成以下文件:
-jdpapi\jdpapi-java\target\jdpapi-java-1.0.1.jar
-jdpapi\jdpapi-native\target\jdpapi-native.dll


把这2个文件复制到测试目录下E:\tool\jdpapi

 

 

 

5.实现

采用java实现.
复制Cookies文件:Cookies_copy,用于测试.
读一条特定的cookies记录,对encrypted_value字段进行解密.通过sqlite工具或ChromeCookiesView或直接在浏览器中获取实际内容.用于解密后的比对.
解密利用Java DPAPI,通过JNI方式调用Windows DPAPI


代码如下:

 

 

 

 

package test_cookies;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import net.sourceforge.jdpapi.DataProtector;;


public class TestCookies {
	static Connection conn = null;
	static String dbPath = "C:\\Users\\Think\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\";
	static String dbName = dbPath + "Cookies_copy";

	private final DataProtector protector;
	public TestCookies() {
		this.protector = new DataProtector();
	}

	private String decrypt(byte[] data) {
		return protector.unprotect(data);
	}

	public static void main(String[] args) {
		try {
			TestCookies testCoolies = new TestCookies();
			Class.forName("org.sqlite.JDBC");
			conn = DriverManager.getConnection("jdbc:sqlite:" + dbName, null, null);

			conn.setAutoCommit(false);
			Statement stmt = conn.createStatement();
			stmt.setQueryTimeout(3); 
			String sql = String.format("select * from cookies where host_key like '%%.cainiao.com%%' and name='cna'");

			ResultSet rs = stmt.executeQuery(sql);
			while (rs.next()) {
				String name = rs.getString("name");
				String value = rs.getString("value");
				InputStream inputStream = rs.getBinaryStream("encrypted_value");
				ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
				int ch;
				while ((ch = inputStream.read()) != -1) {
					byteArrayOutputStream.write(ch);
				}
				byte[] b = byteArrayOutputStream.toByteArray();
				byteArrayOutputStream.close();

				System.out.println(String.format("name=%s value=%s encrypted_value=%s", 
						name, value,testCoolies.decrypt(b)));
			}
			rs.close();
			conn.close();
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

	static {
		System.load("e:/tool/jdpapi/jdpapi-native.dll");
	}
}

sqlite-jdbc驱动用3.23.1版本,有的版本会报以下错误:

java.sql.SQLException: not implemented by SQLite JDBC driver

pom.xml增加sqlite-jdbc依赖:

<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.23.1</version>
</dependency>

 

 

 

### 正确实现基于PyCryptodome库的RSA加密与解密 在Python中,`PyCryptodome` 是一个强大的密码学库,支持多种加密算法,其中包括RSA。以下是关于如何正确实现基于 `PyCryptodome` 库的 RSA 加密与解密的具体说明。 #### 密钥生成 为了进行RSA加密和解密操作,首先需要生成一对公私钥。可以使用以下代码来完成: ```python from Crypto.PublicKey import RSA key = RSA.generate(2048) # 生成2048位的RSA密钥对 private_key = key.export_key() # 导出私钥 public_key = key.publickey().export_key() # 导出公钥 ``` 上述代码片段展示了如何生成并导出RSA密钥对[^1]。 #### 使用PKCS1_v1_5填充模式进行加密与解密 下面是一个完整的例子,展示如何利用 `PKCS1_v1_5` 填充模式来进行加密和解密操作: ```python from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 import base64 # 密钥生成部分省略... message = b'Hello, this is a secret message.' # 要加密的消息 # 加载公钥 pubkey = RSA.import_key(public_key) cipher = Cipher_pkcs1_v1_5.new(pubkey) encrypted_message = cipher.encrypt(message) # 将加密后的消息转换为Base64编码以便于传输存储 encoded_encrypted_message = base64.b64encode(encrypted_message).decode('utf-8') print(f"Encrypted Message: {encoded_encrypted_message}") # 解密过程 privkey = RSA.import_key(private_key) decrypt_cipher = Cipher_pkcs1_v1_5.new(privkey) decoded_encrypted_message = base64.b64decode(encoded_encrypted_message.encode('utf-8')) decrypted_message = decrypt_cipher.decrypt(decoded_encrypted_message, None) if decrypted_message: print(f"Decrypted Message: {decrypted_message.decode('utf-8')}") else: print("Failed to decrypt the message.") ``` 此代码实现了基本的加密流程,并且考虑到了可能存在的错误情况处理[^2]。 需要注意的是,在实际应用过程中可能会遇到由于填充方式不同而导致的结果差异问题。这是因为 `PKCS1_v1_5` 的设计允许一定程度上的随机化以增强安全性[^4]。 #### 使用OAEP填充模式进行加密与解密 相比传统的 `PKCS1_v1_5` 方案,推荐优先采用更安全的 OAEP 填充机制。下面是相应的实现示例: ```python from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP import base64 # 密钥生成部分省略... message = b'This is another example of secure encryption.' # 加载公钥 pubkey = RSA.import_key(public_key) oaep_encryptor = PKCS1_OAEP.new(pubkey) encrypted_message = oaep_encryptor.encrypt(message) # Base64 编码 encoded_encrypted_message = base64.b64encode(encrypted_message).decode('utf-8') print(f"Encrypted Message with OAEP: {encoded_encrypted_message}") # 解密过程 privkey = RSA.import_key(private_key) oaep_decryptor = PKCS1_OAEP.new(privkey) decoded_encrypted_message = base64.b64decode(encoded_encrypted_message.encode('utf-8')) decrypted_message = oaep_decryptor.decrypt(decoded_encrypted_message) print(f"Decrypted Message with OAEP: {decrypted_message.decode('utf-8')}") ``` 该段代码演示了更为现代的安全实践——即使用 `PKCS1_OAEP` 进行加密和解密的操作[^3]。 ### 数据长度限制 无论是哪种填充方案,都存在一定的输入数据大小约束条件。具体而言,对于给定模数 \(n\) 和哈希函数 \(H\), 如果采用 OAEP,则最大可接受的数据量通常小于 \((|n|-2*|H|-2)\);而对于 v1.5 则相对简单一些,但仍然受到固定头部结构的影响而有所局限。 ---
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值