From 6ba4419e1bc08b16a41bf2f83000e5330b4b4e0b Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Tue, 19 Sep 2023 11:35:13 +0200 Subject: [PATCH] Add a script to directly inject one always-trusted cert into Conscrypt This is a notably different approach to the past certificate unpinning setup. Through this new mechanism, we trust just one additional certificate (instead of disabling cert verification entirely) and handle just the standard Android certificate trust (we'll integrate unpinning into this later). This uses a hardcoded cert for now just for testing, but that will of course be configurable in future. --- android-system-certificate-injection.js | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 android-system-certificate-injection.js diff --git a/android-system-certificate-injection.js b/android-system-certificate-injection.js new file mode 100644 index 0000000..fa15d3e --- /dev/null +++ b/android-system-certificate-injection.js @@ -0,0 +1,59 @@ +const CERT_PEM = `-----BEGIN CERTIFICATE----- +MIIDTzCCAjegAwIBAgIRClDpdJeylUmDvNyq5qq+plcwDQYJKoZIhvcNAQELBQAw +QTEYMBYGA1UEAxMPSFRUUCBUb29sa2l0IENBMQswCQYDVQQGEwJYWDEYMBYGA1UE +ChMPSFRUUCBUb29sa2l0IENBMB4XDTIyMTIyNTE5MDQ1MFoXDTIzMTIyNjE5MDQ1 +MFowQTEYMBYGA1UEAxMPSFRUUCBUb29sa2l0IENBMQswCQYDVQQGEwJYWDEYMBYG +A1UEChMPSFRUUCBUb29sa2l0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAz4pwm0pLDvf8qVmAiOi2cvu8xDgboetoLWBoONOY2wvoEFRylLUGaieP +UG5Yuofcj798uYPEqPLoF2ugnw8J/lhYhkMqTEbuqoyZT7DooBiqtSbm4b++T/Zt +F6YnpkYeWIkv88UJaRvLG8OHytVbiC71JQ/DFCEjzNzATCKT7UFqyF4ZsT3cnGJe +3x1iiSzWxJsnDkNZmiQ+IDYSM/dx7RJYwrXO5oWbAHC7otdC66O9eB1uBYq9I8gU +4FcVKHbWAH1BYbsoF/pQJLz2mAXD7E92/Vvho7FgTKYOUq0b58LF9UHt+gDRUGUC +L4HtuMeb/Ckiwyoej50jqI//ER1XswIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBxjAdBgNVHQ4EFgQUfsk69D2mzcAFjDX0GV53o3gySMMw +DQYJKoZIhvcNAQELBQADggEBAH08sPexRXFxzuIVWD1aUoarYq8FwNBP+xusgZcg +DmRKr0FpEyUjBxgVIsmd44WSX/TWdXokdd7aLPVnjwQbq2mhYtXAn4aIRn3QBNaj +TvFQovVy+LCSRwZjvlpr/KJnXlVMrqLIxP++I6FqLO5G5zJ+qDF39C7RUkkMpvmU +tiS70/zpt2LSfLUNtnS287P9s4wXEwbrkOOY6oNgKDz7jtNua0ZiPq2uGdqrkyZ2 +VPgirbNnuoC1uZmuy0Mvih4+8xrvJWHb9QO7JOH3cXA+ZiZ945V+viMEnV0YkQB6 +FL11l3fE9xzkPv357Z3e7QULnC8vDRgFAossuh8WBhNjjmo= +-----END CERTIFICATE-----`; + +Java.perform(() => { + // First, we build a JVM representation of our certificate: + const String = Java.use("java.lang.String"); + const ByteArrayInputStream = Java.use('java.io.ByteArrayInputStream'); + const CertFactory = Java.use('java.security.cert.CertificateFactory'); + + const certFactory = CertFactory.getInstance("X.509"); + const certBytes = String.$new(CERT_PEM).getBytes(); + const cert = certFactory.generateCertificate(ByteArrayInputStream.$new(certBytes)); + + // Then we hook TrustedCertificateIndex. This is used for caching known trusted certs within Conscrypt - + // by prepopulating all instances, we ensure that all TrustManagerImpls (and potentially other + // things) automatically trust our certificate specifically (without disabling validation entirely). + // This should apply to Android v7+ - previous versions used SSLContext & X509TrustManager. + const TrustedCertificateIndex = Java.use('com.android.org.conscrypt.TrustedCertificateIndex'); + TrustedCertificateIndex.$init.overloads.forEach((overload) => { + overload.implementation = function () { + this.$init(...arguments); + // Index our cert as already trusted, right from the start: + this.index(cert); + } + }); + + TrustedCertificateIndex.reset.overloads.forEach((overload) => { + overload.implementation = function () { + const result = this.reset(...arguments); + // Index our cert in here again, since the reset removes it: + this.index(cert); + return result; + }; + }); + + // This effectively adds us to the system certs, and also defeats quite a bit of basic certificate + // pinning too! It auto-trusts us in any implementation that uses TrustManagerImpl (Conscrypt) as + // the underlying cert checking component. + + console.log('Inject system certificate trust'); +}); \ No newline at end of file