AACS icon indicating copy to clipboard operation
AACS copied to clipboard

Getting newer certs

Open wilypomegranate opened this issue 3 years ago • 24 comments

Hi, do you have some info on how to get newer certs if need be?

Thanks for the awesome work here. Looking forward to trying out/hacking :)

wilypomegranate avatar Jun 06 '21 18:06 wilypomegranate

Basic idea is to connect mobile device to openauto/car, dump process memory (pmdump) and search for certificates there.

tomasz-grobelny avatar Jun 13 '21 15:06 tomasz-grobelny

Hi, relating to my question at #4 and emails with Tomasz,

In the end, I was able to dump both latest Headunit Public & Private key and Mobile Device Public & Private key.

In case of Headunit keys, I dumped them from DHU, it's "hidden" inside the app, so I dumped them directly from memory when it was supposed to be loaded by BoringSSL.

In case of Mobile Device keys, it was more difficult. The public key is easy to get from smali files (or via jadx GUI, searching for BEGIN CERTIFICATE keyword). But private key is somehow composed from public key, root certificate and some unknown byte arrays. I wasn't able to figure out how this all works and what are that unknown byte arrays (I don't have much skills about SSL/TLS encryption). Anyway, by decompiling the code and trying to simulate the code, I wasn't able to get correct result. Although, by dumping some of the variables directly from working device, I was able to dump the key. My bet is that Google designed some functions the way, that jadx will generate the pseudo-code wrongly, thus the result will not match. Although, I might be wrong, maybe there are some other factors (I have one suspicion what is different from my reproduction code).

gamelaster avatar Apr 17 '22 13:04 gamelaster

Hi @gamelaster,

I tried to get newer certs by doing pmdump+search as I did previously, but this time wasn't able to get the private key :-( Could you possibly describe your approach in more detail? How did you dump "some of the variables directly from working device" - which tools did you use and how did you find the variables to dump?

tomasz-grobelny avatar Oct 08 '22 22:10 tomasz-grobelny

Do I understand correctly that this function is of primary interest here

    public static SSLContext m23915f(iwa iwa) throws Exception {
        SSLContext instance = SSLContext.getInstance("TLSv1.2");
        Cipher instance2 = Cipher.getInstance("AES/CBC/PKCS5Padding");
        f27646b.mo22711j().mo22663aa(6991).mo22646J("Using %s from %s", "AES/CBC/PKCS5Padding", instance2.getProvider());
        byte[] bArr = new byte[48];
        String a = iwa.mo16269a();
        Charset charset = f27645a;
        m23918i(a.getBytes(charset), bArr, iwa.mo16271c());
        m23918i("-----BEGIN CERTIFICATE-----\nMIIDiTCCAnGgAwIBAgIJAMFO56WkVE1CMA0GCSqGSIb3DQEBBQUAMFsxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW\naWV3MR8wHQYDVQQKDBZHb29nbGUgQXV0b21vdGl2ZSBMaW5rMB4XDTE0MD
        for (int i = 0; i < 7; i++) {
            m23918i(bArr, bArr, iwa.mo16271c());
        }
        byte[] bArr2 = new byte[32];
        System.arraycopy(bArr, 0, bArr2, 0, 32);
        instance2.init(2, new SecretKeySpec(bArr2, "AES"), new IvParameterSpec(bArr, 32, 16));
        byte[] doFinal = instance2.doFinal(iwa.mo16270b());
        int length = doFinal.length - 54;
        byte[] bArr3 = new byte[length];
        System.arraycopy(doFinal, 28, bArr3, 0, length);
        ivx ivx = new ivx(KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(bArr3, 2))), iwa.mo16269a());
        instance.init(new KeyManager[]{ivx}, new TrustManager[]{ivx.f27638a}, (SecureRandom) null);
        f27646b.mo22619d().mo22663aa(6992).mo22646J("Successfully initialized SSL context for protocol: %s using provider: %s", instance.getProtocol(), instance.getProvider().getName());
        return instance;
    }

and what we eventually need is this: Base64.decode(bArr3, 2)?

tomasz-grobelny avatar Oct 08 '22 23:10 tomasz-grobelny

Hi @tomasz-grobelny , as far as I remember, this looks like that function. I will try to take look on it, but probably after 20th October, since I have deadline for a another project :/

gamelaster avatar Oct 09 '22 07:10 gamelaster

I am following along here also. Very interested in getting this working on a vehicle where I can not change the date and time.

kevinlieb avatar Oct 13 '22 21:10 kevinlieb

I was able to take the above function (though it looked slightly different on my computer, I may have got a newer APK or be using an older JADX), tweak it very slightly and run it to produce some private key (and if you don't do it right, the creation step fails, so perhaps it's valid?)

However, the private key is not compatible (e.g. different modulus) to any of the visible certificates in the code - I assume this is what @gamelaster refers to with "correct results" anyway? There's a class that implements X509KeyManager which the private key and a string possibly containing a base64 certificate is passed into, which also appears to load another certificate embedded in the code as well as creating a completely new certificate - this newly created certificate will have the correct modulus, but when I test it it uses the input certificate causing it to expire on the 19th March 2022 (same as the input certificate).

There is some logic to either use the default string (containing the March cert) and the byte arrays from the main class, but it also has provisions to load the content from elsewhere. Some of it maps onto Phenotype (which allows Google to override values via GMS) but it's quite complex logic, so I'm not certain yet if it might not also be pulling values from another location.

Edit to add: the only Android phone I have is meant to be rooted but it seems to be messed up so I wasn't able to run pmdump and actually see the cert from there to see what it looks like.

thegnomewizard avatar Oct 21 '22 21:10 thegnomewizard

@thegnomewizard you're right. For some reason, using directly JADX code doesn't work at all. It must be combination of dumped variable contents from the phone + using some parts of code to produce working private certificate. I'm absolutely cryptography noob, so I don't know how those things works, so I just did things which gave me the cert.

gamelaster avatar Oct 22 '22 06:10 gamelaster

Ah, then in case anyone is interested in trying it, this is the code I copied and tweaked into an empty Android Activity project. Just call 'doTest', from onCreate for example:

    private void doTest() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, CertificateException {
        SomethingProvider somethingProvider = new SomethingProvider();
        Charset useCharset = StandardCharsets.UTF_8;
        Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
        String test = somethingProvider.getString();

        byte[] bArr = new byte[48];
        tweakBytes(somethingProvider.getString().getBytes(useCharset), bArr, somethingProvider.getArray1());
        tweakBytes("-----BEGIN CERTIFICATE-----\nMIIDiTCCAnGgAwIBAgIJAMFO56WkVE1CMA0GCSqGSIb3DQEBBQUAMFsxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW\naWV3MR8wHQYDVQQKDBZHb29nbGUgQXV0b21vdGl2ZSBMaW5rMB4XDTE0MDYwNjE4\nMjgxOVoXDTQ0MDYwNTE4MjgxOVowWzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh\nbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxHzAdBgNVBAoMFkdvb2ds\nZSBBdXRvbW90aXZlIExpbmswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQDUH+iIbwwVb74NdI5eBv/ACFmh4ml/NOW7gUVWdYX50n8uQQsHHLCNIhk5VV2H\nhanvAZ/XXHPuVAPadE2HpnNqePKF/RDo4eJo/+rOief8gBYq/Z+OQTZeLdNm+GoI\nHBrEjU4Ms8IdLuFW0jF8LlIRgekjLHpc7duUl3QpwBlmAWQK40T/SZjprlmhyqfJ\ng1rxFdnGbrSibmCsTmb3m6WZyZUyrcwmd7t6q3pHbMABO+o02asPG/YPj/SJo4+i\nfb5/Nk56f3hH9pBiPKQXJnVUdVLKMXSRgydDBsGSBol4C0JL77MNDrMR5jdafJ4j\nmWmsa2+mnzoAv9AxEL9T0LiNAgMBAAGjUDBOMB0GA1UdDgQWBBS5dqvv8DPQiwrM\nfgn8xKR91k7wgjAfBgNVHSMEGDAWgBS5dqvv8DPQiwrMfgn8xKR91k7wgjAMBgNV\nHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQDKcnBsrbB0Jbz2VGJKP2lwYB6P\ndCTCCpQu7dVp61UQOX+zWfd2hnNMnLs/r1xPO+eyN0vmw7sD05phaIhbXVauKWZi\n9WqWHTaR+9s6CTyBOc1Mye0DMj+4vHt+WLmf0lYjkYUVYvR1EImX8ktXzkVmOqn+\ne30siqlZ8pQpsOgegIKfJ+pNQM8c3eXVv3KFMUgjZW33SziZL8IMsLvSO+1LtH37\nKqbTEMP6XUwVuZopgGvaHU74eT/WSRGlL7vX4OL5/UXXP4qsGH2Zp7uQlErv4H9j\nkMs37UL1vGb4M8RM7Eyu9/RulepSmqZUF+3i+3eby8iGq/3OWk9wgJf7AXnx\n-----END CERTIFICATE-----\n".getBytes(useCharset), bArr, somethingProvider.getArray1());
        for (int i = 0; i < 7; i++) {
            tweakBytes(bArr, bArr, somethingProvider.getArray1());
        }
        byte[] bArr2 = new byte[32];
        System.arraycopy(bArr, 0, bArr2, 0, 32);
        instance.init(2, new SecretKeySpec(bArr2, "AES"), new IvParameterSpec(bArr, 32, 16));
        byte[] doFinal = instance.doFinal(somethingProvider.getArray2());
        int length = doFinal.length - 54;
        byte[] bArr3 = new byte[length];
        System.arraycopy(doFinal, 28, bArr3, 0, length);
        java.security.PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(bArr3, 2)));
        String bobo = Base64.encodeToString(privateKey.getEncoded(), Base64.DEFAULT);
        Log.e("TEST", "privateKey.getEncoded()=" + bobo);

        CertificateFactory factory = CertificateFactory.getInstance("X.509");
        ByteArrayInputStream inputStream = new ByteArrayInputStream(test.getBytes(useCharset));
        X509Certificate certificate = (X509Certificate)factory.generateCertificate(inputStream);
        String baba = Base64.encodeToString(certificate.getEncoded(), Base64.DEFAULT);
        Log.e("TEST", "New cert: " + baba);
    }

    private static void tweakBytes(byte[] bArr, byte[] bArr2, byte[] bArr3) {
        for (int i = 0; i < bArr.length; i++) {
            for (int i2 = 0; i2 < 48; i2++) {
                int b = bArr2[i2] & 255;
                bArr2[i2] = (byte) (((((b >> 7) | (b + b)) + 33) ^ bArr3[i2 % bArr3.length]) ^ bArr[i]);
            }
        }
    }
    final class SomethingProvider {
        /* renamed from: a */
        public final String getString() {
            return "-----BEGIN CERTIFICATE-----\nMIIDLDCCAhQCAgLCMA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNVBAYTAlVTMRMwEQYD\nVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MR8wHQYDVQQK\nDBZHb29nbGUgQXV0b21vdGl2ZSBMaW5rMB4XDTE0MDcwNDAwMDAwMFoXDTIyMDMx\nOTIzMzgzMFowXDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAU\nBgNVBAcMDU1vdW50YWluIFZpZXcxEzARBgNVBAoMCkNhclNlcnZpY2UxCzAJBgNV\nBAsMAjQ3MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFsN3bAQhIz8\nvH5UFqbiztjgcKaL/oE3n8BnKOBB/zbCFw35zSWCVy4fKQxztkLX/A2t9OnIrq6b\njwf7iHIcGqocd7UCeGo1Dswbuor4vGJc5mdH9SFtj/R5ko8z26Q96r4HGIW4XVda\niegpu1n83FLgkznO4DY/C8bkIP56Kon3ehZsrfERXq1TCXDe/ZlPp53cNPwPBHI9\n5NmRfjRAmaBpo4JVSQAKpyFHUJdzkSToPRchKpJSLuLv5ssbdYV7pzuhh2Qwr3k8\nP/XoT41e4DmMCi0KcIe1+E56JX1wuOQGaLtuCgoWDHkJhQTZRUhl9coOYXi4HaF2\nLN7fGDdipwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBbnNS7ZyCLaUEKGMf1ou1a\nHrzqMynjt0M8s6F+rOe50kqthRNAuLlt5HFAW3TI9yQRb8htEUc/5b8tsvHWN2vZ\nJxtvl/f8TQpKV/CtLDEiebmxfqD2e1u3rwSmJPV2ESxbxBtyn8CMNwqgcgYNDCbl\nf2vF3HAp71AmrFgfmu4X0cQ4szIe0yZC0Am7H6eFgI3sHF5/y4loihbATm4lyeQz\nnFew5+8uElgnIubfoRXwVwE/gxipiCOZyjCV28LszeIn9MoCnZ7AEEX5Us8eWl06\nDGK8f6690yJk18E8pEM/tfIzNslgeocRMG2yfmkCuqcHGt6gh/V/B/c74s487fBf\n-----END CERTIFICATE-----\n";
        }

        /* renamed from: b */
        public final byte[] getArray1() {
            final byte[] f30470n = {-72, -102, 94, -110, -110, 104, 43, -1, 94, 31, -44, -115, 58, 97, 83, -21, -108, 122, -3, 17, 71, -10, -83, 96, -114, 106, -58, -59, 94, -68, 118, -76, -13, -77, 40, -94, 29, 42, 96, 17, 64, 98, -55, -50, 33, -89, 88, -45, -113, -88, -19, -9, -41, 14, 30, -41, 10, 71, -86, -16, -48, -49, -14, 5, -124, 83, -120, -91, -30, -60, 4, -64, 39, 56, -20, 71, 54, -4, -125, -74, 53, 76, -125, 120, -127, 53, -94, -89, -28, 72, -39, -80, 105, 69, 51, 120, 8, -81, -25, -22, -46, 43, 67, 12, -66, 4, -17, -46, -64, -29, 99, -111, 111, -103, 4, 57, 100, -101, -116, 1, -1, -120, -65, 24, 89, Byte.MAX_VALUE, -127, 81, 82, 65, -104, 105, 94, -67, 119, -119, 9, 89, -11, -113, -67, 4, 93, 68, -66, 8, 117, 68, -116, -36, 9, 7, -6, -25, -8, 102, 6, 111, 73, -72, 76, -79, -117, -101, -68, 40, -114, -117, -29, 22, 98, -43, -80, 35, 52, 48, -70, 48, 31, -50, -33, -8, 114, 120, 7, -18, 0, 118, 98, 123, 68, -73, 37, -39, 18, -102, 16, -20, -8, 104, -110, 65, 78, -47, 112, -114, 34, 42, -61, -125, 57, 112, 17, 8, 31, 91, 54, -92, -10, 115, 68, 6, 78, -95, 7, 38, 82, 80, -22, -105, -72, -90, 56, 62, -108, 60, -118, 71, 56, 109, 81, 38, -106, 4, -90, -120, 9, 36, -45, -105, -83, 117, 50, 18, 30, 41};
            return f30470n;
        }

        /* renamed from: c */
        public final byte[] getArray2() {
            final byte[] f30471o = {-125, -7, -2, -91, -74, 121, -5, -10, -52, -96, -84, 77, -26, 85, 110, 120, 1, 109, 47, -18, 51, 119, -75, 109, 114, -68, -112, 6, 55, -121, -25, 11, 32, 40, 61, -45, 108, -20, 90, -27, -50, 100, 25, -98, -27, -101, 11, 23, 91, 47, -8, 69, -69, -18, 36, 122, 25, 51, -58, -56, 33, -124, 41, 108, 98, -55, -39, -42, -43, 54, -102, -52, -81, 37, -72, 87, 68, -29, -101, 14, 96, -99, -22, -99, 50, 48, -77, -17, -81, 64, -126, -12, 88, 18, 27, 93, -9, 45, -70, -18, 62, 123, 95, -82, 69, -22, 24, 81, -11, 112, 99, 40, 99, -41, 29, 104, -35, 109, -9, -88, -10, -58, -35, 34, -107, -18, 91, 58, 95, 92, -59, -87, -76, -92, -13, -119, -38, 15, -100, -80, -22, 120, 61, 110, -111, 29, 107, -2, -116, 124, 58, -114, -1, -75, 88, 80, Byte.MAX_VALUE, -125, 110, -77, 49, -52, -115, 96, -32, -58, -2, -18, -82, 115, 90, -38, -83, -99, -8, 57, -25, 83, 102, -13, -5, -41, -19, -54, -123, -31, 53, Byte.MAX_VALUE, -27, 67, 12, 65, -126, 112, -92, -60, 68, -24, -109, -43, 10, 24, 39, 23, 126, 33, -83, -103, -60, 42, 113, -5, -11, -41, -53, 14, 114, -76, 10, 32, 57, 89, -42, -52, -94, 9, -5, -102, -127, -109, 120, -94, 112, -63, -92, 125, -72, 27, -101, Byte.MAX_VALUE, 96, -32, 113, 58, -9, 74, -3, 28, -61, 93, 53, -13, 2, 30, 68, -60, -37, -59, 51, 108, -8, 112, 11, 68, -28, -15, Byte.MIN_VALUE, 25, 109, 60, 112, 42, 14, 85, -78, -117, 77, -11, 115, 74, 119, 111, -105, -78, 84, 0, -122, -54, 1, 25, 114, 74, 83, -27, 79, -85, -13, 69, 123, 30, 98, 43, 99, -62, -40, -125, 85, -18, -25, -91, -15, 99, -96, -31, -4, -45, -95, -116, 88, -14, -119, 74, -8, -71, -19, 80, 99, 109, -101, -20, -76, -37, 41, 4, 92, -107, 51, -38, -98, -20, 87, 67, 72, 52, -25, -70, -43, -96, -52, 54, 40, 27, -34, 52, -62, -41, -102, 101, -78, 75, -20, 94, 32, -106, 91, -75, -2, -54, -16, -74, -71, 93, 22, 71, 32, 69, -64, 39, -65, 98, -38, 57, 92, -93, -41, 9, -12, 90, 36, -100, -48, 24, -91, -62, 7, 92, -87, 0, -82, -37, -65, -90, -81, -124, 118, -109, 76, -106, 124, -67, -86, 65, -57, -26, -124, 54, -101, 85, -94, -64, -59, -101, -49, -92, -115, -23, 124, -69, 63, -114, -61, -115, -74, 55, -54, -105, 35, 86, -76, 121, 107, 30, 88, 58, 58, 86, 56, -73, 121, -12, -59, -43, -83, -105, -34, -30, 83, -11, 107, -117, 48, -39, -72, 43, 33, -58, 47, -61, -13, 94, 34, 79, 106, 118, 8, -68, -11, 112, -85, 94, 68, -13, -33, -26, -20, 102, 63, -118, -27, 81, -32, -52, -79, -114, 49, -102, -30, 26, 118, 42, 85, -26, 78, -3, 94, 17, -100, -32, -15, 60, 7, 73, -58, 34, -74, 47, -75, 81, -63, 82, -35, 99, 24, -86, 38, 75, -80, -14, -23, -9, -13, -99, -61, 66, -80, 21, 39, -17, -110, 0, 37, -68, 29, -114, -58, 117, 24, -42, -30, -77, -106, 69, -37, 112, -24, -123, 58, 106, -82, 28, -26, -95, -5, -76, -28, -96, 107, -98, -26, 74, -106, -20, -94, -112, -75, 43, -41, 3, -71, -90, 125, -26, -22, -53, 65, 95, -4, 9, 45, 91, -25, 25, -15, -107, Byte.MIN_VALUE, -75, -23, 3, 94, -94, 12, -80, -58, -3, -88, 67, -117, 92, -30, -45, -47, 15, 48, 10, -82, 106, -123, -91, 25, -54, 115, 116, 56, -32, 126, 23, 31, -30, 20, 95, -42, -7, 56, -62, 100, -125, 124, 83, 4, 12, -80, 63, -20, 50, 35, 97, 108, -54, -86, 87, 0, -18, 50, -46, 39, -33, -7, 63, 107, 66, -87, 61, 17, 1, -107, 56, 16, 105, -71, 76, 80, 99, -121, Byte.MIN_VALUE, -30, 12, 24, 90, 121, -66, 108, -104, -9, -109, -118, -118, -76, -77, 28, -104, -112, 96, 121, 85, -55, 81, 47, -44, -120, 24, 62, 61, 61, -100, -63, 26, -25, 99, 85, -114, 45, -77, 80, -42, -61, -73, -9, 38, -19, -50, -22, 55, 56, 40, 3, -121, 50, 29, 19, -47, -47, 3, 110, 36, -18, -61, -119, -20, 84, 111, -116, 102, 68, -10, 104, -39, 3, -73, 6, 26, 44, -70, 104, -41, -46, 5, 82, 47, 47, -76, 121, -56, 28, 126, 43, -97, -119, 42, 59, 94, -62, 42, -28, -61, 98, -125, -18, 109, 121, 40, 7, 103, 33, -44, 126, -117, -18, -34, 2, 27, 39, 120, 4, -56, -100, -112, -10, -100, -59, -64, -122, 41, -62, -71, 40, -107, -17, -36, -20, -60, -94, -8, -12, 126, 38, 27, -60, -21, -62, -38, 114, -6, 0, -113, -56, -19, -84, 116, 116, 71, -42, -56, 37, 83, -10, -1, 60, 96, 97, 67, -117, -116, -25, -39, -96, -6, -42, -114, -5, 101, -88, -59, -107, -59, 36, -39, 126, 39, -53, -109, 105, -124, 9, -71, -31, 23, 60, 86, 69, -35, -116, 24, -87, -36, 96, -43, -101, -9, 19, -67, -13, 29, 51, 47, 22, -64, 88, -24, -66, 86, 4, -86, -5, 21, -50, -29, -110, 38, -127, 126, 2, 62, -45, -71, 68, -45, -77, 75, 61, 28, 67, 5, -104, 119, -12, -61, 8, 74, -49, 1, -35, 50, -34, 95, -39, -23, 70, -123, 83, -115, 5, 85, 19, 100, -34, -110, -73, -73, 88, 96, -65, -84, -119, -18, 59, 102, -39, -40, 95, 19, -102, 5, -70, 108, 80, 86, 16, 43, -110, 4, -33, -127, -87, 77, 52, 90, -115, 42, -14, 28, -61, 56, 51, -66, 57, 32, 80, 50, -75, 106, -89, 59, -79, 104, 17, 75, -14, 72, -108, 75, -81, 78, -1, -3, -25, -81, 76, 5, -47, 98, 84, -81, 55, Byte.MAX_VALUE, 4, 85, 112, 18, -57, -49, 31, 83, -33, -68, -62, -3, 8, 36, -10, -111, 58, -90, 15, 41, -93, 64, -125, -69, -118, 57, -67, 14, -85, 115, -60, 99, 113, 10, -83, 6, 75, -71, 10, -121, 104, -44, 121, -103, -43, -126, 78, 64, 51, 103, 72, -86, 35, -8, 78, -50, -87, 119, -61, -58, -96, -3, -102, -16, 26, 124, 85, -58, -32, 48, -126, 14, -119, 57, 67, -84, -1, 8, 71, -119, -69, -47, 38, 82, 120, -65, 24, 36, 118, -84, -94, -115, 7, 13, -71, 113, -100, 9, 59, 28, 80, Byte.MIN_VALUE, 9, 21, 18, -92, 115, 85, -45, -70, 38, 49, -47, 82, 0, 20, 27, 115, -29, 53, 84, -106, -10, 99, -64, 6, -71, -21, 45, 53, 76, -24, 44, 103, -93, -2, Byte.MAX_VALUE, 9, 22, -70, -88, -46, -28, 111, 89, -92, 8, -54, -97, 18, 11, 106, 116, Byte.MAX_VALUE, -116, 24, 81, -37, -3, 86, 78, 39, 126, -76, 14, 13, -3, -8, 72, -100, -46, 77, 26, -46, -75, 49, 71, 8, 122, 93, -36, -118, -67, -116, -94, 101, 79, -66, 54, -86, -22, 1, 50, -87, 101, -34, 43, -124, 36, -62, 55, -82, -67, -95, 92, -45, -9, 42, 122, -100, 58, -42, -127, -66, -18, -104, -6, -111, -54, -62, 72, -111, -18, -104, -50, 57, 23, 111, -58, -45, -40, 25, 63, 75, 92, -17, 12, 38, 18, -53, 16, -126, -2, -108, 118, 57, -6, -38, -117, -74, 99, -51, 59, 78, -70, 41, 57, -120, -32, 7, -77, -69, -15, -115, 5, -13, 22, -59, 114, -115, -98, 97, 24, -100, 15, 5, 98, 49, -49, -45, 117, -88, 81, -44, 42, 13, 77, -85, -10, 97, -64, 77, 49, -93, -87, 31, 83, -107, 65, 103, 76, -75, -118, -33, 117, -27, 13, -41, -13, 60, -75, -6, -74, -115, 0, 48, 43, -83, -115, -83, 61, 58, 41, -100, -24, -45, -101, 98, -49, -5, -37, -70, 109, 74, 68, -58, 89, 65, -1, 47, 81, 17, 2, 68, 54, 37, -1, -103, -76, -30, 112, 94, 0, 3, -96, -37, 64, 124, -62, -16, 16, 62, -49, -93, -3, 88, 33, -72, -45, -117, 14, 68, -5, 64, 94, 44, 46, -99, -10, 62, 89, 99, 66, 107, 70, -34, 124, 14, 21, -99, -102, 33, -81, -121, -95, -115, -76, 71, -44, -49, -95, 24, -37, -119, 35, 24, -11, -112, 35, 125, -115, 126, -7, 67, 114, -62, -37, -58, 116, -64, -22, 73, 40, -15, 123, -101, -59, -35, 32, 39, 42, 125, 8, -52, 10, -90, 112, 103, -63, 11, 111, 124, -27, 11, 29, -123, 38, -88, 112, -125, -76, -84, 117, -126, 32, -41, -44, 26, -115, -112, 83, 83, 118, 57, 93, 82, -72, -59, -119, 88, 93, -82, -51, 62, -74, 27, -71, 6, 95, -36, 20, 72, 107, -10, -66, 28, 92, -114, -86, 56, -42, 86, 29, -113, -126, 70, 86, -106, -28, -71, 63, 76, -110, 95, -79, 107, -42, -47, -121, -17, -88, 6, 61, 103, Byte.MAX_VALUE, -112, -77, 52, -18, 67, 62, 104, -70, -74, -81, -82, 110, -2, 66, 67, 4, 14, -94, -71, 29, -115, -50, 104, -106, -7, -28, -62, -37, 48, -81, 87, -29, -43, 39, 59, -85, -18, 43, -70, 116, Byte.MAX_VALUE, -32, 121, -62, 119, 27, 37, -78, -84, 66, 65, -103, -87, -23, 39, 52, 25, -102, -59, -117, -77, -110, 9, -22, -107, -82, -111, 116, -111, 64, -113, -70, 113, 126, -58, 28, 3, 66, 88, -64, 116, -54, 85, 117, -34, -21, 14, -32, -86, -45, -21, -32, 92, -91, -121, -115, -33, -75, 71, -11, 27, -82, 5, 39, 79, -58, 60, -108, 52, -83, -39, 17, -45, -100, 38, -11, 101, 112, -74, 93, 102, -102, -84, -29, -77, -17, 102, 122, -52, 122, 24, -79, 38, 113, 126, -118, -43, 95, -11, -103, -112, 31, -69, -103, 18, -15, 83, 24, -114, -31, 49, -102, -6, 0, 84, Byte.MAX_VALUE, -119, -75, -94, 78, Byte.MAX_VALUE, -38, -1, -69, 9, 72, 88, 10, -6, 0, 80, 105, 124, 53, -24, -30, 67, -32, 9, -28, -69, 60, 71, -42, -40, 40, -2, Byte.MAX_VALUE, 35, -121, 34, -112, 28};
            return f30471o;
        }
    }

It should print to the log both the private key it found, and a cert I tried generating using the other values from the source (which has sadly already expired, when inspected via openssl x509 -in <filename> -noout -text). For reference, the modulus of the private key I found (via openssl rsa -in <filename> -noout -modulus|openssl md5) was 778f903e3db3bddb5cac4de94937ad4f.

If there are alternate values coming from somewhere they'd go into the SomethingProvider class - the code in the Android Auto app will only replace the default values if all three values (the string, seemingly containing a certificate, and two byte arrays) are provided. If none, one or two only are present, it just uses the default. I think this means there might be some values coming from somewhere for these. Might still just be easier to grab it from memory ;)

thegnomewizard avatar Oct 22 '22 06:10 thegnomewizard

@thegnomewizard as @tomasz-grobelny said, it's no more possible to grab it from memory. It needs the combination.

About the final private key, it should have expiration date at least after 8 months after release of APK.

gamelaster avatar Oct 22 '22 07:10 gamelaster

Oh, I was thinking in terms of maybe shimming the OpenSSL library on device (as I believe Android just natively calls out to it for SSL support), after all it seems like it must still exist in memory at some point (though as I mentioned I still need to pick up a functional, rooted phone).

Regarding the expiry date, I was curious about that - openssl isn't reporting an expiry date for the private key I found (nor the one already in the repo under android_auto.key. Did you try to capture the public key from memory? If so, what was the modulus?

thegnomewizard avatar Oct 22 '22 19:10 thegnomewizard

Finally got a rooted Android device and confirmed the sensible looking key in RAM is 891b79109323149bf53c718de837e0de, different modulus to the private key recovered by running the code in the JAR. I believe this confirms the content from the interface that returns a string (with a certificate) and two binary arrays is being replaced at runtime with values from some kind of contentprovider. The code to do this (which also involves checks for debug keys, for example ((!str.equals("eng") && !str.equals("userdebug")) || (!str2.contains("dev-keys") && !str2.contains("test-keys")))) is pretty complicated though, I haven't yet deduced where it might be coming from manually.

thegnomewizard avatar Nov 02 '22 07:11 thegnomewizard

Scratch that - I think I was just using a too-old APK before. Copied the APK out of my new Android phone and it had different public cert (expires in April 2023) and byte arrays (in the code above, what I called the SomethingProvider). Pasting these values in and running the above code has given me a valid private key that matches the public certificate (sharing the same modulus). I'll need to try it in the car.

thegnomewizard avatar Nov 02 '22 08:11 thegnomewizard

Confirmed the cert and private key I have seemed to work with AAServer and my car (a Chevrolet).

The car had previously stopped just after authentication with no message at all, but it's at the same point others got SSL errors, so I suspect the car just ignores invalid messages.

thegnomewizard avatar Nov 03 '22 02:11 thegnomewizard

Oh cool! How you did that @thegnomewizard?

gamelaster avatar Nov 03 '22 12:11 gamelaster

So after I got an updated APK, I just ran the code I posted above (just taking out the bit at the end that was incorrect. Without that this code is pretty much what appears in the APK, just extracted, tweaked and renamed to be more readable):

    private void doTest() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, CertificateException {
        AnotherProvider somethingProvider = new AnotherProvider();
        Charset useCharset = StandardCharsets.UTF_8;
        Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
        String test = somethingProvider.getString();

        byte[] bArr = new byte[48];
        tweakBytes(somethingProvider.getString().getBytes(useCharset), bArr, somethingProvider.getArray1());
        tweakBytes("-----BEGIN CERTIFICATE-----\nMIIDiTCCAnGgAwIBAgIJAMFO56WkVE1CMA0GCSqGSIb3DQEBBQUAMFsxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW\naWV3MR8wHQYDVQQKDBZHb29nbGUgQXV0b21vdGl2ZSBMaW5rMB4XDTE0MDYwNjE4\nMjgxOVoXDTQ0MDYwNTE4MjgxOVowWzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh\nbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxHzAdBgNVBAoMFkdvb2ds\nZSBBdXRvbW90aXZlIExpbmswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQDUH+iIbwwVb74NdI5eBv/ACFmh4ml/NOW7gUVWdYX50n8uQQsHHLCNIhk5VV2H\nhanvAZ/XXHPuVAPadE2HpnNqePKF/RDo4eJo/+rOief8gBYq/Z+OQTZeLdNm+GoI\nHBrEjU4Ms8IdLuFW0jF8LlIRgekjLHpc7duUl3QpwBlmAWQK40T/SZjprlmhyqfJ\ng1rxFdnGbrSibmCsTmb3m6WZyZUyrcwmd7t6q3pHbMABO+o02asPG/YPj/SJo4+i\nfb5/Nk56f3hH9pBiPKQXJnVUdVLKMXSRgydDBsGSBol4C0JL77MNDrMR5jdafJ4j\nmWmsa2+mnzoAv9AxEL9T0LiNAgMBAAGjUDBOMB0GA1UdDgQWBBS5dqvv8DPQiwrM\nfgn8xKR91k7wgjAfBgNVHSMEGDAWgBS5dqvv8DPQiwrMfgn8xKR91k7wgjAMBgNV\nHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQDKcnBsrbB0Jbz2VGJKP2lwYB6P\ndCTCCpQu7dVp61UQOX+zWfd2hnNMnLs/r1xPO+eyN0vmw7sD05phaIhbXVauKWZi\n9WqWHTaR+9s6CTyBOc1Mye0DMj+4vHt+WLmf0lYjkYUVYvR1EImX8ktXzkVmOqn+\ne30siqlZ8pQpsOgegIKfJ+pNQM8c3eXVv3KFMUgjZW33SziZL8IMsLvSO+1LtH37\nKqbTEMP6XUwVuZopgGvaHU74eT/WSRGlL7vX4OL5/UXXP4qsGH2Zp7uQlErv4H9j\nkMs37UL1vGb4M8RM7Eyu9/RulepSmqZUF+3i+3eby8iGq/3OWk9wgJf7AXnx\n-----END CERTIFICATE-----\n".getBytes(useCharset), bArr, somethingProvider.getArray1());
        for (int i = 0; i < 7; i++) {
            tweakBytes(bArr, bArr, somethingProvider.getArray1());
        }
        byte[] bArr2 = new byte[32];
        System.arraycopy(bArr, 0, bArr2, 0, 32);
        instance.init(2, new SecretKeySpec(bArr2, "AES"), new IvParameterSpec(bArr, 32, 16));
        byte[] doFinal = instance.doFinal(somethingProvider.getArray2());
        int length = doFinal.length - 54;
        byte[] bArr3 = new byte[length];
        System.arraycopy(doFinal, 28, bArr3, 0, length);
        java.security.PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(bArr3, 2)));
        String bobo = Base64.encodeToString(privateKey.getEncoded(), Base64.DEFAULT);
        Log.e("TEST", "privateKey.getEncoded()=" + bobo);
    }

    private static void tweakBytes(byte[] bArr, byte[] bArr2, byte[] bArr3) {
        for (int i = 0; i < bArr.length; i++) {
            for (int i2 = 0; i2 < 48; i2++) {
                int b = bArr2[i2] & 255;
                bArr2[i2] = (byte) (((((b >> 7) | (b + b)) + 33) ^ bArr3[i2 % bArr3.length]) ^ bArr[i]);
            }
        }
    }

From the updated APK, I got new values:

    final class AnotherProvider {
        public final String getString() {
            return "-----BEGIN CERTIFICATE-----\nMIIDLDCCAhQCAgN8MA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNVBAYTAlVTMRMwEQYD\nVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MR8wHQYDVQQK\nDBZHb29nbGUgQXV0b21vdGl2ZSBMaW5rMB4XDTE0MDcwNDAwMDAwMFoXDTIzMDQy\nNTIxMDUyMlowXDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAU\nBgNVBAcMDU1vdW50YWluIFZpZXcxEzARBgNVBAoMCkNhclNlcnZpY2UxCzAJBgNV\nBAsMAjYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8YiJ201G6Nm6\nulscy1/W3VWJI2j0RUV8vKucvt71EnQbooUyJgNw96DbZ6HTlaryt3KDqLQ2MhWe\nxDyi9XjGCIzQuphpV9ywVAtF5aerdFKI+c5xwzaXDKtZ9BqcVDAVumAdQbFpeIGI\n8F6HyGYjf3lTge6X2kR3AGEXVnK7piqH+MHXPcm6NHebz/fqUffGmNhlnEvvg8dH\n4QE2qhQYMf1gSokj8RZiXqK/6COhTZ0x42UzDq2X5S5ysvYnEKIKePYsLPfKnGPo\ncY7NRGhL3cAgcBDclpGg//xLlJkx0vHe0y+oBCtLbY3HqwvyDGcVH/fhTuMDpcOH\n74Fc67CFcwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDL6iWUMt+5zgqkNQY6ObX5\nCYzNB8FCd9MG7a6HmQT+nNPSz1DKgm0yQN5AFZyozm3IM7pk0pm8ui8rIkt/KTyc\nk0RfBvHbMJQ6ptFYjWIth+uo9TX4b4ZbtkcJdf5w9M6lkW5LT5vzfbAFIk16Kp7K\njUiktVoyI8y1968I1iscxIb/UdKyr4Ht6S/me6u5106/8qsGxpUnsQlsHMjbtepl\nezZcDQkoRgWTbfZMz6c32qybeNSCoAHsQTZ+1yiWLxiEjgt0XW8e/DV+BpS3espU\njZ+MPycNvm8rHBVX1A34ylpnw3C9tKbG2QiK6ggvuDFISPddYWqyixHom7W3C80y\n-----END CERTIFICATE-----\n";
        }


        /* renamed from: b */
        public final byte[] getArray2() {
            final byte[] f28562d = {123, 120, -82, 28, 122, -32, 80, 62, 102, 113, 40, 107, -31, 64, 125, -25, 63, 103, -18, 66, 15, -108, -32, -103, 17, 124, 10, 24, 16, -77, -75, Byte.MIN_VALUE, 124, 24, 108, 36, 37, -33, 19, -47, 73, 62, 0, 81, -114, -114, -101, Byte.MAX_VALUE, -37, 7, -27, 5, 26, 89, 79, -99, 22, -100, 15, -124, -126, -15, 25, -117, 121, 111, 93, -47, 21, 74, 64, -50, 122, -109, 80, 107, -88, -38, 101, -16, -120, -127, -95, 106, 111, 26, -123, -18, -33, -95, -55, -26, -75, 105, -76, -19, 43, -6, -111, 98, -25, 112, -18, 38, 53, 54, 22, -62, -37, -45, -125, -64, -123, 38, 92, 123, 65, 28, -62, -115, 3, -25, -91, 77, -95, -58, -74, 68, -64, 26, 50, 95, 114, -84, 55, -32, 56, -79, -70, -41, 27, -97, -108, -122, -37, 24, -47, -56, 28, -37, -12, -68, 114, -44, -93, 63, -60, -102, -27, -113, Byte.MAX_VALUE, -21, 67, -77, -122, 26, -77, -36, -107, 24, 14, 86, -110, 68, -85, 47, 55, 73, 103, 23, 27, 22, -85, -87, -45, 112, -4, 53, -50, 90, 77, -111, 119, -77, 20, -118, -85, Byte.MAX_VALUE, 90, 91, -91, 96, -77, 27, 45, 66, 9, 123, 49, Byte.MIN_VALUE, -56, -111, -94, -100, 100, -58, 101, -107, -41, -53, 38, -51, 5, -8, -14, 31, 14, 113, -36, -111, -42, -91, -37, 36, 118, 12, 51, -5, 36, -99, 22, -54, -81, -8, 92, -81, 88, -102, 53, -103, -22, 95, 50, 47, -86, 57, -4, -78, 117, 24, -17, -59, -4, 95, -47, -117, -8, 19, -18, 27, 53, -73, -126, -123, -57, 113, 15, 72, 103, 32, -30, 56, Byte.MIN_VALUE, 30, 108, -70, 63, -60, -124, -77, -102, -21, 94, -112, -19, 85, -73, 20, 82, -16, -58, 88, -65, -48, 116, 11, -15, -85, 119, 64, 122, -114, -101, -53, -122, -65, -10, -16, 40, -105, 117, 16, -59, -104, -84, 90, -83, 99, -34, -96, 101, 42, 90, -7, 79, 57, 0, -109, -35, -92, 52, 7, 13, -52, 97, 98, 121, 122, -9, -3, -97, -66, 46, 8, 93, 17, 115, 17, 120, -64, -118, -53, -98, -62, -96, -71, 34, 53, 38, 28, 43, 30, -24, -91, 101, 7, 36, 65, -92, -91, 80, -86, 72, 68, -55, 40, 72, 101, 99, -6, -114, 123, 112, -81, -22, -43, 122, 100, 103, 108, 82, 103, -85, -84, -40, -105, -80, -88, -85, 18, 92, 110, -77, -9, -57, -87, 7, -96, 81, 121, 108, 109, -51, -99, -98, 59, 46, -69, 116, 49, 118, 123, -126, -22, 21, 13, -42, 29, -74, 92, 47, -36, 49, 13, 74, -10, -40, -52, -85, 96, -18, -38, -43, -56, -71, 44, 32, -34, -110, -23, -8, -91, 49, 86, -7, -5, -43, 80, -80, 36, 80, -110, 43, -7, -14, -60, 73, -18, -81, 50, 125, -87, -24, -63, -115, -17, -110, 28, -9, -27, 123, -90, 79, -103, 76, 39, 72, 43, -119, 26, 50, 12, 61, 64, 115, -68, -40, -124, -5, -83, 23, 13, -120, 77, 113, -53, -4, 101, -91, 74, -69, -77, 99, -116, -36, 57, -77, -98, 98, -37, -65, 14, -36, 52, 18, 33, 112, 78, -33, -80, 72, -14, -39, 124, 86, -123, 5, -113, -75, -85, -34, -26, 112, 9, -82, 21, 68, 88, -12, -25, 81, 108, 38, 6, 105, 55, 40, 70, -9, 91, -123, 38, -123, 106, 0, -90, 75, 113, -89, -65, 68, 18, -74, 52, -99, -52, 52, 64, -50, -28, 2, 63, 10, -6, 0, 117, 87, -66, 41, 87, -58, 1, -118, 4, 79, 49, 123, -18, -92, 109, -87, -115, 25, -127, -76, 60, -94, 105, 62, -87, 6, -7, 80, -88, 83, -55, -74, -95, 16, 34, 119, -120, -53, -84, 62, 122, -85, 97, -50, -31, -14, 126, -75, -72, -97, 56, -78, -108, 44, 21, 94, -67, 76, 79, Byte.MAX_VALUE, 95, 69, 24, 97, -2, Byte.MAX_VALUE, -21, -103, -32, 39, -51, 34, 75, -67, 93, 123, 99, -126, -89, -39, -67, -60, 6, 37, 121, 14, -23, Byte.MIN_VALUE, 100, 28, -80, -102, 15, 90, -15, 34, -58, -41, 35, -52, -15, -11, -80, 111, -73, 19, -54, -22, 94, 91, 82, 3, 48, 0, 21, 43, -115, 64, 54, -89, -89, -76, 57, -97, -109, -118, 6, -124, 52, -59, -25, -28, -120, -126, -43, 111, 45, 84, -72, 16, 0, -109, 37, -109, 7, -64, 100, 7, 9, 95, 11, -60, -17, 81, -105, 45, 62, 105, 107, -82, -108, 99, -115, 70, 64, -23, 5, 2, -7, -22, 121, -8, Byte.MIN_VALUE, -41, 22, -58, 81, 48, -7, -17, 63, 122, 108, -50, -75, -43, -112, 76, 108, -111, 1, 80, 39, 95, -116, -92, 35, -86, -17, 60, -36, -3, 116, 78, -1, -40, -9, -70, 71, 85, 100, -65, 44, 30, 85, -122, Byte.MIN_VALUE, -50, -98, 126, -73, -117, 22, 30, 53, 114, -84, 32, 50, -83, -88, -9, 124, -18, -60, 6, -97, 96, 62, -83, 108, -51, -53, 85, -93, 117, -32, 91, 47, 97, 49, -70, 3, -12, 125, 23, -76, -126, -8, 119, 100, 62, 82, 20, 101, 28, -120, 15, -106, 66, -72, 14, 24, 48, -29, -119, 4, -43, -93, 89, 27, 122, -27, 3, -53, -61, -84, 12, -119, -79, -58, 15, 20, 70, -123, -85, 48, 102, 0, -9, -118, -5, 70, 21, -99, -89, -94, -67, -26, -98, 66, -14, -22, -116, -85, -122, 42, 38, -43, 58, 116, 96, 16, 121, 8, 75, -21, 76, 56, 124, -104, -8, 109, 44, -59, -6, 106, 78, 33, -63, -21, -77, 48, 107, 0, -95, 58, -40, -59, 13, -124, 116, 67, -20, -113, 84, -5, -2, -101, 91, 28, 0, -109, -101, 24, -99, 110, 100, 112, 112, -73, 84, 38, 54, 110, 36, -122, -18, 22, Byte.MAX_VALUE, 123, -118, -114, -122, 122, 39, 92, -91, -87, -115, 126, -100, -86, -58, -15, -47, 97, -95, 13, 65, -111, 113, -65, 28, 28, 111, 89, 13, 0, 6, -80, -108, -13, -53, 121, -15, Byte.MIN_VALUE, -38, -63, -45, 115, -32, 48, 77, -43, 45, 40, -4, -72, -46, 37, -92, 68, 90, -72, 8, 3, -49, 37, -49, -114, 110, -25, -25, 41, 13, 109, -67, -22, -126, 9, 126, -58, 27, 21, 77, -101, 77, -43, 39, 100, 98, 24, 94, 88, -108, 86, -80, 84, -65, 92, -124, 44, -7, 119, -36, 99, 113, 96, 75, -91, 59, 52, -4, -77, -62, 90, -106, -91, 89, 67, -59, 114, 8, -90, -72, 8, 69, -29, 74, 115, -35, 81, -63, 98, 67, -89, -57, 74, 2, 32, 25, 54, 89, 36, 17, 75, 57, 57, -26, 4, 65, 41, -58, -125, -1, -9, -76, 112, -41, 54, 69, -2, 56, -26, 17, -35, 46, 93, -76, 61, 110, 42, -5, -104, -63, -49, -60, 15, -70, 30, -107, 6, 14, 2, 80, 31, -70, 29, -70, 18, -19, 100, -40, 123, -45, 122, -20, -112, 94, 90, -117, -60, 97, Byte.MIN_VALUE, 59, -91, 97, -3, 65, -76, -78, 16, 83, -89, 30, -18, 112, -65, -26, -58, 94, -125, 68, -7, 40, -70, 68, -74, 77, -43, 79, 26, 54, -24, -80, -86, 43, 78, -78, 44, 50, -17, -14, -74, 116, -21, -74, -23, 6, 75, -32, -71, 118, 126, -8, -115, 40, 61, -77, 111, 100, 109, -50, 17, 3, -29, 57, -69, 89, -74, 60, 39, -29, 47, 28, -112, -62, -115, 26, -110, -87, -3, -125, 23, 65, 123, -112, -68, -66, -30, -16, -77, 83, -51, 120, -119, -124, -116, -104, -126, 89, -35, -39, -22, 99, -78, Byte.MAX_VALUE, 58, 84, -1, 113, 117, 22, 83, 7, -76, 52, 63, 46, -42, 3, 95, -76, -5, Byte.MIN_VALUE, -127, 35, -24, 24, -77, -14, 37, 27, 14, -45, 123, -3, 67, -101, 79, -112, 67, 7, -18, 99, -36, 30, -42, 56, -73, -65, 33, -90, 12, -34, -109, 38, -82, -16, 43, 64, 105, -64, -51, -18, -110, -117, -93, 33, 37, -126, 43, -122, -36, 17, -28, 3, 124, 87, -2, 68, -29, 37, 101, 46, 25, -70, 52, -61, -57, 19, -3, 45, -46, -100, 84, 50, 34, -32, -124, -90, 92, 96, 63, 97, 29, -47, 72, -44, 110, 104, -59, -6, -52, -58, Byte.MAX_VALUE, 39, 99, -121, -73, -60, 10, -32, 122, 114, -52, 124, 8, 36, -1, 71, -21, 6, 25, 45, 76, -80, -27, 125, -54, -83, -11, -26, -12, -97, 28, -26, -32, 2, 66, -38, 26, -87, -59, -70, 90, -21, 12, -47, 108, -105, -83, -39, 123, 27, 106, -97, -3, -99, 61, 78, -49, -62, -116, 23, 39, -37, -123, 114, 42, -83, -121, 0, -75, -74, 59, 100, -71, -91, -54, -88, -30, -37, 41, 114, 96, 0, 107, 34, -15, -84, 86, -98, -89, -22, 15, 41, -57, -124, 111, 97, -70, -20, -122, 118, 5, -118, 83, 97, -79, 126, -45, 59, -45, -87, -88, -107, -4, 82, -88, -31, 120, -27, -97, -13, 21, -112, 46, -75, 94, -65, -67, -42, -122, 100, 35, 6, -86, Byte.MIN_VALUE, 80, 55, 84, 17, -43, 30, -87, 103, 5, 89, -96, 117, 46, -62, -1, 108, -85, 31, 22, -20, -68, -66, 12, -103, 60, 26, -59, -113, -33, 20, -62, -37, -57, -55, 91, -4, 92, 27, -33, 97, -28, 83, -81, -75, 57, -122, 54, -89, 25, -22, 81, -3, 111, -22, -126, -117, -14, -5, 99, -22, 7, -98, 91, 83, 108, -56, -3, 96, -81, 86, 88, -58, 4, -119, 101, -74, 79, 102, -58, -24, 6, -23, 15, 53, -64, 86, -115, 57, -118, -62, -100, 4, 30, -14, 66, 110, -65, 5, 38, 46, 64, -36, 96, -61, -9, 13, 4, -40, -7, 28, -122, -107, -71, -17, 26, -92, 74, -9, -94, 13, -40, 50, -120, -106, 6, -49, 96, -113, -30, 51, -53, 93, -99, -11, -55, -61, -60, -8, -25, -20, 98, 95, -75, 25, 50, -102, -102, -51, 69, 71, 90, -83, -57, 90, 20, -16, -85, 9, -17, -19, -2, 14, 108, -33, -56, 108, 100, 97, 62, -59, 28, -91, -116, -85, -74, Byte.MAX_VALUE, -117, 34, -43, 104, 27, 8, 74, 75, -1, -93, -123, -15, 99, 48, -16, 106, -104, 10, 39, 88, 124};
            return f28562d;
        }

        /* renamed from: c */
        public final byte[] getArray1() {
            final byte[] f28561c = {90, -74, 78, -28, -115, 37, -102, -41, 120, 116, -123, 36, -42, 107, 69, -42, 113, 48, -124, 125, 116, 12, 65, 20, -56, 111, -25, 95, -85, -14, 21, 123, 54, -12, 89, 53, -27, -124, 31, 24, -13, -104, 96, 48, -78, -53, 12, -85, 18, -35, -31, 110, 20, 74, 108, -58, -70, -79, 69, 44, 6, -22, 85, -99, -1, -88, 107, -11, 57, -20, 23, -92, 114, -27, 26, 10, 7, -84, Byte.MAX_VALUE, -75, -125, 123, -85, 15, -32, -13, 39, -46, 40, -15, -117, 40, -2, -74, 112, -65, -35, -117, -79, -8, -94, -97, -105, 38, -80, 102, -91, 35, 21, 93, 49, 63, 31, 41, 65, 48, -85, 126, 64, 70, 116, 99, 38, 116, -92, 73, 49, -49, 6, 101, -80, 46, 4, 26, -78, 118, 116, -29, -19, -89, -56, 49, -54, 110, -33, -39, -53, -72, 70, 14, 54, -127, 34, -52, -10, 44, -50, -83, -92, -42, -103, 118, 97, 72, 100, 7, -127, -27, -30, -98, -42, -23, Byte.MIN_VALUE, -82, 95, -122, -84, -81, -6, -126, -65, 26, 35, -8, -77, -114, 58, 99, 19, 67, 110, 101, -95, -40, 100, 53, -115, 0, -83, -4, 100, 79, -41, 112, -33, -109, -86, 41, 58, -2, 3, -28, -98, 102, 57, -63, -127, -21, -114, -34, -58, 94, -41, -120, 20, 106, -57, -121, -116, -98, -69, -76, -69, -12, -108, 64, 69, -23, -1, 105, 82, 54, -53, -16, -117, 27, -27, -67, -15, -45, -46, 75, -66, 18, 56, 86};
            return f28561c;
        }
    }

Then simply ran the code in an otherwise blank Android application project and it printed the private key to the console. The public certificate in the above bit of code is the public cert, too.

thegnomewizard avatar Nov 04 '22 05:11 thegnomewizard

@thegnomewizard wow great! It's interesting, because I was trying to do exactly the same, but my outputs were not good, always it was some garbage or something, I thought the JADX is decompiling it wrongly. Although, I was running the code on normal Java on PC, not inside Android, so maybe that is the trick. Thanks!

gamelaster avatar Nov 04 '22 06:11 gamelaster

@gamelaster it'd be interesting to know what went wrong - I think the only really fiddly bit I noticed was in the helper function. It has a little bit of code that JADX decompiles wrong - it comes out as something like byte b = bArr2[i2] & 255; but as is that will cause the output to be nonsense, because it gets the sign wrong (the code is actually a standard Java trick for doing bitwise operations, int b = bArr2[i2] & 255;), after I fixed that I got the correct results.

thegnomewizard avatar Nov 05 '22 04:11 thegnomewizard

@thegnomewizard, could you share how you got the values for AnotherProvider, in particular getArray1/getArray2? Did you do a memory dump or some further processing of decompiled APK? If memdump, what tools did you use (I am no Android expert so a high level instruction would be most welcome)?

tomasz-grobelny avatar Nov 14 '22 01:11 tomasz-grobelny

@tomasz-grobelny it was all via the decompiled APK - I used jadx-gui to decompile (as much as it could) then looked up the function referenced above your comment from 8th October. From that example, "AnotherProvider" is the parameter passed in, iwa iwa, and the functions getString is mo16269a(), getArray1 is mo16270b() and getArray2 is mo16271c() (or the other way around, I forget). As such, you can search the entire APK via jadx-gui for iwa, mo16269a, mo16270b and mo16271b, all of which should return both the root iwa interface and any classes implementing them. One of the classes should contain the correct public certificate + binary arrays (and nothing else). It actually just pulls the byte arrays back from the class containing m23915f() though.

I was initially confused as I was using an older APK (I used a 'download APK' site and I suspect they cached an old one). Getting a real phone also meant it updated itself and the APK on the device had a valid cert and changed byte arrays to match.

thegnomewizard avatar Nov 14 '22 03:11 thegnomewizard

I don't understand all the lower-level stuff here but appreciate the work! Please let me know when the new cert can be checked in to the codebase here, or share with me privately if you'd like me to test on my hardware (2021 Kia Niro and 2020 Harley)

kevinlieb avatar Nov 15 '22 00:11 kevinlieb

I can confirm that @thegnomewizard's method is still working for com.google.android.projection.gearhead_9.4.631627-release-94631627 🥳

gamelaster avatar Jun 05 '23 09:06 gamelaster

A bit late to the party... I was wondering why this rabbit and tortoise game ? If I understand correctly, the head unit isn't updated with new google certificate anyway once its delivered. I'm guessing that there is no certificate pinning in the unit so it's working with any device's certificate that's signed with Google's key.

So the certificate validation is only for the "device". This probably means that you can use any Google's certificate (even the one from 2022) for bootstrapping the communication. What is important is for the device to accept to use that certificate, so said differently, either adjust its date & time to make it believe it's in 2022 or write a X509TrustManager that completely ignore the expiration date of the certificate.

That way, you don't enter the course for finding the latest certificate from Google obfuscated source.

Am I missing something?

Here's and example TrustManager class that's ignoring the certificate validity. It should be used in the server's code (or its equivalent if not using Java).

X-Ryl669 avatar Feb 07 '24 06:02 X-Ryl669

This might work, but I actually find easier to dump the keys from APK than doing all of this :D

gamelaster avatar Feb 07 '24 08:02 gamelaster