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