package co.codewizards.cloudstore.test; import static co.codewizards.cloudstore.core.oio.OioFileFactory.*; import static org.assertj.core.api.Assertions.*; import java.io.IOException; import java.security.cert.CertificateException; import javax.net.ssl.SSLException; import javax.ws.rs.client.ClientBuilder; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import co.codewizards.cloudstore.core.config.ConfigDir; import co.codewizards.cloudstore.core.dto.RemoteException; import co.codewizards.cloudstore.core.oio.File; import co.codewizards.cloudstore.core.util.ExceptionUtil; import co.codewizards.cloudstore.core.util.TestException; import co.codewizards.cloudstore.rest.client.ClientBuilderDefaultValuesDecorator; import co.codewizards.cloudstore.rest.client.CloudStoreRestClient; import co.codewizards.cloudstore.rest.client.request.TestRequest; import co.codewizards.cloudstore.rest.client.ssl.CheckServerTrustedCertificateExceptionContext; import co.codewizards.cloudstore.rest.client.ssl.CheckServerTrustedCertificateExceptionResult; import co.codewizards.cloudstore.rest.client.ssl.DynamicX509TrustManagerCallback; import co.codewizards.cloudstore.rest.client.ssl.HostnameVerifierAllowingAll; import co.codewizards.cloudstore.rest.client.ssl.SSLContextBuilder; import co.codewizards.cloudstore.rest.server.service.TestService; /** * Test asserting the certificate handling using the {@link DynamicX509TrustManagerCallback} works correctly. * It uses the simple {@link TestService} for this purpose and checks it completely: success + exception * (with {@link RemoteException} being thrown). * @author Marco หงุ่ยตระกูล-Schulze - marco at codewizards dot co */ public class CertificateHandlingAndTestServiceIT extends AbstractIT { private static final Logger logger = LoggerFactory.getLogger(CertificateHandlingAndTestServiceIT.class); private ClientBuilder clientBuilder; static File getRandomTrustStoreFile() throws IOException { final File dir = createFile(ConfigDir.getInstance().getFile(), "ssl.client"); dir.mkdirs(); final File trustStoreFile = createTempFile("truststore_", null, dir); trustStoreFile.delete(); // It must not exist (reading it fails). return trustStoreFile; } @Before public void setUp(){ clientBuilder = new ClientBuilderDefaultValuesDecorator(); } @Test public void testSuccessWithPermanentTrust() throws Exception { final File trustStoreFile = getRandomTrustStoreFile(); final long[] handleCertificateExceptionCounter = new long[1]; final DynamicX509TrustManagerCallback callback1 = new DynamicX509TrustManagerCallbackTrustingPermanently(handleCertificateExceptionCounter); clientBuilder.sslContext(SSLContextBuilder.create().trustStoreFile(trustStoreFile).callback(callback1).build()); CloudStoreRestClient cloudStoreRestClient = new CloudStoreRestClient(getSecureUrl(), clientBuilder); cloudStoreRestClient.execute(new TestRequest(false)); assertThat(handleCertificateExceptionCounter[0]).isEqualTo(1); cloudStoreRestClient = new CloudStoreRestClient(getSecureUrl(), clientBuilder); final DynamicX509TrustManagerCallback callback2 = new DynamicX509TrustManagerCallback() { @Override public CheckServerTrustedCertificateExceptionResult handleCheckServerTrustedCertificateException(final CheckServerTrustedCertificateExceptionContext context) { Assert.fail("The certificate trust should have been stored permanently! But it was not."); return null; } }; clientBuilder = new ClientBuilderDefaultValuesDecorator() .sslContext(SSLContextBuilder.create().trustStoreFile(trustStoreFile).callback(callback2).build()); cloudStoreRestClient.execute(new TestRequest(false)); } @Test public void testException() throws Exception { final File trustStoreFile = getRandomTrustStoreFile(); final long[] handleCertificateExceptionCounter = new long[1]; final DynamicX509TrustManagerCallback callback = new DynamicX509TrustManagerCallbackTrustingPermanently(handleCertificateExceptionCounter); clientBuilder.sslContext(SSLContextBuilder.create().trustStoreFile(trustStoreFile).callback(callback).build()); final CloudStoreRestClient cloudStoreRestClient = new CloudStoreRestClient(getSecureUrl(), clientBuilder); try { cloudStoreRestClient.execute(new TestRequest(true)); Assert.fail("cloudStoreRESTClient.testException() should have thrown a RemoteException, but it did not!"); } catch (final Exception x) { logger.info(x.toString(), x); assertThat(handleCertificateExceptionCounter[0]).isEqualTo(1); assertThat(x).isInstanceOf(TestException.class); assertThat(x.getMessage()).isEqualTo("Test"); assertThat(x.getCause()).isNotNull(); assertThat(x.getCause()).isInstanceOf(RemoteException.class); final RemoteException rx = (RemoteException) x.getCause(); assertThat(rx.getErrorClassName()).isEqualTo(TestException.class.getName()); assertThat(rx.getMessage()).isEqualTo("Test"); assertThat(rx.getStackTrace()).isNotNull(); assertThat(rx.getStackTrace().length).isGreaterThan(0); assertThat(rx.getStackTrace()[0]).isNotNull(); assertThat(rx.getStackTrace()[0].getClassName()).isEqualTo("co.codewizards.cloudstore.rest.server.service.TestService"); } } @Test public void nonTrustedCertificate() throws Exception { final File trustStoreFile = getRandomTrustStoreFile(); final long[] handleCertificateExceptionCounter = new long[1]; final DynamicX509TrustManagerCallback callback = new DynamicX509TrustManagerCallbackTrustingPermanently(handleCertificateExceptionCounter) { @Override public CheckServerTrustedCertificateExceptionResult handleCheckServerTrustedCertificateException(final CheckServerTrustedCertificateExceptionContext context) { final CheckServerTrustedCertificateExceptionResult result = super.handleCheckServerTrustedCertificateException(context); result.setTrusted(false); return result; } }; clientBuilder.sslContext(SSLContextBuilder.create().trustStoreFile(trustStoreFile).callback(callback).build()); final CloudStoreRestClient cloudStoreRestClient = new CloudStoreRestClient(getSecureUrl(), clientBuilder); try { cloudStoreRestClient.execute(new TestRequest(false)); Assert.fail("The certificate was trusted - but it should *not* be!!!"); } catch (final Exception x) { final CertificateException certificateException = ExceptionUtil.getCause(x, CertificateException.class); final SSLException sslException = ExceptionUtil.getCause(x, SSLException.class); if (certificateException == null && sslException == null) throw x; } assertThat(handleCertificateExceptionCounter[0]).isEqualTo(1); } @Test public void testSuccessWithTemporaryTrust() throws Exception { final File trustStoreFile = getRandomTrustStoreFile(); final long[] handleCertificateExceptionCounter = new long[1]; final DynamicX509TrustManagerCallback callback = new DynamicX509TrustManagerCallbackTrustingTemporarily(handleCertificateExceptionCounter); clientBuilder.sslContext(SSLContextBuilder.create().trustStoreFile(trustStoreFile).callback(callback).build()); CloudStoreRestClient cloudStoreRestClient = new CloudStoreRestClient(getSecureUrl(), clientBuilder); cloudStoreRestClient.execute(new TestRequest(false)); assertThat(handleCertificateExceptionCounter[0]).isEqualTo(1); clientBuilder = new ClientBuilderDefaultValuesDecorator() .sslContext(SSLContextBuilder.create().trustStoreFile(trustStoreFile).callback(callback).build()); cloudStoreRestClient = new CloudStoreRestClient(getSecureUrl(), clientBuilder); cloudStoreRestClient.execute(new TestRequest(false)); assertThat(handleCertificateExceptionCounter[0]).isEqualTo(2); } }