Regarding the dummy TrustManager point, in my experience trust and hostname
verification are
separate steps (at least in Java’s implementation of SSL). Here are some tests:
@Test
public void selfSignedHostnameVerified() throws Exception {
assertEquals(204, getResponseCode("cn=localhost", null, null));
}
@Test
public void selfSignedHostnameNotVerifiedHostHeaderNotSent() throws Exception {
try {
getResponseCode("cn=otherhostname", null, null);
fail("Expected exception");
} catch (SSLHandshakeException expected) {
assertEquals("No name matching localhost found", expected.getMessage());
}
}
@Test
public void selfSignedHostnameNotVerifiedHostHeaderSent() throws Exception {
try {
getResponseCode("cn=otherhostname", "otherhostname", null);
fail("Expected exception");
} catch (SSLHandshakeException expected) {
assertEquals("No name matching localhost found", expected.getMessage());
}
}
@Test
public void selfSignedHostnameVerifiedByCustomVerifier() throws Exception {
assertEquals(204, getResponseCode("cn=otherhostname", "otherhostname",
((hostname, session) -> true)));
}
private int getResponseCode(String certName, String hostHeader,
HostnameVerifier hostnameVerifier) throws Exception {
var keystorePassword = "secret";
var keystoreFile = keystoreFileWithSelfSignedCertificate(certName,
keystorePassword);
var keystore = KeyStore.getInstance("PKCS12");
keystore.load(new ByteArrayInputStream(Files.readAllBytes(keystoreFile)),
keystorePassword.toCharArray());
var keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keystore, keystorePassword.toCharArray());
var serverContext = SSLContext.getInstance("TLSv1.3");
serverContext.init(keyManagerFactory.getKeyManagers(), null, new
SecureRandom());
int port = 8443;
var server = HttpsServer.create(new InetSocketAddress("0.0.0.0", port),
100);
server.setHttpsConfigurator(new HttpsConfigurator(serverContext));
server.createContext("/foo", exchange -> exchange.sendResponseHeaders(204,
-1));
server.setExecutor(Executors.newSingleThreadExecutor());
server.start();
var clientContext = SSLContext.getInstance("TLSv1.3");
clientContext.init(null, new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String
authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String
authType) {
}
} }, new SecureRandom());
var originalDefaultSocketFactory =
HttpsURLConnection.getDefaultSSLSocketFactory();
var originalHostnameVerifier =
HttpsURLConnection.getDefaultHostnameVerifier();
try {
HttpsURLConnection.setDefaultSSLSocketFactory(clientContext.getSocketFactory());
if (hostnameVerifier != null) {
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
}
var connection = (HttpsURLConnection) new URL("https://localhost:" +
port + "/foo").openConnection();
if (hostHeader != null) {
connection.addRequestProperty("Host", hostHeader);
}
connection.connect();
return connection.getResponseCode();
} finally {
HttpsURLConnection.setDefaultSSLSocketFactory(originalDefaultSocketFactory);
HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
Files.delete(keystoreFile);
server.stop(1);
}
}
> On Nov 2, 2018, at 1:26 AM, Michael McMahon <[email protected]>
> wrote:
>
> There is a fix in progress for
> https://bugs.openjdk.java.net/browse/JDK-8213189
> which will allow the "Host" header to be overridden, along with some of the
> other currently
> restricted ones.
>
> I don't follow the other point though. With a dummy TrustManager, the
> contents of the
> server's certificate is ignored and can contain anything, and be self-signed,
> I believe.
>
> Michael
>
> On 01/11/2018, 19:50, Anders Wisch wrote:
>> Yes, although this is more restrictive because it means I have to have
>> common name or subject
>> alternative names in the self-signed certificate for “localhost”,
>> “localhost.localdomain”,
>> “127.0.0.1”, or similar so that my requests get routed to the local server.
>> Testing hostname-based
>> redirects under SSL is also made difficult by the host header being on the
>> restricted list. If
>> the hostname verifier used the contents of the host header instead of the
>> URI's host, and the host
>> header was customizable, then a self-signed certificate with CN and SAN
>> entries for all of the
>> names in question would work (provided I supplied a dummy TrustManager like
>> you suggested).
>>
>>> On Nov 1, 2018, at 11:45 AM, Michael McMahon<[email protected]>
>>> wrote:
>>>
>>> You could also isolate the behavior to a specific SSLContext (and therefore
>>> HttpClient)
>>> by initializing the SSLContext with a dummy TrustManager (if it's only for
>>> testing).
>>>
>>> - Michael.
>>>
>>> On 01/11/2018, 18:09, Anders Wisch wrote:
>>>> Thankfully, all of my uses are for testing. To test hostname-based
>>>> redirects or integration tests of
>>>> server code under SSL I start short-lived servers that serve self-signed
>>>> certificates. Test cases
>>>> use HTTP clients that disable hostname verification, connect to a local
>>>> address and port, and
>>>> sometimes vary the contents of the “Host” header. Since tests can run in
>>>> parallel to speed up suite
>>>> execution and since other tests require secure hostname verification, it’s
>>>> useful to be able to
>>>> isolate the behavior.
>>>>
>>>>> On Nov 1, 2018, at 10:53 AM, Chris Hegarty<[email protected]>
>>>>> wrote:
>>>>>
>>>>> In order to evaluate this request, can you please provide
>>>>> use-cases for such. What “secure” server are you trying
>>>>> to connect to that is unwilling to identify itself in its
>>>>> certificate.
>>>>>
>>>>> -Chris.
>>>>>
>>>>>> On 1 Nov 2018, at 17:48, Anders Wisch<[email protected]> wrote:
>>>>>>
>>>>>> Hi all,
>>>>>>
>>>>>> I think it should be possible to supply a custom
>>>>>> javax.net.ssl.HostnameVerifier while building a
>>>>>> java.net.http.HttpClient. While it is possible to disable standard
>>>>>> hostname verification via the
>>>>>> system property “jdk.internal.httpclient.disableHostnameVerification”,
>>>>>> this doesn’t allow you to
>>>>>> quarantine the behavior to a single HTTP client within the JVM.
>>>>>>
>>>>>> Thanks for your consideration,
>>>>>> Anders Wisch
>>>>>>
>>>>>>
>>