Skip to content

Instantly share code, notes, and snippets.

@VonXXGhost
Created January 31, 2021 11:55
Show Gist options
  • Save VonXXGhost/a890b32b4251ed316ad36ec6cb2d95f0 to your computer and use it in GitHub Desktop.
Save VonXXGhost/a890b32b4251ed316ad36ec6cb2d95f0 to your computer and use it in GitHub Desktop.
Java Chrome Cookies Reader Utils(Windows)。读取Windows下Chrome的cookies的工具类
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.jna.platform.win32.Crypt32Util;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
/**
* @author VonXXGhost
* @date 2020/9/8 下午 11:02
*/
@Slf4j
public class CookiesUtils {
/**
* Chrome加密cookie的方式
*/
private enum ChromeEncryptType {
/**
* DPAPI:旧版加密方式 V10、V11:chrome 80后使用的加密方式 UNKNOWN:未知加密方式
*/
DPAPI,
V10,
V11,
UNKNOWN
}
/**
* @param value encrypted_value字段内容
* @return 加密方式
*/
private static ChromeEncryptType getChromeEncryptType(byte[] value) {
if (value[0] == 1 && value[1] == 0 && value[2] == 0 && value[3] == 0) {
return ChromeEncryptType.DPAPI;
} else if (value[0] == 118 && value[1] == 49 && value[2] == 48) {
return ChromeEncryptType.V10;
} else if (value[0] == 118 && value[1] == 49 && value[2] == 49) {
return ChromeEncryptType.V11;
} else {
return ChromeEncryptType.UNKNOWN;
}
}
/**
* 判断加密方式并自动选择对应的方式解密
*
* @param value encrypted_value字段内容
* @param encryptKey V10使用,chrome的加密key
* @return 解密后的字符串
*/
public static String decryptChromeCookie(byte[] value, String encryptKey) {
ChromeEncryptType encryptType = getChromeEncryptType(value);
if (encryptType == ChromeEncryptType.DPAPI) {
return decryptByDpapi(value);
} else if (encryptType == ChromeEncryptType.V10 ||
encryptType == ChromeEncryptType.V11) {
return decryptChromeCookieV10(value, encryptKey);
} else {
throw new RuntimeException("Unsupported encrypt type: " + encryptType);
}
}
/**
* 调用系统DPAPI接口解密
*
* @param value 被DPAPI加密后的值
* @return 解密字符串
*/
private static String decryptByDpapi(byte[] value) {
return new String(decryptByDpapiBytes(value));
}
private static byte[] decryptByDpapiBytes(byte[] value) {
return Crypt32Util.cryptUnprotectData(value);
}
/**
* 从chrome的 Local State 中读取V10解密需要的加密key
*
* @param filePath Local State
* @return 加密key
*/
@Nullable
public static String extractChromeEncryptKey(String filePath) throws IOException {
InputStream inputStream = new FileInputStream(filePath);
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> json = objectMapper.readValue(inputStream, new TypeReference<>() {
});
if (Objects.nonNull(json.get("os_crypt"))) {
return ((Map<String, String>) json.get("os_crypt")).get("encrypted_key");
}
return null;
}
@Nullable
private static String decryptChromeCookieV10(byte[] value, String encryptKey) {
Objects.requireNonNull(encryptKey);
byte[] encryptKeyDecodeSource = Base64.getDecoder().decode(encryptKey);
byte[] encryptKeyDecode = Arrays.copyOfRange(encryptKeyDecodeSource, 5, encryptKeyDecodeSource.length);
byte[] decryptedKey = decryptByDpapiBytes(encryptKeyDecode);
byte[] nonce = Arrays.copyOfRange(value, 3, 15);
byte[] cipherText = Arrays.copyOfRange(value, 15, value.length - 16);
byte[] tag = Arrays.copyOfRange(value, value.length - 16, value.length);
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptedKey, "AES"),
new GCMParameterSpec(16 * Byte.SIZE, nonce));
cipher.update(cipherText);
return new String(
cipher.doFinal(tag),
StandardCharsets.UTF_8
);
} catch (Exception e) {
log.error("Decrypt Error.", e);
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment