Is there a way of using Client Certificates with okhttp?
Probably. But I haven't done it, and we don't have any guides on how to do it, or test cases that keep it working. If you'd like to do the legwork of figuring that out, I'd love to see a sample.
Most of the work is going to go in the SSL socket factory code, which OkHttp mostly just delegates to.
Ok, I don't know much about Java, but let me see what I can find. Can you share with me what you use as an editor?
In OkHttpClient.java in okhttp/src/main
private synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
if (defaultSslSocketFactory == null) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
defaultSslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
}
return defaultSslSocketFactory;
}
Hmm, what does this mean:
"This code avoids that by defaulting to an OkHttp-created SSL context.
sslContext.init looks like
public final void init(KeyManager[] km,
TrustManager[] tm,
SecureRandom random)
According to http://chariotsolutions.com/blog/post/https-with-client-certificates-on/, the KeyManager is what will provides the client certificate. Given a pfx,
keyStore = KeyStore.getInstance("PKCS12");
fis = new FileInputStream(certificateFile);<br>keyStore.load(fis, clientCertPassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, clientCertPassword.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, null, null);
Update:
Another example,
http://stackoverflow.com/questions/21624663/ssl-client-authentication-with-certificate-in-android
Maybe something like below. Also could be extended to allow for specifying your own ca.
private synchronized SSLSocketFactory getDefaultSSLSocketFactory(
byte[] pksData, String password) {
if (defaultSslSocketFactory == null) {
try {
InputStream stream = new ByteArrayInputStream(pksData);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(stream, password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, password.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, null, null);
defaultSslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
}
return defaultSslSocketFactory;
}
Can I specify my own sslSocketFactory?
Looks like I could use
public OkHttpClient setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
return this;
}
And create my own new socket factory, though I think there probably should be a way of setting the ClientCertificate. Maybe
public OkHttpClient setClientCertificate
would be used to recreate a sslSocketFactory.
Any chance you can add this kind of thing to the build?
BTW, I calling OkHttp from Xamarin.Android (C#) using ModernHttpClient.
Tofutim, were you able to successfully use client certificate in Xamarin? Can you share the code?
@tofutim could you kindly share your findings?
The answer is that it should work, but I need to test it. Maybe someone
could test my code?
On Tue, Sep 29, 2015 at 8:54 PM, Xu Hui Hui [email protected]
wrote:
@tofutim https://github.com/tofutim could you kindly share your
findings?—
Reply to this email directly or view it on GitHub
https://github.com/square/okhttp/issues/1711#issuecomment-144274995.
OkHttp 3.2.0 does not honour javax.net.ssl.keyStore on Java 1.8.0_65 (and I would guess for all platforms) when set up like:
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
The above code will result in Received fatal alert: handshake_failure when connecting to a site that requires client certificates. But the following _will_ honour javax.net.ssl.keyStore:
SSLSocketFactory sslSocketFactory = SSLContext.getDefault().getSocketFactory();
OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory).build();
This is unintuitive, and I believe a consequence of calling SSLContext.init() with keyManager set to null. trustStore works, on the other hand, I think because of https://github.com/square/okhttp/blob/fb3c390590131e61562bede0a6e030705ccbe8ce/okhttp/src/main/java/okhttp3/OkHttpClient.java#L183,L196 .
I'll write test cases, and hopefully a fix to suitably set up keyStores for Java 1.8.0_65, at least.
We deliberately do not use the system’s default SSL context. The problem with that is that in some situations that context cannot safely be shared between OkHttp and other libraries. The result is your system will crash hard.
And so the workaround of manually providing an SSL context is just fine.
How about OkHttp continues using its own SSLContext, but that context
honours javax.net.ssl.keyStore? I can look into how to do that.
The rationale is that other HTTPS libraries honour
javax.net.ssl.keyStore, and it's a WTF that a library doesn't support it
by default. Further, debugging TLS to discover this is non-trivial. I
recognise that client certificates are uncommon and that changes to such
critical code should be treated with scepticism, however.
On Wed, 9 Mar 2016, 03:15 Jesse Wilson, [email protected] wrote:
And so the workaround of manually providing an SSL context is just fine.
—
Reply to this email directly or view it on GitHub
https://github.com/square/okhttp/issues/1711#issuecomment-194093017.
This is old, but I think you can close this as it definitely works
https://github.com/yschimke/oksocial/blob/master/src/main/java/com/baulsupp/oksocial/Main.java#L379
@tofutim the best way to test I found is with "openssl s_server -Verify ..."
Awesome.
Hi,
I am new here, but am trying to get a swagger-generated API client app working with our server, which requires client-authenticated SSL. The generated code uses com.squareup.okhttp.OkHttpClient, which is how I found this issue, but I am really having difficulty following the info here. Is there a clean example of what would be needed to enable client-authentication with okhttp?
It possibly deserves its own howto page. But it's not a one size fits all situation. You may have client certs on disk, you may have them in a keystore or you may have a token.
This test covers client auth https://github.com/square/okhttp/blob/c77023cd0369c679bf217506ae21d429e6e81bdf/okhttp-tests/src/test/java/okhttp3/internal/tls/ClientAuthTest.java#L108
This code handles either client auth from a government id card or from disk, but in different places.
It is possibile to specify Certificate Authority, private key and client certificate?
What I would like to know if there is the equivalent of
curl --cacert ca.pem --key client.key --cert client.crt
@romanofranz1992 yes, but you have to load the certificates yourself and call clientBuilder.sslSocketFactory
There are examples here https://github.com/square/okhttp/blob/c77023cd0369c679bf217506ae21d429e6e81bdf/okhttp-tests/src/test/java/okhttp3/internal/tls/ClientAuthTest.java#L108
and in an external Kotlin test client here
This is probably a better question for stackoverflow.
Hi @yschimke, Do you know if this solution works with grpc OkHttpChannelBuilder?
It does not.
Most helpful comment
It is possibile to specify Certificate Authority, private key and client certificate?
What I would like to know if there is the equivalent of
curl --cacert ca.pem --key client.key --cert client.crt