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.
This commit is contained in:
Tim Perry
2023-09-19 11:35:14 +02:00
parent 179d508e0f
commit 6ba4419e1b
+59
View File
@@ -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');
});