Instantly share code, notes, and snippets.
Created
January 31, 2021 11:55
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save VonXXGhost/a890b32b4251ed316ad36ec6cb2d95f0 to your computer and use it in GitHub Desktop.
Java Chrome Cookies Reader Utils(Windows)。读取Windows下Chrome的cookies的工具类
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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