Skip to content

Commit db6716a

Browse files
committed
Fix for AsyncHttpClient#197 -- use a hostname verifier that calls out to HostnameChecker.
1 parent 595a061 commit db6716a

File tree

2 files changed

+98
-2
lines changed

2 files changed

+98
-2
lines changed

api/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.asynchttpclient.filter.IOExceptionFilter;
2222
import org.asynchttpclient.filter.RequestFilter;
2323
import org.asynchttpclient.filter.ResponseFilter;
24-
import org.asynchttpclient.util.AllowAllHostnameVerifier;
24+
import org.asynchttpclient.util.DefaultHostnameVerifier;
2525
import org.asynchttpclient.util.ProxyUtils;
2626

2727
import javax.net.ssl.HostnameVerifier;
@@ -596,7 +596,7 @@ public static class Builder {
596596
private boolean allowSslConnectionPool = true;
597597
private boolean useRawUrl = false;
598598
private boolean removeQueryParamOnRedirect = true;
599-
private HostnameVerifier hostnameVerifier = new AllowAllHostnameVerifier();
599+
private HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
600600
private int ioThreadMultiplier = 2;
601601
private boolean strict302Handling;
602602
private boolean spdyEnabled;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* To the extent possible under law, Kevin Locke has waived all copyright and
3+
* related or neighboring rights to this work.
4+
* <p/>
5+
* A legal description of this waiver is available in <a href="https://gist.github.com/kevinoid/3829665">LICENSE.txt</a>
6+
*/
7+
package org.asynchttpclient.util;
8+
9+
import sun.security.util.HostnameChecker;
10+
11+
import javax.net.ssl.HostnameVerifier;
12+
import javax.net.ssl.SSLPeerUnverifiedException;
13+
import javax.net.ssl.SSLSession;
14+
import javax.security.auth.kerberos.KerberosPrincipal;
15+
import java.security.Principal;
16+
import java.security.cert.Certificate;
17+
import java.security.cert.CertificateException;
18+
import java.security.cert.X509Certificate;
19+
20+
/**
21+
* Uses the internal HostnameChecker to verify the server's hostname matches with the
22+
* certificate. This is a requirement for HTTPS, but the raw SSLEngine does not have
23+
* this functionality. As such, it has to be added in manually. For a more complete
24+
* description of hostname verification and why it's important,
25+
* please read
26+
* <a href="http://tersesystems.com/2014/03/23/fixing-hostname-verification/">Fixing
27+
* Hostname Verification</a>.
28+
* <p/>
29+
* This code is based on Kevin Locke's <a href="http://kevinlocke.name/bits/2012/10/03/ssl-certificate-verification-in-dispatch-and-asynchttpclient/">guide</a> .
30+
* <p/>
31+
32+
*/
33+
public class DefaultHostnameVerifier implements HostnameVerifier {
34+
35+
private HostnameVerifier extraHostnameVerifier;
36+
37+
public DefaultHostnameVerifier() {
38+
}
39+
40+
public DefaultHostnameVerifier(HostnameVerifier extraHostnameVerifier) {
41+
this.extraHostnameVerifier = extraHostnameVerifier;
42+
}
43+
44+
private boolean hostnameMatches(String hostname, SSLSession session) {
45+
HostnameChecker checker =
46+
HostnameChecker.getInstance(HostnameChecker.TYPE_TLS);
47+
48+
boolean validCertificate = false, validPrincipal = false;
49+
try {
50+
Certificate[] peerCertificates = session.getPeerCertificates();
51+
52+
if (peerCertificates.length > 0 &&
53+
peerCertificates[0] instanceof X509Certificate) {
54+
X509Certificate peerCertificate =
55+
(X509Certificate) peerCertificates[0];
56+
57+
try {
58+
checker.match(hostname, peerCertificate);
59+
// Certificate matches hostname
60+
validCertificate = true;
61+
} catch (CertificateException ex) {
62+
// Certificate does not match hostname
63+
}
64+
} else {
65+
// Peer does not have any certificates or they aren't X.509
66+
}
67+
} catch (SSLPeerUnverifiedException ex) {
68+
// Not using certificates for peers, try verifying the principal
69+
try {
70+
Principal peerPrincipal = session.getPeerPrincipal();
71+
if (peerPrincipal instanceof KerberosPrincipal) {
72+
validPrincipal = HostnameChecker.match(hostname,
73+
(KerberosPrincipal) peerPrincipal);
74+
} else {
75+
// Can't verify principal, not Kerberos
76+
}
77+
} catch (SSLPeerUnverifiedException ex2) {
78+
// Can't verify principal, no principal
79+
}
80+
}
81+
82+
return validCertificate || validPrincipal;
83+
}
84+
85+
public boolean verify(String hostname, SSLSession session) {
86+
if (hostnameMatches(hostname, session)) {
87+
return true;
88+
} else {
89+
if (extraHostnameVerifier != null) {
90+
return extraHostnameVerifier.verify(hostname, session);
91+
} else {
92+
return false;
93+
}
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)