1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
OkHttp TLS
==========
Approachable APIs for using TLS.
A [`HeldCertificate`][held_certificate] is a certificate and its private key. Use the
[builder][held_certificate_builder] to create a self-signed certificate that a test server can use
for HTTPS:
```java
String localhost = InetAddress.getByName("localhost").getCanonicalHostName();
HeldCertificate localhostCertificate = new HeldCertificate.Builder()
.addSubjectAlternativeName(localhost)
.build();
```
[`HandshakeCertificates`][handshake_certificates] keeps the certificates for a TLS handshake.
Use its [builder][handshake_certificates_builder] to define which certificates the HTTPS server
returns to its clients. The returned instance can create an `SSLSocketFactory` that implements this
policy:
```java
HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()
.heldCertificate(localhostCertificate)
.build();
MockWebServer server = new MockWebServer();
server.useHttps(serverCertificates.sslSocketFactory(), false);
```
`HandshakeCertificates` also works for clients where its job is to define which root certificates
to trust. In this simplified example we trust the server's self-signed certificate:
```java
HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
.addTrustedCertificate(localhostCertificate.certificate())
.build();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
.build();
```
With a server that holds a certificate and a client that trusts it we have enough for an HTTPS
handshake. The best part of this example is that we don't need to make our test code insecure with a
a fake `HostnameVerifier` or `X509TrustManager`.
Certificate Authorities
-----------------------
The above example uses a self-signed certificate. This is convenient for testing but not
representative of real-world HTTPS deployment. To get closer to that we can use `HeldCertificate`
to generate a trusted root certificate, an intermediate certificate, and a server certificate.
We use `certificateAuthority(int)` to create certificates that can sign other certificates. The
int specifies how many intermediate certificates are allowed beneath it in the chain.
```java
HeldCertificate rootCertificate = new HeldCertificate.Builder()
.certificateAuthority(1)
.build();
HeldCertificate intermediateCertificate = new HeldCertificate.Builder()
.certificateAuthority(0)
.signedBy(rootCertificate)
.build();
String localhost = InetAddress.getByName("localhost").getCanonicalHostName();
HeldCertificate serverCertificate = new HeldCertificate.Builder()
.addSubjectAlternativeName(localhost)
.signedBy(intermediateCertificate)
.build();
```
To serve this configuration the server needs to provide its clients with a chain of certificates
starting with its own and including everything up-to but not including the root. We don't need to
include root certificates because the client already has them.
```java
HandshakeCertificates serverHandshakeCertificates = new HandshakeCertificates.Builder()
.heldCertificate(serverCertificate, intermediateCertificate.certificate())
.build();
```
The client only needs to know the trusted root certificate. It checks the server's certificate by
validating the signatures within the chain.
```java
HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
.addTrustedCertificate(rootCertificate.certificate())
.build();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
.build();
```
Client Authentication
---------------------
The above scenario is representative of most TLS set ups: the client uses certificates to validate
the identity of a server. The converse is also possible. Here we create a server that authenticates
a client and a client that authenticates a server.
```java
// Create the root for client and server to trust. We could also use different roots for each!
HeldCertificate rootCertificate = new HeldCertificate.Builder()
.certificateAuthority(0)
.build();
// Create a server certificate and a server that uses it.
HeldCertificate serverCertificate = new HeldCertificate.Builder()
.commonName("ingen")
.addSubjectAlternativeName(server.getHostName())
.signedBy(rootCertificate)
.build();
HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()
.addTrustedCertificate(rootCertificate.certificate())
.heldCertificate(serverCertificate)
.build();
MockWebServer server = new MockWebServer();
server.useHttps(serverCertificates.sslSocketFactory(), false);
server.requestClientAuth();
server.enqueue(new MockResponse());
// Create a client certificate and a client that uses it.
HeldCertificate clientCertificate = new HeldCertificate.Builder()
.commonName("ianmalcolm")
.signedBy(rootCertificate)
.build();
HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
.addTrustedCertificate(rootCertificate.certificate())
.heldCertificate(clientCertificate)
.build();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
.build();
// Connect 'em all together. Certificates are exchanged in the handshake.
Call call = client.newCall(new Request.Builder()
.url(server.url("/"))
.build());
Response response = call.execute();
System.out.println(response.handshake().peerPrincipal());
RecordedRequest recordedRequest = server.takeRequest();
System.out.println(recordedRequest.getHandshake().peerPrincipal());
```
This handshake is successful because each party has prearranged to trust the root certificate that
signs the other party's chain.
Well-Known Certificate Authorities
----------------------------------
In these examples we've prearranged which root certificates to trust. But for regular HTTPS on the
Internet this set of trusted root certificates is usually provided by default by the host platform.
Such a set typically includes many root certificates from well-known certificate authorities like
Entrust and Verisign.
This is the behavior you'll get with your OkHttpClient if you don't specifically configure
`HandshakeCertificates`. Or you can do it explicitly with `addPlatformTrustedCertificates()`:
```java
HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.build();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
.build();
```
PEM files
---------
You can encode a `HeldCertificate` in PEM format:
```java
HeldCertificate heldCertificate = ...
System.out.println(heldCertificate.certificatePem())
```
```
-----BEGIN CERTIFICATE-----
MIIBSjCB8aADAgECAgEBMAoGCCqGSM49BAMCMC8xLTArBgNVBAMTJDJiYWY3NzVl
LWE4MzUtNDM5ZS1hYWE2LTgzNmNiNDlmMGM3MTAeFw0xODA3MTMxMjA0MzJaFw0x
ODA3MTQxMjA0MzJaMC8xLTArBgNVBAMTJDJiYWY3NzVlLWE4MzUtNDM5ZS1hYWE2
LTgzNmNiNDlmMGM3MTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDmlOiZ3dxA2
zw1KwqGNsKVUZbkUVj5cxV1jDbSTvTlOjSj6LR0Ovys9RFdrjcbbMLWvSvMQgHch
k8Q50c6Kb34wCgYIKoZIzj0EAwIDSAAwRQIhAJkXiCbIR3zxuH5SQR5PEAPJ+ntg
msOSMaAKwAePESf+AiBlxbEu6YpHt1ZJoAhMAv6raYnwSp/A94eJGlJynQ0igQ==
-----END CERTIFICATE-----
```
You can also do so with the private key. Be careful with these!
```java
HeldCertificate heldCertificate = ...
System.out.println(heldCertificate.privateKeyPkcs8Pem())
```
```
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgQbYDQiewSnregm9e
IjXEHQgc6w3ELHdnH1houEUom9CgCgYIKoZIzj0DAQehRANCAAQ5pTomd3cQNs8N
SsKhjbClVGW5FFY+XMVdYw20k705To0o+i0dDr8rPURXa43G2zC1r0rzEIB3IZPE
OdHOim9+
-----END PRIVATE KEY-----
```
Recommendations
---------------
Typically servers need a held certificate plus a chain of intermediates. Servers only need the
private key for their own certificate. The chain served by a server doesn't need the root
certificate.
The trusted roots don't need to be the same for client and server when using client authentication.
Clients might rely on the platform certificates and servers might use a private
organization-specific certificate authority.
By default `HeldCertificate` instances expire after 24 hours. Use `duration()` to adjust.
By default server certificates need to identify which hostnames they're trusted for. You may add as
many as necessary with `addSubjectAlternativeName()`. This mechanism also supports a very limited
form of wildcards `*.example.com` where the `*` must be first and doesn't match nested subdomains.
By default certificates use fast and secure 256-bit ECDSA keys. For interoperability with very old
clients use `HeldCertificate.Builder.rsa2048()`.
Download
--------
Get via Maven:
```xml
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-tls</artifactId>
<version>(insert latest version)</version>
</dependency>
```
or via Gradle
```groovy
implementation 'com.squareup.okhttp3:okhttp-tls:(insert latest version)'
```
[held_certificate]: http://square.github.io/okhttp/3.x/okhttp-tls/okhttp3/tls/HeldCertificate.html
[held_certificate_builder]: http://square.github.io/okhttp/3.x/okhttp-tls/okhttp3/tls/HeldCertificate.Builder.html
[handshake_certificates]: http://square.github.io/okhttp/3.x/okhttp-tls/okhttp3/tls/HandshakeCertificates.html
[handshake_certificates_builder]: http://square.github.io/okhttp/3.x/okhttp-tls/okhttp3/tls/HandshakeCertificates.Builder.html
|