Home > Software design >  OkHttp client authentication failing to validate server and send client certificate in the same requ
OkHttp client authentication failing to validate server and send client certificate in the same requ

Time:10-20

I keep editing this question as I dig further into it.

EDIT I'm able to build my OkHttp client to where it includes both the client cert in the Client.SSLContext.KeyManager, and the trusted certs in the Client.SSLContext.TrustManager

// Create keyManagerFactory with keystore.jks
KeyStore clientStore = KeyStore.getInstance(KeyStore.getDefaultType());
clientStore.load(new FileInputStream(new File("keystore.jks")), storePassword.toCharArray());

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientStore, storePassword.toCharArray());
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
        
// Create trustManagerFactory with default cacerts truststore
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:"
                                                  Arrays.toString(trustManagers));
        }
trustManager = trustManagers[0];

// Create sslContext from keyManagers (from custom keystore with client key) and default trustManagers
sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
sslSocketFactory = sslContext.getSocketFactory();
defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();

okClient = new OkHttpClient
                 .Builder()
                 .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager)
                 .build();

However, my client still isn't sending my client certificate (server cert is validated through the trust store successfully). Getting this in the ssl debug logs

No X.509 certificate for client authentication, use empty Certificate message instead

Here's what my SSLContext looks like on the HttpClient. enter image description here Seems like that should send the client cert named "cureskeystore" in the request?

keystore.jks is built with the following commands

openssl pkcs12 -export \
        -name curesKeyStore \
        -in clientCert.crt \
        -inkey privateKey.pem \
        -certfile clientCert.crt \
        -out chain.p12 \
        -passout pass:${STORE_PASSWORD}

keytool -importkeystore \
        -srckeystore chain.p12 \
        -srcstoretype pkcs12 \
        -destkeystore keystore.jks \
        -deststoretype pkcs12 \
        -storepass ${STORE_PASSWORD} \
        -srcstorepass ${STORE_PASSWORD} > /dev/null 2>&1

I have also tried creating a store with the client cert -CAfile with the root and intermediate certs:

# client cert with CAcerts included
openssl pkcs12 -export -chain \
        -in clientCert.crt \
        -inkey privateKey.pem \
        -out keystore.p12 \
        -name p12KeyStore \
        -CAfile caCerts.crt \
        -caname root \
        -passout pass:${STORE_PASSWORD}

keytool -importkeystore \
        -srcstoretype PKCS12 \
        -destkeystore keystore.jks \
        -srckeystore keystore.p12 \
        -alias p12KeyStore \
        -storepass ${STORE_PASSWORD} \
        -srcstorepass ${STORE_PASSWORD}

Another possible issue is the CertificateRequest not matching my client certificate.

javax.net.ssl|DEBUG|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:671|Consuming CertificateRequest handshake message (
"CertificateRequest": {
  "certificate types": [ecdsa_sign, rsa_sign, dss_sign]
  "supported signature algorithms": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha224, rsa_sha224, dsa_sha224, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]
  "certificate authorities": [redacted, but does not include Entrust]
}
)
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp256r1_sha256
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp384r1_sha384
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp521r1_sha512
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha256
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha384
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha512
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha256
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha384
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha512
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha256
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha384
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha512
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha256
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for EC
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_sha224
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_sha224
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha224
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for EC
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_sha1
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha1
javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha1
javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:774|No available authentication scheme

My certificate's signing algorithm is SHA256withRSA. Is that not the same as rsa_pkcs1_sha256? Also, my client certificate is signed by Entrust, which is not listed in the certificate authorities for the server's CertificateRequest.

EDIT: I made some requests to a different HTTPS server that does not include certificate authorities in its CertificateRequest to the client. I verified that SSL can find the expected client certificate and sends it back to the server as expected. So it seems like this is an issue with the server request not including my CA in their list of accepted certificate authorities. Reaching out to the server to request an update.

CodePudding user response:

Okay; it has developed your problem is that when the server requests your client-cert/auth, it specifies a CA list that doesn't include the CA(s?) used by your cert-and-chain, even though when presented with your cert-and-chain the server accepts it. After commenting about writing a wrapper KeyManager, I realized it would be easy enough to test, and the following example works for me to send a client cert different from what the server asked for. I used SSLSocket directly for simplicity, but anything (like OkHttp) using the same SSLContext or SSLSocketFactory should work. Tested in 8u301 (but I can check some others if you want) against OpenSSL commandline, which lets me request client cert for CA X but when I submit a cert from CA Y it only logs the verification error without aborting the connection.

public class SO69577136KeyManagerIgnoreCAs  {
    public static void main (String[] args) throws Exception {
        // keystore.p12 pw truststore.p12 pw host port [Y: wrap KM to ignore issuers]
        KeyStore st = KeyStore.getInstance("PKCS12");
        try( InputStream is = new FileInputStream(args[0]) ){ st.load(is,args[1].toCharArray()); }
        KeyManagerFactory kf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kf.init(st,  args[1].toCharArray());
        KeyManager[] km = kf.getKeyManagers();
        try( InputStream is = new FileInputStream(args[2]) ){ st.load(is,args[3].toCharArray()); }
        TrustManagerFactory tf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tf.init(st);
        TrustManager[] tm = tf.getTrustManagers();
        
        if( args.length>6 && args[6].startsWith("Y") ){
            X509ExtendedKeyManager orig = (X509ExtendedKeyManager)km[0]; // exception if wrong type
            km[0] = new X509ExtendedKeyManager(){

                @Override
                public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
                    return orig.chooseClientAlias(keyType, null, socket);
                }

                @Override
                public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
                    // not implemented
                    return null;
                }

                @Override
                public X509Certificate[] getCertificateChain(String alias) {
                    return orig.getCertificateChain(alias);
                }

                @Override
                public String[] getClientAliases(String keyType, Principal[] issuers) {
                    // shouldn't actually be used AFAICT but just in case
                    return orig.getClientAliases(keyType, issuers);
                }

                @Override
                public PrivateKey getPrivateKey(String alias) {
                    return orig.getPrivateKey(alias);
                }

                @Override
                public String[] getServerAliases(String keyType, Principal[] issuers) {
                    // not implemented
                    return null;
                }

                public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
                    return orig.chooseEngineClientAlias(keyType, null, engine);
                    // could just forward to chooseClientAlias(socket=null), that's what underlying does
                }

                public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
                    // not implemented
                    return null;
                }
            };
        }
        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(km, tm, null /* default */);
        SSLSocketFactory sf = ctx.getSocketFactory();
        SSLSocket ss = (SSLSocket) sf.createSocket(args[4], Integer.parseInt(args[5]));
        ss.startHandshake();
        System.out.println ("successful");
    }
}
  • Related