解密chrome cookies文件的encrypted
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 jdpapimvn clean package assembly:assembly
执行后会生成jdpapi-java-1.0.1.jar.但编译jdpapi-native失败.
生成C++头文件:
执行命令:
cd jdpapi\jdpapi-java\target\classesjavah net.sourceforge.jdpapi.DPAPI
当前目录生成文件net_sourceforge_jdpapi_DPAPI.h,把头文件复制到目录:jdpapi\jdpapi-native\src\main\native
执行命令:
cd jdpapimvn 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>