blob: f52865acff9e34d3e18e1bad44773445e272c0af [file] [log] [blame]
Scott Main00158362013-01-17 16:58:50 -08001page.title=Security with HTTPS and SSL
Scott Main1c2dea02013-04-10 18:59:29 -07002page.tags="network","certificates"
3
Scott Main00158362013-01-17 16:58:50 -08004page.article=true
5@jd:body
6
7<div id="tb-wrapper">
8<div id="tb">
9<h2>In this document</h2>
10<ol class="nolist">
11 <li><a href="#Concepts">Concepts</a></li>
12 <li><a href="#HttpsExample">An HTTP Example</a></li>
13 <li><a href="#CommonProblems">Common Problems Verifying Server Certificates</a>
14 <ol class="nolist">
15 <li><a href="#UnknownCa">Unknown certificate authority</a></li>
16 <li><a href="#SelfSigned">Self-signed server certificate</a></li>
17 <li><a href="#MissingCa">Missing intermediate certificate authority</a></li>
18 </ol>
19 </li>
20 <li><a href="#CommonHostnameProbs">Common Problems with Hostname Verification</a></li>
21 <li><a href="#WarningsSslSocket">Warnings About Using SSLSocket Directly</a></li>
22 <li><a href="#Blacklisting">Blacklisting</a></li>
23 <li><a href="#Pinning">Pinning</a></li>
24 <li><a href="#ClientCert">Client Certificates</a></li>
25</ol>
26
27
28<h2>See also</h2>
29<ul>
30<li><a href="http://source.android.com/tech/security/index.html">Android
31Security Overview</a></li>
32<li><a href="{@docRoot}guide/topics/security/permissions.html">Permissions</a></li>
33</ul>
34</div></div>
35
36
37
38<p>The Secure Sockets Layer (SSL)&mdash;now technically known as <a
39href="http://en.wikipedia.org/wiki/Transport_Layer_Security">Transport Layer Security
40(TLS)</a>&mdash;is a
41common building block for encrypted communications between clients and servers. It's possible that
42an application might use SSL incorrectly such that malicious entities may
43be able to intercept an app's data over the network. To help you ensure that this does not happen
44to your app, this article highlights the common pitfalls when using secure network protocols and addresses some larger concerns about using <a
45href="http://en.wikipedia.org/wiki/Public-key_infrastructure">Public-Key Infrastructure (PKI)</a>.
46
47
48<h2 id="Concepts">Concepts</h2>
49
50<p>In a typical SSL usage scenario, a server is configured with a certificate containing a
51public key as well as a matching private key. As part of the handshake between an SSL client
52and server, the server proves it has the private key by signing its certificate with <a
53href="http://en.wikipedia.org/wiki/Public-key_cryptography">public-key cryptography</a>.</p>
54
55<p>However, anyone can generate their own certificate and private key, so a simple handshake
56doesn't prove anything about the server other than that the server knows the private key that
57matches the public key of the certificate. One way to solve this problem is to have the client
58have a set of one or more certificates it trusts. If the certificate is not in the set, the
59server is not to be trusted.</p>
60
61<p>There are several downsides to this simple approach. Servers should be able to
62upgrade to stronger keys over time ("key rotation"), which replaces the public key in the
63certificate with a new one. Unfortunately, now the client app has to be updated due to what
64is essentially a server configuration change. This is especially problematic if the server
65is not under the app developer's control, for example if it is a third party web service. This
66approach also has issues if the app has to talk to arbitrary servers such as a web browser or
67email app.</p>
68
69<p>In order to address these downsides, servers are typically configured with certificates
70from well known issuers called <a
71href="http://en.wikipedia.org/wiki/Certificate_authority">Certificate Authorities (CAs)</a>.
72The host platform generally contains a list of well known CAs that it trusts.
73As of Android 4.2 (Jelly Bean), Android currently contains over 100 CAs that are updated
74in each release. Similar to a server, a CA has a certificate and a private key. When issuing
75a certificate for a server, the CA <a
76href="http://en.wikipedia.org/wiki/Digital_signature">signs</a>
77the server certificate using its private key. The
78client can then verify that the server has a certificate issued by a CA known to the platform.</p>
79
80<p>However, while solving some problems, using CAs introduces another. Because the CA issues
81certificates for many servers, you still need some way to make sure you are talking to the
82server you want. To address this, the certificate issued by the CA identifies the server
83either with a specific name such as <em>gmail.com</em> or a wildcarded set of
84hosts such as <em>*.google.com</em>. </p>
85
86<p>The following example will make these concepts a little more concrete. In the snippet below
87from a command line, the <a href="http://www.openssl.org/docs/apps/openssl.html">{@code openssl}</a>
88tool's {@code s_client} command looks at Wikipedia's server certificate information. It
89specifies port 443 because that is the default for <acronym title="Hypertext Transfer
90Protocol Secure">HTTPS</acronym>. The command sends
91the output of {@code openssl s_client} to {@code openssl x509}, which formats information
92about certificates according to the <a
93href="http://en.wikipedia.org/wiki/X.509">X.509 standard</a>. Specifically,
94the command asks for the subject, which contains the server name information,
95and the issuer, which identifies the CA.</p>
96
97<pre class="no-pretty-print">
98$ openssl s_client -connect wikipedia.org:443 | openssl x509 -noout -subject -issuer
99<b>subject=</b> /serialNumber=sOrr2rKpMVP70Z6E9BT5reY008SJEdYv/C=US/O=*.wikipedia.org/OU=GT03314600/OU=See www.rapidssl.com/resources/cps (c)11/OU=Domain Control Validated - RapidSSL(R)/<b>CN=*.wikipedia.org</b>
100<b>issuer=</b> /C=US/O=GeoTrust, Inc./CN=<b>RapidSSL CA</b>
101</pre>
102
103<p>You can see that the certificate was issued for servers matching <em>*.wikipedia.org</em> by
104the RapidSSL CA.</p>
105
106
107
108<h2 id="HttpsExample">An HTTPS Example</h2>
109
110<p>Assuming you have a web server with a
111certificate issued by a well known CA, you can make a secure request with code as
112simple this:</p>
113
114<pre>
115URL url = new URL("https://wikipedia.org");
116URLConnection urlConnection = url.openConnection();
117InputStream in = urlConnection.getInputStream();
118copyInputStreamToOutputStream(in, System.out);
119</pre>
120
121<p>Yes, it really can be that simple. If you want to tailor the HTTP request, you can cast to
122an {@link java.net.HttpURLConnection}. The Android documentation for
123{@link java.net.HttpURLConnection} has further examples about how to deal with request
124and response headers, posting content, managing cookies, using proxies, caching responses,
125and so on. But in terms of the details for verifying certificates and hostnames, the Android
126framework takes care of it for you through these APIs.
127This is where you want to be if at all possible. That said, below are some other considerations.</p>
128
129
130
131<h2 id="CommonProblems">Common Problems Verifying Server Certificates</h2>
132
133<p>Suppose instead of receiving the content from {@link java.net.URLConnection#getInputStream
134getInputStream()}, it throws an exception:</p>
135
136<pre class="no-pretty-print">
137javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
138 at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374)
139 at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
140 at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
141 at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
142 at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
143 at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
144 at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
145 at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
146 at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
147</pre>
148
149<p>This can happen for several reasons, including:
150<ol>
151 <li><a href="#UnknownCa">The CA that issued the server certificate was unknown</a></li>
152 <li><a href="#SelfSigned">The server certificate wasn't signed by a CA, but was self signed</a></li>
153 <li><a href="#MissingCa">The server configuration is missing an intermediate CA</a></li>
154</ol>
155
156<p>The following sections discuss how to address these problems while keeping your
157connection to the server secure.
158
159
160
161<h3 id="UnknownCa">Unknown certificate authority</h3>
162
163<p>In this case, the {@link javax.net.ssl.SSLHandshakeException} occurs
164because you have a CA that isn't trusted by the system. It could be because
165you have a certificate from a new CA that isn't yet trusted by Android or your app is
166running on an older version without the CA. More often a CA is unknown because it isn't a
167public CA, but a private one issued by an organization such as a government, corporation,
168or education institution for their own use.</p>
169
170<p>Fortunately, you can teach {@link javax.net.ssl.HttpsURLConnection}
171to trust a specific set of CAs. The procedure
172can be a little convoluted, so below is an example that takes a specific CA from
173an {@link java.io.InputStream}, uses it to create a {@link java.security.KeyStore},
174which is then used to create and initialize a
175{@link javax.net.ssl.TrustManager}. A {@link javax.net.ssl.TrustManager} is what the system
176uses to validate certificates from the server
177and&mdash;by creating one from a {@link java.security.KeyStore} with one or more CAs&mdash;those
178will be the only CAs trusted by that {@link javax.net.ssl.TrustManager}.</p>
179
180<p>Given the new {@link javax.net.ssl.TrustManager},
181the example initializes a new {@link javax.net.ssl.SSLContext} which provides
182an {@link javax.net.ssl.SSLSocketFactory} you can use to override the default
183{@link javax.net.ssl.SSLSocketFactory} from
184{@link javax.net.ssl.HttpsURLConnection}. This way the
185connection will use your CAs for certificate validation.</p>
186
187<p>Here is the example in
188full using an organizational CA from the University of Washington:</p>
189
190<pre>
191// Load CAs from an InputStream
192// (could be from a resource or ByteArrayInputStream or ...)
193CertificateFactory cf = CertificateFactory.getInstance("X.509");
194// From https://www.washington.edu/itconnect/security/ca/load-der.crt
195InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
196Certificate ca;
197try {
198 ca = cf.generateCertificate(caInput);
199 System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
200} finally {
201 caInput.close();
202}
203
204// Create a KeyStore containing our trusted CAs
205String keyStoreType = KeyStore.getDefaultType();
206KeyStore keyStore = KeyStore.getInstance(keyStoreType);
207keyStore.load(null, null);
208keyStore.setCertificateEntry("ca", ca);
209
210// Create a TrustManager that trusts the CAs in our KeyStore
211String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
212TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
213tmf.init(keyStore);
214
215// Create an SSLContext that uses our TrustManager
216SSLContext context = SSLContext.getInstance("TLS");
217context.init(null, tmf.getTrustManagers(), null);
218
219// Tell the URLConnection to use a SocketFactory from our SSLContext
220URL url = new URL("https://certs.cac.washington.edu/CAtest/");
221HttpsURLConnection urlConnection =
222 (HttpsURLConnection)url.openConnection();
223urlConnection.setSSLSocketFactory(context.getSocketFactory());
224InputStream in = urlConnection.getInputStream();
225copyInputStreamToOutputStream(in, System.out);
226</pre>
227
228<p>With a custom {@link javax.net.ssl.TrustManager} that knows about your CAs,
229the system is able to validate
230that your server certificate come from a trusted issuer.</p>
231
232<p class="caution"><strong>Caution:</strong>
233Many web sites describe a poor alternative solution which is to install a
234{@link javax.net.ssl.TrustManager} that does nothing. If you do this you might as well not
235be encrypting your communication, because anyone can attack your users at a public Wi-Fi hotspot
236by using <acronym title="Domain Name System">DNS</acronym> tricks to send your users'
237traffic through a proxy of their own that pretends to be your server. The attacker can then
238record passwords and other personal data. This works because the attacker can generate a
239certificate and&mdash;without a {@link javax.net.ssl.TrustManager} that actually
240validates that the certificate comes from a trusted
241source&mdash;your app could be talking to anyone. So don't do this, not even temporarily. You can
242always make your app trust the issuer of the server's certificate, so just do it.</p>
243
244
245
246<h3 id="SelfSigned">Self-signed server certificate</h3>
247
248<p>The second case of {@link javax.net.ssl.SSLHandshakeException} is
249due to a self-signed certificate, which means the server is behaving as its own CA.
250This is similar to an unknown certificate authority, so you can use the
251same approach from the previous section.</p>
252
David Friedman36b692d2013-09-27 10:04:48 -0700253<p>You can create your own {@link javax.net.ssl.TrustManager},
Scott Main00158362013-01-17 16:58:50 -0800254this time trusting the server certificate directly. This has all of the
255downsides discussed earlier of tying your app directly to a certificate, but can be done
256securely. However, you should be careful to make sure your self-signed certificate has a
257reasonably strong key. As of 2012, a 2048-bit RSA signature with an exponent of 65537 expiring
258yearly is acceptable. When rotating keys, you should check for <a
259href="http://csrc.nist.gov/groups/ST/key_mgmt/index.html">recommendations</a> from an
260authority (such as <a href="http://www.nist.gov/">NIST</a>) about what is acceptable.</p>
261
262
263
264<h3 id="MissingCa">Missing intermediate certificate authority</h3>
265
266<p>The third case of {@link javax.net.ssl.SSLHandshakeException}
267occurs due to a missing intermediate CA. Most public
268CAs don't sign server certificates directly. Instead, they use their main CA certificate,
269referred to as the root CA, to sign intermediate CAs. They do this so the root CA can be stored
270offline to reduce risk of compromise. However, operating systems like Android typically
271trust only root CAs directly, which leaves a short gap of trust between the server
272certificate&mdash;signed by the intermediate CA&mdash;and the certificate verifier,
273which knows the root CA. To solve
274this, the server doesn't send the client only it's certificate during the SSL handshake, but
275a chain of certificates from the server CA through any intermediates necessary to reach a
276trusted root CA.</p>
277
278<p>To see what this looks like in practice, here's the <em>mail.google.com</em> certificate
279chain as viewed by the <a href="http://www.openssl.org/docs/apps/openssl.html">{@code openssl}</a>
280{@code s_client} command:</p>
281
282<pre class="no-pretty-print">
283$ openssl s_client -connect mail.google.com:443
284---
285Certificate chain
286 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=mail.google.com
287 i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
288 1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
289 i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
290---
291</pre>
292
293
294<p>This shows that the server sends a certificate for <em>mail.google.com</em>
295issued by the <em>Thawte SGC</em> CA, which is an intermediate CA, and a second certificate
296for the <em>Thawte SGC</em> CA issued by a <em>Verisign</em> CA, which is the primary CA that's
297trusted by Android.</p>
298
299<p>However, it is not uncommon to configure a server to not include the necessary
300intermediate CA. For example, here is a server that can cause an error in Android browsers and
301exceptions in Android apps:</p>
302
303<pre class="no-pretty-print">
304$ openssl s_client -connect egov.uscis.gov:443
305---
306Certificate chain
307 0 s:/C=US/ST=District Of Columbia/L=Washington/O=U.S. Department of Homeland Security/OU=United States Citizenship and Immigration Services/OU=Terms of use at www.verisign.com/rpa (c)05/CN=egov.uscis.gov
308 i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3
309---
310</pre>
311
312<p>What is interesting to note here is that visiting this server in most desktop browsers
313does not cause an error like a completely unknown CA or self-signed server certificate would
314cause. This is because most desktop browsers cache trusted intermediate CAs over time. Once
315a browser has visited and learned about an intermediate CA from one site, it won't
316need to have the intermediate CA included in the certificate chain the next time.</p>
317
318<p>Some sites do this intentionally for secondary web servers used to serve resources. For
319example, they might have their main HTML page served by a server with a full certificate
320chain, but have servers for resources such as images, CSS, or JavaScript not include the
321CA, presumably to save bandwidth. Unfortunately, sometimes these servers might be providing
322a web service you are trying to call from your Android app, which is not as forgiving.</p>
323
324<p>There are two approaches to solve this issue:</p>
325<ul>
326 <li>Configure the server to
327 include the intermediate CA in the server chain. Most CAs provide documentation on how to do
328 this for all common web servers. This is the only approach if you need the site to work with
329 default Android browsers at least through Android 4.2.</li>
330 <li>Or, treat the
331 intermediate CA like any other unknown CA, and create a {@link javax.net.ssl.TrustManager}
332 to trust it directly, as done in the previous two sections.</li>
333</ul>
334
335
336<h2 id="CommonHostnameProbs">Common Problems with Hostname Verification</h2>
337
338<p>As mentioned at the beginning of this article,
339there are two key parts to verifying an SSL connection. The first
340is to verify the certificate is from a trusted source, which was the focus of the previous
341section. The focus of this section is the second part: making sure the server you are
342talking to presents the right certificate. When it doesn't, you'll typically see an error
343like this:</p>
344
345<pre class="no-pretty-print">
346java.io.IOException: Hostname 'example.com' was not verified
347 at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
348 at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
349 at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
350 at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
351 at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
352 at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
353 at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
354</pre>
355
356
357<p>One reason this can happen is due to a server configuration error. The server is
358configured with a certificate that does not have a subject or subject alternative name fields
359that match the server you are trying to reach. It is possible to have one certificate be used
360with many different servers. For example, looking at the <em>google.com</em> certificate with
361<a href="http://www.openssl.org/docs/apps/openssl.html">{@code openssl}</a> {@code
362s_client -connect google.com:443 | openssl x509 -text} you can see that a subject
363that supports <em>*.google.com</em> but also subject alternative names for <em>*.youtube.com</em>,
364<em>*.android.com</em>, and others. The error occurs only when the server name you
365are connecting to isn't listed by the certificate as acceptable.</p>
366
367<p>Unfortunately this can happen for another reason as well: <a
368href="http://en.wikipedia.org/wiki/Virtual_hosting">virtual hosting</a>. When sharing a
369server for more than one hostname with HTTP, the web server can tell from the HTTP/1.1 request
370which target hostname the client is looking for. Unfortunately this is complicated with
371HTTPS, because the server has to know which certificate to return before it sees the HTTP
372request. To address this problem, newer versions of SSL, specifically TLSv.1.0 and later,
373support <a href="http://en.wikipedia.org/wiki/Server_Name_Indication">Server Name Indication
374(SNI)</a>, which allows the SSL client to specify the intended
375hostname to the server so the proper certificate can be returned.</p>
376
377<p>Fortunately, {@link javax.net.ssl.HttpsURLConnection} supports
378SNI since Android 2.3. Unfortunately, Apache
379HTTP Client does not, which is one of the many reasons we discourage its use. One workaround
380if you need to support Android 2.2 (and older) or Apache HTTP Client is to set up an alternative
381virtual host on a unique port so that it's unambiguous which server certificate to return.</p>
382
383<p>The more drastic alternative is to replace {@link javax.net.ssl.HostnameVerifier}
384with one that uses not the
385hostname of your virtual host, but the one returned by the server by default.</p>
386
387<p class="caution"><strong>Caution:</strong> Replacing {@link javax.net.ssl.HostnameVerifier}
388can be <strong>very dangerous</strong> if the other virtual host is
389not under your control, because a man-in-the-middle attack could direct traffic to another
390server without your knowledge.</p>
391
392<p>If you are still sure you want to override hostname verification, here is an example
393that replaces the verifier for a single {@link java.net.URLConnection}
394with one that still verifies that the hostname is at least on expected by the app:</p>
395
396<pre>
397// Create an HostnameVerifier that hardwires the expected hostname.
398// Note that is different than the URL's hostname:
399// example.com versus example.org
400HostnameVerifier hostnameVerifier = new HostnameVerifier() {
401 &#64;Override
402 public boolean verify(String hostname, SSLSession session) {
403 HostnameVerifier hv =
404 HttpsURLConnection.getDefaultHostnameVerifier();
405 return hv.verify("example.com", session);
406 }
407};
408
409// Tell the URLConnection to use our HostnameVerifier
410URL url = new URL("https://example.org/");
411HttpsURLConnection urlConnection =
412 (HttpsURLConnection)url.openConnection();
413urlConnection.setHostnameVerifier(hostnameVerifier);
414InputStream in = urlConnection.getInputStream();
415copyInputStreamToOutputStream(in, System.out);
416</pre>
417
418<p>But remember, if you find yourself replacing hostname verification, especially
419due to virtual hosting, it's still <strong>very dangerous</strong> if the other virtual host is
420not under your control and you should find an alternative hosting arrangement
421that avoids this issue.</p>
422
423
424
425
426<h2 id="WarningsSslSocket">Warnings About Using SSLSocket Directly</h2>
427
428<p>So far, the examples have focused on HTTPS using {@link javax.net.ssl.HttpsURLConnection}.
429Sometimes apps need to use SSL separate from HTTP. For example, an email app might use SSL variants
430of SMTP, POP3, or IMAP. In those cases, the app would want to use {@link javax.net.ssl.SSLSocket}
431directly, much the same way that {@link javax.net.ssl.HttpsURLConnection} does internally.</p>
432
433<p>The techniques described so
434far to deal with certificate verification issues also apply to {@link javax.net.ssl.SSLSocket}.
435In fact, when using a custom {@link javax.net.ssl.TrustManager}, what is passed to
436{@link javax.net.ssl.HttpsURLConnection} is an {@link javax.net.ssl.SSLSocketFactory}.
437So if you need to use a custom {@link javax.net.ssl.TrustManager} with an
438{@link javax.net.ssl.SSLSocket}, follow
439the same steps and use that {@link javax.net.ssl.SSLSocketFactory} to create your
440{@link javax.net.ssl.SSLSocket}.</p>
441
442<p class="caution"><strong>Caution:</strong>
443{@link javax.net.ssl.SSLSocket} <strong>does not</strong> perform hostname verification. It is
444up the your app to do its own hostname verification, preferably by calling {@link
445javax.net.ssl.HttpsURLConnection#getDefaultHostnameVerifier()} with the expected hostname. Further
446beware that {@link javax.net.ssl.HostnameVerifier#verify HostnameVerifier.verify()}
447doesn't throw an exception on error but instead returns a boolean result that you must
448explicitly check.</p>
449
450<p>Here is an example showing how you can do this. It shows that when connecting to
451<em>gmail.com</em> port 443 without SNI support, you'll receive a certificate for
452<em>mail.google.com</em>. This is expected in this case, so check to make sure that
453the certificate is indeed for <em>mail.google.com</em>:</p>
454
455<pre>
456// Open SSLSocket directly to gmail.com
457SocketFactory sf = SSLSocketFactory.getDefault();
458SSLSocket socket = (SSLSocket) sf.createSocket("gmail.com", 443);
459HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
460SSLSession s = socket.getSession();
461
462// Verify that the certicate hostname is for mail.google.com
463// This is due to lack of SNI support in the current SSLSocket.
464if (!hv.verify("mail.google.com", s)) {
465 throw new SSLHandshakeException("Expected mail.google.com, "
466 "found " + s.getPeerPrincipal());
467}
468
469// At this point SSLSocket performed certificate verificaiton and
470// we have performed hostname verification, so it is safe to proceed.
471
472// ... use socket ...
473socket.close();
474</pre>
475
476
477
478<h2 id="Blacklisting">Blacklisting</h2>
479
480<p>SSL relies heavily on CAs to issue certificates to only the properly verified owners
481of servers and domains. In rare cases, CAs are either tricked or, in the case of <a
482href="http://en.wikipedia.org/wiki/Comodo_Group#Breach_of_security">Comodo</a> or <a
483href="http://en.wikipedia.org/wiki/DigiNotar">DigiNotar</a>, breached,
484resulting in the certificates for a hostname to be issued to
485someone other than the owner of the server or domain.</p>
486
487<p>In order to mitigate this risk, Android has the ability to blacklist certain certificates or even
488whole CAs. While this list was historically built into the operating system, starting in
489Android 4.2 this list can be remotely updated to deal with future compromises.</p>
490
491
492
493<h2 id="Pinning">Pinning</h2>
494
495<p>An app can further protect itself from fraudulently issued certificates by a
496technique known as pinning. This is basically using the example provided in the unknown CA case
497above to restrict an app's trusted CAs to a small set known to be used by the app's servers. This
498prevents the compromise of one of the other 100+ CAs in the system from resulting in a breach of
499the apps secure channel.</p>
500
501
502
503<h2 id="ClientCert">Client Certificates</h2>
504
505<p>This article has focused on the user of SSL to secure communications with servers. SSL also
506supports the notion of client certificates that allow the server to validate the identity of a
507client. While beyond the scope of this article, the techniques involved are similar to specifying
508a custom {@link javax.net.ssl.TrustManager}.
509See the discussion about creating a custom {@link javax.net.ssl.KeyManager} in the documentation for
510{@link javax.net.ssl.HttpsURLConnection}.</p>
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541