1a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller/*
2a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Copyright (C) 2015 Square, Inc.
3a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller *
4a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
5a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * you may not use this file except in compliance with the License.
6a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * You may obtain a copy of the License at
7a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller *
8a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
9a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller *
10a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Unless required by applicable law or agreed to in writing, software
11a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
12a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * See the License for the specific language governing permissions and
14a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * limitations under the License.
15a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */
16a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerpackage com.squareup.okhttp.recipes;
17a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
18a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport com.squareup.okhttp.CertificatePinner;
19a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport com.squareup.okhttp.Headers;
20a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport com.squareup.okhttp.OkHttpClient;
21a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport com.squareup.okhttp.Request;
22a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport com.squareup.okhttp.Response;
23a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.io.IOException;
24a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.io.InputStream;
25a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.security.GeneralSecurityException;
26a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.security.KeyStore;
27a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.security.SecureRandom;
28a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.security.cert.Certificate;
29a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.security.cert.CertificateFactory;
30a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.util.Collection;
31a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport javax.net.ssl.KeyManagerFactory;
32a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport javax.net.ssl.SSLContext;
33a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport javax.net.ssl.TrustManagerFactory;
34a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport okio.Buffer;
35a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
36a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerpublic final class CustomTrust {
37a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  private final OkHttpClient client;
38a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
39a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  public CustomTrust() {
40a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    client = new OkHttpClient();
41a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    SSLContext sslContext = sslContextForTrustedCertificates(trustedCertificatesInputStream());
42a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    client.setSslSocketFactory(sslContext.getSocketFactory());
43a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
44a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
45a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  public void run() throws Exception {
46a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Request request = new Request.Builder()
47a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .url("https://publicobject.com/helloworld.txt")
48a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .build();
49a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
50a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Response response = client.newCall(request).execute();
51a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
52a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
53a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Headers responseHeaders = response.headers();
54a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    for (int i = 0; i < responseHeaders.size(); i++) {
55a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
56a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    }
57a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
58a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    System.out.println(response.body().string());
59a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
60a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
61a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  /**
62a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * Returns an input stream containing one or more certificate PEM files. This implementation just
63a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * embeds the PEM files in Java strings; most applications will instead read this from a resource
64a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * file that gets bundled with the application.
65a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   */
66a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  private InputStream trustedCertificatesInputStream() {
67a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // PEM files for root certificates of Comodo and Entrust. These two CAs are sufficient to view
68a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // https://publicobject.com (Comodo) and https://squareup.com (Entrust). But they aren't
69a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // sufficient to connect to most HTTPS sites including https://godaddy.com and https://visa.com.
70a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Typically developers will need to get a PEM file from their organization's TLS administrator.
71a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    String comodoRsaCertificationAuthority = ""
72a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "-----BEGIN CERTIFICATE-----\n"
73a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\n"
74a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n"
75a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\n"
76a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5\n"
77a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\n"
78a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\n"
79a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\n"
80a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR\n"
81a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X\n"
82a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC\n"
83a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV\n"
84a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf\n"
85a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z\n"
86a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w\n"
87a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah\n"
88a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC\n"
89a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf\n"
90a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq\n"
91a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\n"
92a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\n"
93a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl\n"
94a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM\n"
95a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV\n"
96a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna\n"
97a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ\n"
98a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK\n"
99a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke\n"
100a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL\n"
101a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb\n"
102a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl\n"
103a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n"
104a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "NVOFBkpdn627G190\n"
105a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "-----END CERTIFICATE-----\n";
106a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    String entrustRootCertificateAuthority = ""
107a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "-----BEGIN CERTIFICATE-----\n"
108a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC\n"
109a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0\n"
110a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW\n"
111a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl\n"
112a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw\n"
113a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw\n"
114a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy\n"
115a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV\n"
116a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ\n"
117a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo\n"
118a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4\n"
119a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9\n"
120a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI\n"
121a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi\n"
122a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB\n"
123a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi\n"
124a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo\n"
125a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE\n"
126a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA\n"
127a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t\n"
128a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua\n"
129a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP\n"
130a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/\n"
131a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m\n"
132a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "0vdXcDazv/wor3ElhVsT/h5/WrQ8\n"
133a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        + "-----END CERTIFICATE-----\n";
134a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    return new Buffer()
135a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .writeUtf8(comodoRsaCertificationAuthority)
136a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .writeUtf8(entrustRootCertificateAuthority)
137a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .inputStream();
138a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
139a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
140a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  /**
141a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * Returns a SSL context that trusts {@code certificates} and none other. HTTPS services whose
142a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * certificates have not been signed by these certificates will fail with a {@code
143a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * SSLHandshakeException}.
144a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   *
145a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * <p>This can be used to replace the host platform's built-in trusted certificates with a custom
146a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * set. This is useful in development where certificate authority-trusted certificates aren't
147a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * available. Or in production, to avoid reliance on third-party certificate authorities.
148a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   *
149a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * <p>See also {@link CertificatePinner}, which can limit trusted certificates while still using
150a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * the host platform's built-in trust store.
151a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   *
152a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * <h3>Warning: Customizing Trusted Certificates is Dangerous!</h3>
153a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * Relying on your own trusted certificates limits your server team's ability to update their TLS
154a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * certificates. By installing a specific set of trusted certificates, you take on additional
155a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * operational complexity and limit your ability to migrate between certificate authorities. Do
156a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * not use custom trusted certificates in production without the blessing of your server's TLS
157a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * administrator.
158a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   */
159a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  public SSLContext sslContextForTrustedCertificates(InputStream in) {
160a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    try {
161a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
162a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
163a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      if (certificates.isEmpty()) {
164a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        throw new IllegalArgumentException("expected non-empty set of trusted certificates");
165a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      }
166a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
167a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      // Put the certificates a key store.
168a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      char[] password = "password".toCharArray(); // Any password will work.
169a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      KeyStore keyStore = newEmptyKeyStore(password);
170a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      int index = 0;
171a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      for (Certificate certificate : certificates) {
172a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        String certificateAlias = Integer.toString(index++);
173a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        keyStore.setCertificateEntry(certificateAlias, certificate);
174a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      }
175a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
176a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      // Wrap it up in an SSL context.
177a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
178a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          KeyManagerFactory.getDefaultAlgorithm());
179a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      keyManagerFactory.init(keyStore, password);
180a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
181a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          TrustManagerFactory.getDefaultAlgorithm());
182a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      trustManagerFactory.init(keyStore);
183a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      SSLContext sslContext = SSLContext.getInstance("TLS");
184a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(),
185a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          new SecureRandom());
186a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      return sslContext;
187a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    } catch (GeneralSecurityException e) {
188a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      throw new RuntimeException(e);
189a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    }
190a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
191a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
192a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
193a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    try {
194a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
195a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      InputStream in = null; // By convention, 'null' creates an empty key store.
196a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      keyStore.load(in, password);
197a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      return keyStore;
198a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    } catch (IOException e) {
199a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      throw new AssertionError(e);
200a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    }
201a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
202a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
203a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  public static void main(String... args) throws Exception {
204a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    new CustomTrust().run();
205a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
206a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller}
207