Skip to content

Commit

Permalink
Merge pull request square#2804 from yschimke/clientauth_example
Browse files Browse the repository at this point in the history
Clientauth example
  • Loading branch information
swankjesse authored Aug 23, 2016
2 parents c25fb7f + 45f9338 commit 65a1134
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ public static class Builder {
* the server's certificate, further certificates are included in the handshake so the client
* can build a trusted path to a CA certificate.
*/
public Builder certificateChain(HeldCertificate serverCert, HeldCertificate... chain) {
public Builder certificateChain(HeldCertificate localCert, HeldCertificate... chain) {
X509Certificate[] certificates = new X509Certificate[chain.length];
for (int i = 0; i < chain.length; i++) {
certificates[i] = chain[i].certificate;
}
return certificateChain(serverCert.keyPair, serverCert.certificate, certificates);
return certificateChain(localCert.keyPair, localCert.certificate, certificates);
}

public Builder certificateChain(KeyPair keyPair, X509Certificate keyCert,
Expand Down
221 changes: 221 additions & 0 deletions okhttp-tests/src/test/java/okhttp3/internal/tls/ClientAuthTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*
* Copyright (C) 2016 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.tls;

import java.io.IOException;
import java.net.ConnectException;
import java.security.GeneralSecurityException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.security.auth.x500.X500Principal;
import okhttp3.Call;
import okhttp3.DelegatingSSLSocketFactory;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

public final class ClientAuthTest {
@Rule public final MockWebServer server = new MockWebServer();

public enum ClientAuth {
NONE, WANTS, NEEDS;
}

private HeldCertificate serverRootCa;
private HeldCertificate serverIntermediateCa;
private HeldCertificate serverCert;
private HeldCertificate clientRootCa;
private HeldCertificate clientIntermediateCa;
private HeldCertificate clientCert;

@Before
public void initialise() throws GeneralSecurityException {
serverRootCa = new HeldCertificate.Builder()
.serialNumber("1")
.ca(3)
.commonName("root")
.build();
serverIntermediateCa = new HeldCertificate.Builder()
.issuedBy(serverRootCa)
.ca(2)
.serialNumber("2")
.commonName("intermediate_ca")
.build();

serverCert = new HeldCertificate.Builder()
.issuedBy(serverIntermediateCa)
.serialNumber("3")
.commonName(server.getHostName())
.build();

clientRootCa = new HeldCertificate.Builder()
.serialNumber("1")
.ca(13)
.commonName("root")
.build();
clientIntermediateCa = new HeldCertificate.Builder()
.issuedBy(serverRootCa)
.ca(12)
.serialNumber("2")
.commonName("intermediate_ca")
.build();

clientCert = new HeldCertificate.Builder()
.issuedBy(clientIntermediateCa)
.serialNumber("4")
.commonName("Jethro Willis")
.build();
}

@Test public void clientAuthForWants() throws Exception {
OkHttpClient client = buildClient(clientCert, clientIntermediateCa);

SSLSocketFactory socketFactory = buildServerSslSocketFactory(ClientAuth.WANTS);

server.useHttps(socketFactory, false);
server.enqueue(new MockResponse().setBody("abc"));

Call call = client.newCall(new Request.Builder().url(server.url("/")).build());
Response response = call.execute();
assertEquals(new X500Principal("CN=localhost"), response.handshake().peerPrincipal());
assertEquals(new X500Principal("CN=Jethro Willis"), response.handshake().localPrincipal());
assertEquals("abc", response.body().string());
}

@Test public void clientAuthForNeeds() throws Exception {
OkHttpClient client = buildClient(clientCert, clientIntermediateCa);

SSLSocketFactory socketFactory = buildServerSslSocketFactory(ClientAuth.NEEDS);

server.useHttps(socketFactory, false);
server.enqueue(new MockResponse().setBody("abc"));

Call call = client.newCall(new Request.Builder().url(server.url("/")).build());
Response response = call.execute();
assertEquals(new X500Principal("CN=localhost"), response.handshake().peerPrincipal());
assertEquals(new X500Principal("CN=Jethro Willis"), response.handshake().localPrincipal());
assertEquals("abc", response.body().string());
}

@Test public void clientAuthSkippedForNone() throws Exception {
OkHttpClient client = buildClient(clientCert, clientIntermediateCa);

SSLSocketFactory socketFactory = buildServerSslSocketFactory(ClientAuth.NONE);

server.useHttps(socketFactory, false);
server.enqueue(new MockResponse().setBody("abc"));

Call call = client.newCall(new Request.Builder().url(server.url("/")).build());
Response response = call.execute();
assertEquals(new X500Principal("CN=localhost"), response.handshake().peerPrincipal());
assertEquals(null, response.handshake().localPrincipal());
assertEquals("abc", response.body().string());
}

@Test public void missingClientAuthSkippedForWantsOnly() throws Exception {
OkHttpClient client = buildClient(null, clientIntermediateCa);

SSLSocketFactory socketFactory = buildServerSslSocketFactory(ClientAuth.WANTS);

server.useHttps(socketFactory, false);
server.enqueue(new MockResponse().setBody("abc"));

Call call = client.newCall(new Request.Builder().url(server.url("/")).build());
Response response = call.execute();
assertEquals(new X500Principal("CN=localhost"), response.handshake().peerPrincipal());
assertEquals(null, response.handshake().localPrincipal());
assertEquals("abc", response.body().string());
}

@Test public void missingClientAuthFailsForNeeds() throws Exception {
OkHttpClient client = buildClient(null, clientIntermediateCa);

SSLSocketFactory socketFactory = buildServerSslSocketFactory(ClientAuth.NEEDS);

server.useHttps(socketFactory, false);

Call call = client.newCall(new Request.Builder().url(server.url("/")).build());

try {
call.execute();
fail();
} catch (ConnectException expected) {
}
}

@Test public void invalidClientAuthFails() throws Throwable {
HeldCertificate clientCert2 = new HeldCertificate.Builder()
.serialNumber("4")
.commonName("Jethro Willis")
.build();

OkHttpClient client = buildClient(clientCert2);

SSLSocketFactory socketFactory = buildServerSslSocketFactory(ClientAuth.NEEDS);

server.useHttps(socketFactory, false);

Call call = client.newCall(new Request.Builder().url(server.url("/")).build());

try {
call.execute();
fail();
} catch (ConnectException expected) {
}
}

public OkHttpClient buildClient(HeldCertificate cert, HeldCertificate... chain) {
SslClient.Builder sslClientBuilder = new SslClient.Builder()
.addTrustedCertificate(serverRootCa.certificate);

if (cert != null) {
sslClientBuilder.certificateChain(cert, chain);
}

SslClient sslClient = sslClientBuilder.build();
return new OkHttpClient.Builder()
.sslSocketFactory(sslClient.socketFactory, sslClient.trustManager)
.build();
}

public SSLSocketFactory buildServerSslSocketFactory(final ClientAuth clientAuth) {
SslClient serverSslClient = new SslClient.Builder()
.addTrustedCertificate(serverRootCa.certificate)
.addTrustedCertificate(clientRootCa.certificate)
.certificateChain(serverCert, serverIntermediateCa)
.build();

return new DelegatingSSLSocketFactory(serverSslClient.socketFactory) {
@Override protected SSLSocket configureSocket(SSLSocket sslSocket) throws IOException {
if (clientAuth == ClientAuth.NEEDS) {
sslSocket.setNeedClientAuth(true);
} else if (clientAuth == ClientAuth.WANTS) {
sslSocket.setWantClientAuth(true);
}

return super.configureSocket(sslSocket);
}
};
}
}

0 comments on commit 65a1134

Please sign in to comment.