blob: a5396e04f3bc3d5225a5bb8655b91ae0d59cb7bc [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client;
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.security.cert.CertificateException;
import java.util.concurrent.ExecutionException;
import javax.net.ssl.SSLHandshakeException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* This test class runs tests to make sure that hostname verification (http://www.ietf.org/rfc/rfc2818.txt
* section 3.1) is configurable in SslContextFactory and works as expected.
*/
public class HostnameVerificationTest
{
private SslContextFactory clientSslContextFactory = new SslContextFactory();
private Server server = new Server();
private HttpClient client;
private NetworkConnector connector;
@Before
public void setUp() throws Exception
{
SslContextFactory serverSslContextFactory = new SslContextFactory();
serverSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
serverSslContextFactory.setKeyStorePassword("storepwd");
connector = new ServerConnector(server, serverSslContextFactory);
server.addConnector(connector);
server.setHandler(new DefaultHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.getWriter().write("foobar");
}
});
server.start();
// keystore contains a hostname which doesn't match localhost
clientSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
clientSslContextFactory.setKeyStorePassword("storepwd");
QueuedThreadPool executor = new QueuedThreadPool();
executor.setName(executor.getName() + "-client");
client = new HttpClient(clientSslContextFactory);
client.setExecutor(executor);
client.start();
}
@After
public void tearDown() throws Exception
{
client.stop();
server.stop();
server.join();
}
/**
* This test is supposed to verify that hostname verification works as described in:
* http://www.ietf.org/rfc/rfc2818.txt section 3.1. It uses a certificate with a common name different to localhost
* and sends a request to localhost. This should fail with a SSLHandshakeException.
*
* @throws Exception
*/
@Test
public void simpleGetWithHostnameVerificationEnabledTest() throws Exception
{
clientSslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
String uri = "https://localhost:" + connector.getLocalPort() + "/";
try
{
client.GET(uri);
Assert.fail("sending request to client should have failed with an Exception!");
}
catch (ExecutionException x)
{
// The test may fail in 2 ways, since the CertificateException thrown because of the hostname
// verification failure is not rethrown immediately by the JDK SSL implementation, but only
// rethrown on the next read or write.
// Therefore this test may catch a SSLHandshakeException, or a ClosedChannelException.
// If it is the former, we verify that its cause is a CertificateException.
// ExecutionException wraps an SSLHandshakeException
Throwable cause = x.getCause();
if (cause==null)
{
x.printStackTrace();
Assert.fail("No cause?");
}
if (cause instanceof SSLHandshakeException)
Assert.assertThat(cause.getCause().getCause(), Matchers.instanceOf(CertificateException.class));
else
Assert.assertThat(cause, Matchers.instanceOf(ClosedChannelException.class));
}
}
/**
* This test has hostname verification disabled and connecting, ssl handshake and sending the request should just
* work fine.
*
* @throws Exception
*/
@Test
public void simpleGetWithHostnameVerificationDisabledTest() throws Exception
{
clientSslContextFactory.setEndpointIdentificationAlgorithm(null);
String uri = "https://localhost:" + connector.getLocalPort() + "/";
try
{
client.GET(uri);
}
catch (ExecutionException e)
{
Assert.fail("SSLHandshake should work just fine as hostname verification is disabled! " + e.getMessage());
}
}
/**
* This test has hostname verification disabled by setting trustAll to true and connecting,
* ssl handshake and sending the request should just work fine.
*
* @throws Exception
*/
@Test
public void trustAllDisablesHostnameVerificationTest() throws Exception
{
clientSslContextFactory.setTrustAll(true);
String uri = "https://localhost:" + connector.getLocalPort() + "/";
try
{
client.GET(uri);
}
catch (ExecutionException e)
{
Assert.fail("SSLHandshake should work just fine as hostname verification is disabled! " + e.getMessage());
}
}
}