Skip to content

Commit bbdc1b3

Browse files
committed
Add new working certs with subjectAltName and ipaddress of 127.0.0.1 to make tests pass
1 parent fa056c5 commit bbdc1b3

File tree

9 files changed

+181
-78
lines changed

9 files changed

+181
-78
lines changed

api/src/main/java/org/asynchttpclient/util/DefaultHostnameVerifier.java

Lines changed: 71 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
import javax.net.ssl.SSLPeerUnverifiedException;
1111
import javax.net.ssl.SSLSession;
1212
import javax.security.auth.kerberos.KerberosPrincipal;
13-
import java.lang.reflect.InvocationTargetException;
14-
import java.lang.reflect.Method;
1513
import java.security.Principal;
1614
import java.security.cert.Certificate;
1715
import java.security.cert.CertificateException;
1816
import java.security.cert.X509Certificate;
17+
import java.util.logging.Level;
18+
import java.util.logging.Logger;
1919

2020
/**
2121
* Uses the internal HostnameChecker to verify the server's hostname matches with the
@@ -31,119 +31,112 @@
3131
*/
3232
public class DefaultHostnameVerifier implements HostnameVerifier {
3333

34+
private HostnameChecker checker;
35+
3436
private HostnameVerifier extraHostnameVerifier;
3537

38+
// Logger to log exceptions.
39+
private static final Logger log = Logger.getLogger(DefaultHostnameVerifier.class.getName());
40+
41+
/**
42+
* A hostname verifier that uses the {{sun.security.util.HostnameChecker}} under the hood.
43+
*/
3644
public DefaultHostnameVerifier() {
45+
this.checker = new ProxyHostnameChecker();
3746
}
3847

39-
public DefaultHostnameVerifier(HostnameVerifier extraHostnameVerifier) {
40-
this.extraHostnameVerifier = extraHostnameVerifier;
48+
/**
49+
* A hostname verifier that takes an external hostname checker. Useful for testing.
50+
*
51+
* @param checker a hostnamechecker.
52+
*/
53+
public DefaultHostnameVerifier(HostnameChecker checker) {
54+
this.checker = checker;
4155
}
4256

43-
public final static byte TYPE_TLS = 1;
44-
45-
private Object getHostnameChecker() {
46-
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
47-
try {
48-
final Class<Object> hostnameCheckerClass = (Class<Object>) classLoader.loadClass("sun.security.util.HostnameChecker");
49-
final Method instanceMethod = hostnameCheckerClass.getMethod("getInstance", Byte.TYPE);
50-
final Object hostnameChecker = instanceMethod.invoke(null, TYPE_TLS);
51-
return hostnameChecker;
52-
} catch (ClassNotFoundException e) {
53-
throw new IllegalStateException(e);
54-
} catch (NoSuchMethodException e) {
55-
throw new IllegalStateException(e);
56-
} catch (InvocationTargetException e) {
57-
throw new IllegalStateException(e);
58-
} catch (IllegalAccessException e) {
59-
throw new IllegalStateException(e);
60-
}
61-
}
62-
63-
private void match(Object checker, String hostname, X509Certificate peerCertificate) throws CertificateException {
64-
try {
65-
final Class<?> hostnameCheckerClass = checker.getClass();
66-
final Method checkMethod = hostnameCheckerClass.getMethod("match", String.class, X509Certificate.class);
67-
checkMethod.invoke(checker, hostname, peerCertificate);
68-
} catch (NoSuchMethodException e) {
69-
throw new IllegalStateException(e);
70-
} catch (InvocationTargetException e) {
71-
Throwable t = e.getCause();
72-
if (t instanceof CertificateException) {
73-
throw (CertificateException) t;
74-
} else {
75-
throw new IllegalStateException(e);
76-
}
77-
} catch (IllegalAccessException e) {
78-
throw new IllegalStateException(e);
79-
}
57+
/**
58+
* A hostname verifier that falls back to another hostname verifier if not found.
59+
*
60+
* @param extraHostnameVerifier another hostname verifier.
61+
*/
62+
public DefaultHostnameVerifier(HostnameVerifier extraHostnameVerifier) {
63+
this.checker = new ProxyHostnameChecker();
64+
this.extraHostnameVerifier = extraHostnameVerifier;
8065
}
8166

82-
private boolean match(Object checker, String hostname, Principal principal) {
83-
try {
84-
final Class<?> hostnameCheckerClass = checker.getClass();
85-
final Method checkMethod = hostnameCheckerClass.getMethod("match", String.class, Principal.class);
86-
final boolean result = (Boolean) checkMethod.invoke(null, hostname, principal);
87-
return result;
88-
} catch (NoSuchMethodException e) {
89-
throw new IllegalStateException(e);
90-
} catch (InvocationTargetException e) {
91-
throw new IllegalStateException(e);
92-
} catch (IllegalAccessException e) {
93-
throw new IllegalStateException(e);
94-
}
67+
/**
68+
* A hostname verifier with a hostname checker, that falls back to another hostname verifier if not found.
69+
*
70+
* @param checker a custom HostnameChecker.
71+
* @param extraHostnameVerifier another hostname verifier.
72+
*/
73+
public DefaultHostnameVerifier(HostnameChecker checker, HostnameVerifier extraHostnameVerifier) {
74+
this.checker = checker;
75+
this.extraHostnameVerifier = extraHostnameVerifier;
9576
}
9677

78+
/**
79+
* Matches the hostname against the peer certificate in the session.
80+
*
81+
* @param hostname the IP address or hostname of the expected server.
82+
* @param session the SSL session containing the certificates with the ACTUAL hostname/ipaddress.
83+
* @return true if the hostname matches, false otherwise.
84+
*/
9785
private boolean hostnameMatches(String hostname, SSLSession session) {
98-
boolean validCertificate = false;
99-
boolean validPrincipal = false;
86+
log.log(Level.FINE, "hostname = {0}, session = {1}", new Object[] { hostname, Base64.encode(session.getId()) });
10087

101-
final Object checker = getHostnameChecker();
10288
try {
103-
10489
final Certificate[] peerCertificates = session.getPeerCertificates();
90+
if (peerCertificates.length == 0) {
91+
log.log(Level.FINE, "No peer certificates");
92+
return false;
93+
}
10594

106-
if (peerCertificates.length > 0 &&
107-
peerCertificates[0] instanceof X509Certificate) {
108-
X509Certificate peerCertificate =
109-
(X509Certificate) peerCertificates[0];
110-
95+
if (peerCertificates[0] instanceof X509Certificate) {
96+
X509Certificate peerCertificate = (X509Certificate) peerCertificates[0];
97+
log.log(Level.FINE, "peerCertificate = {0}", peerCertificate);
11198
try {
112-
match(checker, hostname, peerCertificate);
113-
// Certificate matches hostname
114-
validCertificate = true;
99+
checker.match(hostname, peerCertificate);
100+
// Certificate matches hostname if no exception is thrown.
101+
return true;
115102
} catch (CertificateException ex) {
116-
// Certificate does not match hostname
103+
log.log(Level.FINE, "Certificate does not match hostname", ex);
117104
}
118105
} else {
119-
// Peer does not have any certificates or they aren't X.509
106+
log.log(Level.FINE, "Peer does not have any certificates or they aren't X.509");
120107
}
108+
return false;
121109
} catch (SSLPeerUnverifiedException ex) {
122-
// Not using certificates for peers, try verifying the principal
110+
log.log(Level.FINE, "Not using certificates for peers, try verifying the principal");
123111
try {
124112
Principal peerPrincipal = session.getPeerPrincipal();
113+
log.log(Level.FINE, "peerPrincipal = {0}", peerPrincipal);
125114
if (peerPrincipal instanceof KerberosPrincipal) {
126-
validPrincipal = match(checker, hostname,
127-
(KerberosPrincipal) peerPrincipal);
115+
return checker.match(hostname, (KerberosPrincipal) peerPrincipal);
128116
} else {
129-
// Can't verify principal, not Kerberos
117+
log.log(Level.FINE, "Can't verify principal, not Kerberos");
130118
}
131119
} catch (SSLPeerUnverifiedException ex2) {
132120
// Can't verify principal, no principal
121+
log.log(Level.FINE, "Can't verify principal, no principal", ex2);
133122
}
123+
return false;
134124
}
135-
return validCertificate || validPrincipal;
136125
}
137126

127+
/**
128+
* Verifies the hostname against the peer certificates in a session. Falls back to extraHostnameVerifier if
129+
* there is no match.
130+
*
131+
* @param hostname the IP address or hostname of the expected server.
132+
* @param session the SSL session containing the certificates with the ACTUAL hostname/ipaddress.
133+
* @return true if the hostname matches, false otherwise.
134+
*/
138135
public boolean verify(String hostname, SSLSession session) {
139136
if (hostnameMatches(hostname, session)) {
140137
return true;
141138
} else {
142-
if (extraHostnameVerifier != null) {
143-
return extraHostnameVerifier.verify(hostname, session);
144-
} else {
145-
return false;
146-
}
139+
return extraHostnameVerifier != null && extraHostnameVerifier.verify(hostname, session);
147140
}
148141
}
149142
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) Will Sargent. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
package org.asynchttpclient.util;
14+
15+
import java.security.Principal;
16+
import java.security.cert.CertificateException;
17+
import java.security.cert.X509Certificate;
18+
19+
/**
20+
* Hostname checker interface.
21+
*/
22+
public interface HostnameChecker {
23+
24+
public void match(String hostname, X509Certificate peerCertificate) throws CertificateException;
25+
26+
public boolean match(String hostname, Principal principal);
27+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) Will Sargent. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
package org.asynchttpclient.util;
14+
15+
import java.lang.reflect.InvocationTargetException;
16+
import java.lang.reflect.Method;
17+
import java.security.Principal;
18+
import java.security.cert.CertificateException;
19+
import java.security.cert.X509Certificate;
20+
21+
/**
22+
* A HostnameChecker proxy.
23+
*/
24+
public class ProxyHostnameChecker implements HostnameChecker {
25+
26+
public final static byte TYPE_TLS = 1;
27+
28+
private final Object checker = getHostnameChecker();
29+
30+
public ProxyHostnameChecker() {
31+
}
32+
33+
private Object getHostnameChecker() {
34+
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
35+
try {
36+
final Class<Object> hostnameCheckerClass = (Class<Object>) classLoader.loadClass("sun.security.util.HostnameChecker");
37+
final Method instanceMethod = hostnameCheckerClass.getMethod("getInstance", Byte.TYPE);
38+
return instanceMethod.invoke(null, TYPE_TLS);
39+
} catch (ClassNotFoundException e) {
40+
throw new IllegalStateException(e);
41+
} catch (NoSuchMethodException e) {
42+
throw new IllegalStateException(e);
43+
} catch (InvocationTargetException e) {
44+
throw new IllegalStateException(e);
45+
} catch (IllegalAccessException e) {
46+
throw new IllegalStateException(e);
47+
}
48+
}
49+
50+
public void match(String hostname, X509Certificate peerCertificate) throws CertificateException {
51+
try {
52+
final Class<?> hostnameCheckerClass = checker.getClass();
53+
final Method checkMethod = hostnameCheckerClass.getMethod("match", String.class, X509Certificate.class);
54+
checkMethod.invoke(checker, hostname, peerCertificate);
55+
} catch (NoSuchMethodException e) {
56+
throw new IllegalStateException(e);
57+
} catch (InvocationTargetException e) {
58+
Throwable t = e.getCause();
59+
if (t instanceof CertificateException) {
60+
throw (CertificateException) t;
61+
} else {
62+
throw new IllegalStateException(e);
63+
}
64+
} catch (IllegalAccessException e) {
65+
throw new IllegalStateException(e);
66+
}
67+
}
68+
69+
public boolean match(String hostname, Principal principal) {
70+
try {
71+
final Class<?> hostnameCheckerClass = checker.getClass();
72+
final Method checkMethod = hostnameCheckerClass.getMethod("match", String.class, Principal.class);
73+
return (Boolean) checkMethod.invoke(null, hostname, principal);
74+
} catch (NoSuchMethodException e) {
75+
throw new IllegalStateException(e);
76+
} catch (InvocationTargetException e) {
77+
throw new IllegalStateException(e);
78+
} catch (IllegalAccessException e) {
79+
throw new IllegalStateException(e);
80+
}
81+
}
82+
83+
}
1.58 KB
Binary file not shown.
913 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)