MacOS下获取Chrome保存的Cookie

先说结论,MacOS上的Chrome Cookie保存方式为Sqlite,对应的表名为cookies,数据的加密方式为AES + PBKDF2 + SHA1

Cookie存储路径

MacOSChrome Cookie保存方式为Sqlite文件,存储的路径为:

1
~/Library/Application Support/Google/Chrome/Default/Cookies

Cookie表结构

我们通过sqlite读取Cookies文件后,可以查看cookies表结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE TABLE cookies(
creation_utc INTEGER NOT NULL,
host_key TEXT NOT NULL, -- 网站域名
top_frame_site_key TEXT NOT NULL,
name TEXT NOT NULL, -- cookie名称
value TEXT NOT NULL,
encrypted_value BLOB NOT NULL, -- cookie值(加密)
path TEXT NOT NULL,
expires_utc INTEGER NOT NULL,
is_secure INTEGER NOT NULL,
is_httponly INTEGER NOT NULL,
last_access_utc INTEGER NOT NULL,
has_expires INTEGER NOT NULL,
is_persistent INTEGER NOT NULL,
priority INTEGER NOT NULL,
samesite INTEGER NOT NULL,
source_scheme INTEGER NOT NULL,
source_port INTEGER NOT NULL,
last_update_utc INTEGER NOT NULL,
source_type INTEGER NOT NULL,
has_cross_site_ancestor INTEGER NOT NULL
)

其中encrypted_value就是网站的cookie值,但是这个值是加密后的值,我们需要进行解密。

Cookie解密

获取密钥

此处我们可以通过终端命令获取解密密钥,过程中可能需要输入登录密码

1
2
yi@23456 ~ % security find-generic-password -wga Chrome
Exxxxabccsssx==

解密

解密方式为AES,密钥生成方式为PBKDF2,底层哈希使用SHA1,以Java为例,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String salt = "saltysalt"; // 盐
String iv = String.format("%" + 16 + "s", ""); // 长度为16的空字符串
int length = 128; // 长度
String password = "Exxxxabccsssx=="; // 上一步获取的密钥
int iterations = 1003; // 迭代次数

SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), iterations, length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyFactory.generateSecret(keySpec).getEncoded(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);

byte[] cookieBytes = cipher.doFinal(Arrays.copyOfRange(encryptedValueBytes, 3, encryptedValueBytes.length));
System.out.println(new String(cookieBytes)); // 最终的cookie值

完整代码

Java完整示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Arrays;

/**
* 依赖:
* <dependency>
* <groupId>org.xerial</groupId>
* <artifactId>sqlite-jdbc</artifactId>
* <version>3.42.0.0</version>
* </dependency>
*/
public class ChromeCookieTest {

public static void main(String[] args) throws Exception {
Class.forName("org.sqlite.JDBC");

String cookiePath = "本地Cookie路径"; // 本地 Cookies 文件路径
String password = "解密密钥"; // 通过 `security find-generic-password -wga Chrome` 获取

try (Connection con = DriverManager.getConnection("jdbc:sqlite:" + cookiePath);
Statement statement = con.createStatement()) {
ResultSet resultSet = statement.executeQuery("select encrypted_value from cookies where host_key like '%host_name%' and name = 'cookie_name'");

byte[] encryptedValueBytes = (byte[]) resultSet.getObject(1); // cookie加密值

String salt = "saltysalt"; // 盐
String iv = String.format("%" + 16 + "s", ""); // 长度为16的空字符串
int length = 128; // 长度
int iterations = 1003; // 迭代次数

SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), iterations, length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyFactory.generateSecret(keySpec).getEncoded(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);

byte[] cookieBytes = cipher.doFinal(Arrays.copyOfRange(encryptedValueBytes, 3, encryptedValueBytes.length));
System.out.println(new String(cookieBytes));
}
}
}

参考

https://gist.github.com/dacort/bd6a5116224c594b14db