Reading and Inserting Chrome Cookies Java Reading and Inserting Chrome Cookies Java google-chrome google-chrome

Reading and Inserting Chrome Cookies Java


I spent a long time on this myself. Here are some classes you can use to decrypt cookies for Chrome on Mac, Windows, and Linux. I didn't implement inserting cookies, but I imagine you have most of everything you need with this code to add that functionality.

It requires some third party libraries (JNI Windows encryption wrappers and SQLite) so I am linking to the Maven project I am using the code in if you need to see the code in context.

import java.io.BufferedReader;import java.io.File;import java.io.IOException;import java.io.InputStreamReader;import java.net.InetAddress;import java.net.UnknownHostException;import java.nio.file.Files;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Arrays;import java.util.Date;import java.util.HashSet;import java.util.Set;import javax.crypto.Cipher;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.PBEKeySpec;import javax.crypto.spec.SecretKeySpec;import com.sun.jna.platform.win32.Crypt32Util;public class Test {    public static void main(String[] args) throws IOException {        ChromeBrowser chrome = new ChromeBrowser();        Set<Cookie> cookies = chrome.getCookiesForDomain("stackoverflow.com");        for(Cookie cookie : cookies){            System.out.println(cookie.toString());        }    }    public static abstract class Cookie {        protected String name;        protected byte[] encryptedValue;        protected Date expires;        protected String path;        protected String domain;        protected boolean secure;        protected boolean httpOnly;        protected File cookieStore;        public Cookie(String name, byte[] encryptedValue, Date expires, String path, String domain, boolean secure, boolean httpOnly, File cookieStore) {            this.name = name;            this.encryptedValue = encryptedValue;            this.expires = expires;            this.path = path;            this.domain = domain;            this.secure = secure;            this.httpOnly = httpOnly;            this.cookieStore = cookieStore;        }        public String getName() {            return name;        }        public byte[] getEncryptedValue() {            return encryptedValue;        }        public Date getExpires() {            return expires;        }        public String getPath() {            return path;        }        public String getDomain() {            return domain;        }        public boolean isSecure() {            return secure;        }        public boolean isHttpOnly() {            return httpOnly;        }        public File getCookieStore(){            return cookieStore;        }        public abstract boolean isDecrypted();    }    public static class DecryptedCookie extends Cookie {        private String decryptedValue;        public DecryptedCookie(String name, byte[] encryptedValue, String decryptedValue, Date expires, String path, String domain, boolean secure, boolean httpOnly, File cookieStore) {            super(name, encryptedValue, expires, path, domain, secure, httpOnly, cookieStore);            this.decryptedValue = decryptedValue;        }        public String getDecryptedValue(){            return decryptedValue;        }        @Override        public boolean isDecrypted() {            return true;        }        @Override        public String toString() {            return "Cookie [name=" + name + ", value=" + decryptedValue + "]";        }    }    public static class EncryptedCookie extends Cookie {        public EncryptedCookie(String name, byte[] encryptedValue, Date expires, String path, String domain, boolean secure, boolean httpOnly, File cookieStore) {            super(name, encryptedValue, expires, path, domain, secure, httpOnly, cookieStore);        }        @Override        public boolean isDecrypted() {            return false;        }        @Override        public String toString() {            return "Cookie [name=" + name + " (encrypted)]";        }    }    public static class OS {        public static String getOsArchitecture() {            return System.getProperty("os.arch");        }        public static String getOperatingSystem() {            return System.getProperty("os.name");        }        public static String getOperatingSystemVersion() {            return System.getProperty("os.version");        }        public static String getIP() throws UnknownHostException {            InetAddress ip = InetAddress.getLocalHost();            return ip.getHostAddress();        }        public static String getHostname() throws UnknownHostException {            return InetAddress.getLocalHost().getHostName();         }        public static boolean isWindows() {            return (getOperatingSystem().toLowerCase().indexOf("win") >= 0);        }        public static boolean isMac() {            return (getOperatingSystem().toLowerCase().indexOf("mac") >= 0);        }        public static boolean isLinux() {            return (getOperatingSystem().toLowerCase().indexOf("nix") >= 0 || getOperatingSystem().toLowerCase().indexOf("nux") >= 0 || getOperatingSystem().toLowerCase().indexOf("aix") > 0 );        }        public static boolean isSolaris() {            return (getOperatingSystem().toLowerCase().indexOf("sunos") >= 0);        }    }    public static abstract class Browser {        /**         * A file that should be used to make a temporary copy of the browser's cookie store         */        protected File cookieStoreCopy = new File(".cookies.db");        /**         * Returns all cookies         */        public Set<Cookie> getCookies() {            HashSet<Cookie> cookies = new HashSet<Cookie>();            for(File cookieStore : getCookieStores()){                cookies.addAll(processCookies(cookieStore, null));            }            return cookies;        }        /**         * Returns cookies for a given domain         */        public Set<Cookie> getCookiesForDomain(String domain) {            HashSet<Cookie> cookies = new HashSet<Cookie>();            for(File cookieStore : getCookieStores()){                cookies.addAll(processCookies(cookieStore, domain));            }            return cookies;        }        /**         * Returns a set of cookie store locations         * @return         */        protected abstract Set<File> getCookieStores();        /**         * Processes all cookies in the cookie store for a given domain or all         * domains if domainFilter is null         *          * @param cookieStore         * @param domainFilter         * @return         */        protected abstract Set<Cookie> processCookies(File cookieStore, String domainFilter);        /**         * Decrypts an encrypted cookie         * @param encryptedCookie         * @return         */        protected abstract DecryptedCookie decrypt(EncryptedCookie encryptedCookie);    }    /**     * An implementation of Chrome cookie decryption logic for Mac, Windows, and Linux installs      *      * References:      * 1) http://n8henrie.com/2014/05/decrypt-chrome-cookies-with-python/     * 2) https://github.com/markushuber/ssnoob     *      * @author Ben Holland     */    public static class ChromeBrowser extends Browser {        private String chromeKeyringPassword = null;        /**         * Returns a set of cookie store locations         * @return         */        @Override        protected Set<File> getCookieStores() {            HashSet<File> cookieStores = new HashSet<File>();            // pre Win7            cookieStores.add(new File(System.getProperty("user.home") + "\\Application Data\\Google\\Chrome\\User Data\\Default\\Cookies"));            // Win 7+            cookieStores.add(new File(System.getProperty("user.home") + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies"));            // Mac            cookieStores.add(new File(System.getProperty("user.home") + "/Library/Application Support/Google/Chrome/Default/Cookies"));            // Linux            cookieStores.add(new File(System.getProperty("user.home") + "/.config/chromium/Default/Cookies"));            return cookieStores;        }        /**         * Processes all cookies in the cookie store for a given domain or all         * domains if domainFilter is null         *          * @param cookieStore         * @param domainFilter         * @return         */        @Override        protected Set<Cookie> processCookies(File cookieStore, String domainFilter) {            HashSet<Cookie> cookies = new HashSet<Cookie>();            if(cookieStore.exists()){                Connection connection = null;                try {                    cookieStoreCopy.delete();                    Files.copy(cookieStore.toPath(), cookieStoreCopy.toPath());                    // load the sqlite-JDBC driver using the current class loader                    Class.forName("org.sqlite.JDBC");                    // create a database connection                    connection = DriverManager.getConnection("jdbc:sqlite:" + cookieStoreCopy.getAbsolutePath());                    Statement statement = connection.createStatement();                    statement.setQueryTimeout(30); // set timeout to 30 seconds                    ResultSet result = null;                    if(domainFilter == null || domainFilter.isEmpty()){                        result = statement.executeQuery("select * from cookies");                    } else {                        result = statement.executeQuery("select * from cookies where host_key like \"%" + domainFilter + "%\"");                    }                    while (result.next()) {                        String name = result.getString("name");                        byte[] encryptedBytes = result.getBytes("encrypted_value");                        String path = result.getString("path");                        String domain = result.getString("host_key");                        boolean secure = result.getBoolean("secure");                        boolean httpOnly = result.getBoolean("httponly");                        Date expires = result.getDate("expires_utc");                        EncryptedCookie encryptedCookie = new EncryptedCookie(name,                                                                              encryptedBytes,                                                                               expires,                                                                              path,                                                                               domain,                                                                               secure,                                                                              httpOnly,                                                                               cookieStore);                        DecryptedCookie decryptedCookie = decrypt(encryptedCookie);                        if(decryptedCookie != null){                            cookies.add(decryptedCookie);                        } else {                            cookies.add(encryptedCookie);                        }                        cookieStoreCopy.delete();                    }                } catch (Exception e) {                    e.printStackTrace();                    // if the error message is "out of memory",                    // it probably means no database file is found                } finally {                    try {                        if (connection != null){                            connection.close();                        }                    } catch (SQLException e) {                        // connection close failed                    }                }            }            return cookies;        }        /**         * Decrypts an encrypted cookie         * @param encryptedCookie         * @return         */        @Override        protected DecryptedCookie decrypt(EncryptedCookie encryptedCookie) {            byte[] decryptedBytes = null;            if(OS.isWindows()){                try {                    decryptedBytes = Crypt32Util.cryptUnprotectData(encryptedCookie.getEncryptedValue());                } catch (Exception e){                    decryptedBytes = null;                }            } else if(OS.isLinux()){                try {                    byte[] salt = "saltysalt".getBytes();                    char[] password = "peanuts".toCharArray();                    char[] iv = new char[16];                    Arrays.fill(iv, ' ');                    int keyLength = 16;                    int iterations = 1;                    PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength * 8);                    SecretKeyFactory pbkdf2 = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");                    byte[] aesKey = pbkdf2.generateSecret(spec).getEncoded();                    SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");                    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");                    cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(new String(iv).getBytes()));                    // if cookies are encrypted "v10" is a the prefix (has to be removed before decryption)                    byte[] encryptedBytes = encryptedCookie.getEncryptedValue();                    if (new String(encryptedCookie.getEncryptedValue()).startsWith("v10")) {                        encryptedBytes = Arrays.copyOfRange(encryptedBytes, 3, encryptedBytes.length);                    }                    decryptedBytes = cipher.doFinal(encryptedBytes);                } catch (Exception e) {                    decryptedBytes = null;                }            } else if(OS.isMac()){                // access the decryption password from the keyring manager                if(chromeKeyringPassword == null){                    try {                        chromeKeyringPassword = getMacKeyringPassword("Chrome Safe Storage");                    } catch (IOException e) {                        decryptedBytes = null;                    }                }                try {                    byte[] salt = "saltysalt".getBytes();                    char[] password = chromeKeyringPassword.toCharArray();                    char[] iv = new char[16];                    Arrays.fill(iv, ' ');                    int keyLength = 16;                    int iterations = 1003;                    PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength * 8);                    SecretKeyFactory pbkdf2 = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");                    byte[] aesKey = pbkdf2.generateSecret(spec).getEncoded();                    SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");                    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");                    cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(new String(iv).getBytes()));                    // if cookies are encrypted "v10" is a the prefix (has to be removed before decryption)                    byte[] encryptedBytes = encryptedCookie.getEncryptedValue();                    if (new String(encryptedCookie.getEncryptedValue()).startsWith("v10")) {                        encryptedBytes = Arrays.copyOfRange(encryptedBytes, 3, encryptedBytes.length);                    }                    decryptedBytes = cipher.doFinal(encryptedBytes);                } catch (Exception e) {                    decryptedBytes = null;                }            }            if(decryptedBytes == null){                return null;            } else {                        return new DecryptedCookie(encryptedCookie.getName(),                                           encryptedCookie.getEncryptedValue(),                                           new String(decryptedBytes),                                           encryptedCookie.getExpires(),                                           encryptedCookie.getPath(),                                           encryptedCookie.getDomain(),                                           encryptedCookie.isSecure(),                                           encryptedCookie.isHttpOnly(),                                           encryptedCookie.getCookieStore());            }        }        /**         * Accesses the apple keyring to retrieve the Chrome decryption password         * @param application         * @return         * @throws IOException         */        private static String getMacKeyringPassword(String application) throws IOException {            Runtime rt = Runtime.getRuntime();            String[] commands = {"security", "find-generic-password","-w", "-s", application};            Process proc = rt.exec(commands);            BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));            String result = "";            String s = null;            while ((s = stdInput.readLine()) != null) {                result += s;            }            return result;        }    }}