Search code examples
javaandroidsslx509certificateandroid-keystore

Add multiple SSL certificate pinning to Android KeyStore doesn't work. (from Resource file)


I want to add multiple SSL certificates from a ressource file to the Android KeyStore as follow:

if (sslContext==null) {
        // loading CA from an InputStream
        InputStream is = AVApplication.getContext().getResources().openRawResource(R.raw.wildcard);
        String certificates = Converter.convertStreamToString(is);
        String certificateArray[] = certificates.split("-----BEGIN CERTIFICATE-----");

        for (int i = 1; i < certificateArray.length; i++) {
            certificateArray[i] = "-----BEGIN CERTIFICATE-----" + certificateArray[i];
            //LogAV.d("cert:" + certificateArray[i]);

            // generate input stream for certificate factory
            InputStream stream = IOUtils.toInputStream(certificateArray[i]);

            // CertificateFactory
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            // certificate
            Certificate ca;
            try {
                ca = cf.generateCertificate(stream);
            } finally {
                is.close();
            }

            // creating a KeyStore containing our trusted CAs
            KeyStore ks = KeyStore.getInstance("BKS");
            ks.load(null, null);
            ks.setCertificateEntry("av-ca" + i, ca);

            // TrustManagerFactory
            String algorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
            // Create a TrustManager that trusts the CAs in our KeyStore
            tmf.init(ks);

            // Create a SSLContext with the certificate that uses tmf (TrustManager)
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
        }

    }

    return sslContext;

Only the last certificate of the file works! It seems the certificate overwrites the other one.

File looks like:

-----BEGIN CERTIFICATE-----
    cert 
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
    cert 
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
    cert 
-----END CERTIFICATE-----

I hope somebody can help me! :)


Solution

  • Thx to @Dan Getz, now it works.

    1. Solution with SSL context & Self signed Certificate:

    public static SSLContext getSSLContext() throws Exception {
            if (sslContext==null) {
                // loading CA from an InputStream
                InputStream is = AVApplication.getContext().getResources().openRawResource(R.raw.certificates);
                String certificates = Converter.convertStreamToString(is);
                String certificateArray[] = certificates.split("-----BEGIN CERTIFICATE-----");
    
                // creating a KeyStore containing our trusted CAs
                KeyStore ks = KeyStore.getInstance("BKS");
                ks.load(null, null);
                for (int i = 1; i < certificateArray.length; i++) {
                    certificateArray[i] = "-----BEGIN CERTIFICATE-----" + certificateArray[i];
                    //LogAV.d("cert:" + certificateArray[i]);
    
                    // generate input stream for certificate factory
                    InputStream stream = IOUtils.toInputStream(certificateArray[i]);
    
                    // CertificateFactory
                    CertificateFactory cf = CertificateFactory.getInstance("X.509");
                    // certificate
                    Certificate ca;
                    try {
                        ca = cf.generateCertificate(stream);
                    } finally {
                        is.close();
                    }
    
                    ks.setCertificateEntry("av-ca" + i, ca);
                }
                // TrustManagerFactory
                String algorithm = TrustManagerFactory.getDefaultAlgorithm();
                TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
                // Create a TrustManager that trusts the CAs in our KeyStore
                tmf.init(ks);
    
                // Create a SSLContext with the certificate that uses tmf (TrustManager)
                sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
            }
    
            return sslContext;
        }
    

    Then using the SSL context:

    client = okHttpClient.newBuilder()
             .sslSocketFactory(getSslContext(context).getSocketFactory())
             .build();
    

    2. Solution with Pinning a not root certificate with OkHttp via fingerprints:

    Pinning a non root CA, I'm using the CertificatePinner from OkHttp (! this does not work for self-signed certificate - root CAs):

    CertificatePinner = new CertificatePinner.Builder()
                .add(new URL(url).getHost(), "sha256/<certificate1 fingerprint [base64]>")
                .add(new URL(url).getHost(), "sha256/<certificate2 fingerprint [base64]>")
                .build();
    OkHttpClient client;
        client = okHttpClient.newBuilder()
            .certificatePinner(certificatePinner)
            .build();