Is production: true
#migrated #java #tls

Title: How to use client certiificate in android okhttp

Created: 17 Feb 2021 Modified: 17 Feb 2021

Description:



Link reference

Gradle

    implementation 'com.squareup.okhttp3:okhttp:4.9.1'
    implementation 'io.reactivex.rxjava3:rxjava:3.0.10'
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'

Custom TrustManager

public class X509Impl implements X509KeyManager {
    private final String alias;
    private final X509Certificate[] certChain;
    private final PrivateKey privateKey;

    public static SSLContext setForConnection(HttpsURLConnection con, Context context, String alias) throws CertificateException, KeyManagementException {
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
        } catch(NoSuchAlgorithmException e){
            throw new RuntimeException("Should not happen...", e);
        }

        sslContext.init(new KeyManager[] { fromAlias(context, alias)}, null, null);
        con.setSSLSocketFactory(sslContext.getSocketFactory());
        return sslContext;
    }

    public static X509Impl fromAlias(Context context, String alias) throws CertificateException {
        X509Certificate[] certChain;
        PrivateKey privateKey;
        try {
            certChain = KeyChain.getCertificateChain(context, alias);
            privateKey = KeyChain.getPrivateKey(context, alias);
        } catch (KeyChainException e) {
            throw new CertificateException(e);
        } catch (InterruptedException e) {
            throw new CertificateException(e);
        }
        if(certChain == null || privateKey == null){
            throw new CertificateException("Can't access certificate from keystore");
        }

        return new X509Impl(alias, certChain, privateKey);
    }

    public X509Impl(String alias, X509Certificate[] certChain, PrivateKey privateKey) throws CertificateException {
        this.alias = alias;
        this.certChain = certChain;
        this.privateKey = privateKey;
    }

    @Override
    public String chooseClientAlias(String[] arg0, Principal[] arg1, Socket arg2) {
        return alias;
    }

    @Override
    public X509Certificate[] getCertificateChain(String alias) {
        if(this.alias.equals(alias)) return certChain;
        return null;
    }

    @Override
    public PrivateKey getPrivateKey(String alias) {
        if(this.alias.equals(alias)) return privateKey;
        return null;
    }


    // Methods unused (for client SSLSocket callbacks)
    @Override public final String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
        throw new UnsupportedOperationException();
    }

    @Override public final String[] getClientAliases(String keyType, Principal[] issuers) {
        throw new UnsupportedOperationException();
    }

    @Override public final String[] getServerAliases(String keyType, Principal[] issuers) {
        throw new UnsupportedOperationException();
    }
}

Example code

        @io.reactivex.rxjava3.annotations.NonNull BehaviorSubject<String> subject = BehaviorSubject.create();
        KeyChain.choosePrivateKeyAlias(requireActivity(), new KeyChainAliasCallback() {
            @Override
            public void alias(@Nullable String alias) {
                System.out.println(alias);
                subject.onNext(alias);

            }
        }, null, null, null, -1, null);

        subject
                .observeOn(Schedulers.io())
                .map(it->{
                    X509Certificate[] certChain = KeyChain.getCertificateChain(requireContext(), it);
                    PrivateKey certKey = KeyChain.getPrivateKey(requireContext(), it);

                    TrustManagerFactory trustManager = TrustManagerFactory.getInstance("X509");
                    KeyStore ks = KeyStore.getInstance("AndroidCAStore");

                    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                    trustStore.load(null,null);
                    if (ks != null) {
                        ks.load(null, null);
                        Enumeration<String> aliases = ks.aliases();

                        while (aliases.hasMoreElements()) {

                            String alias = (String) aliases.nextElement();

                            X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
                            trustStore.setCertificateEntry(alias, cert);

                            //To print all certs
                            System.out.println(cert.getIssuerDN().getName());
                        }
                    }

                    trustManager.init(trustStore);
                    X509Impl x509 = new X509Impl(it, certChain, certKey);

                    SSLContext sslContext = SSLContext.getInstance("TLS");
                    sslContext.init(new KeyManager[]{x509}, trustManager.getTrustManagers(), null);
                    OkHttpClient.Builder builer = new OkHttpClient.Builder();

                    TrustManager[] ms = trustManager.getTrustManagers();
                    builer.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) ms[0]);

                    return builer.build();
                })
                .map(it->{
                    OkHttpClient client = it;
                    Request request = new Request.Builder()
                            .url("https://www.dextro.link")
                            .build();;
                    Response response = client.newCall(request).execute();

                    return response;

                })
                .subscribe()
                ;

[Legacy Link]