package org.jivesoftware.openfire.keystore; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.File; import java.io.FileOutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyStore; import java.security.cert.*; import java.util.*; /** * Unit tests that verify the functionality of {@link OpenfireX509TrustManager}. * * @author Guus der Kinderen, guus.der.kinderen@gmail.com */ public class OpenfireX509TrustManagerTest { /** * An instance that is freshly recreated before each test. */ private OpenfireX509TrustManager systemUnderTest; /** * The keystore that contains the certificates used by the system under test (refreshed before every test invocation). */ private KeyStore trustStore; private String location; // location on disc for the keystore. Used to clean-up after each test. private X509Certificate[] validChain; private X509Certificate[] expiredIntChain; private X509Certificate[] expiredRootChain; private X509Certificate[] untrustedCAChain; @Before public void createFixture() throws Exception { // Create a fresh store in a location that holds only temporary files. final String tempDir = System.getProperty("java.io.tmpdir"); location = tempDir + ( tempDir.endsWith( File.separator ) ? "" : File.separator ) + UUID.randomUUID(); trustStore = KeyStore.getInstance( KeyStore.getDefaultType()); final String password = "TS%WV@# aSG 4"; trustStore.load( null, password.toCharArray() ); // Populate the store with a valid CA certificate. validChain = KeystoreTestUtils.generateValidCertificateChain(); expiredIntChain = KeystoreTestUtils.generateCertificateChainWithExpiredIntermediateCert(); expiredRootChain = KeystoreTestUtils.generateCertificateChainWithExpiredRootCert(); untrustedCAChain = KeystoreTestUtils.generateValidCertificateChain(); trustStore.setCertificateEntry( getLast( validChain ).getSubjectDN().getName(), getLast( validChain ) ); trustStore.setCertificateEntry( getLast( expiredIntChain ).getSubjectDN().getName(), getLast( expiredIntChain ) ); trustStore.setCertificateEntry( getLast( expiredRootChain ).getSubjectDN().getName(), getLast( expiredRootChain ) ); // Persist the key store file try ( FileOutputStream fos = new FileOutputStream( location ) ) { trustStore.store( fos, password.toCharArray() ); } // Create the Trust Manager that is subject of these tests. systemUnderTest = new OpenfireX509TrustManager( trustStore, false, true ); } /** * Returns the last element from the provided array. * @param chain An array (cannot be null). * @return The last element of the provided array. */ private static <X extends Object> X getLast(X[]chain) { return chain[ chain.length - 1 ]; } @After public void tearDown() throws Exception { // Attempt to delete any left-overs from the test. validChain = null; if ( trustStore != null) { trustStore = null; Files.deleteIfExists( Paths.get( location ) ); } systemUnderTest = null; } /** * This test verifies that {@link OpenfireX509TrustManager#getAcceptedIssuers()} does not return expired * certificates. */ @Test public void testAcceptedIssuersAreAllValid() throws Exception { // Setup fixture. // Execute system under test. final X509Certificate[] result = systemUnderTest.getAcceptedIssuers(); // Verify results. Assert.assertEquals( 2, result.length ); Assert.assertTrue( Arrays.asList( result ).contains( getLast( validChain ) ) ); Assert.assertTrue( Arrays.asList( result ).contains( getLast( expiredIntChain ) ) ); Assert.assertFalse( Arrays.asList( result ).contains( getLast( expiredRootChain ) ) ); } /** * Verifies that a valid chain is not rejected. */ @Test public void testValidChain() throws Exception { // Setup fixture. // Execute system under test. systemUnderTest.checkClientTrusted( validChain, "RSA" ); // Verify result // (getting here without an exception being thrown is enough). } /** * Verifies that a chain that has an intermediate certificate that is expired is rejected. */ @Test(expected = CertificateException.class) public void testInvalidChainExpiredIntermediate() throws Exception { // Setup fixture. // Execute system under test. systemUnderTest.checkClientTrusted( expiredIntChain, "RSA" ); } /** * Verifies that a chain that has an root certificate (trust anchor) that is expired is rejected. */ @Test(expected = CertificateException.class) public void testInvalidChainExpiredTrustAnchor() throws Exception { // Setup fixture. // Execute system under test. systemUnderTest.checkClientTrusted( expiredRootChain, "RSA" ); } /** * Verifies that a chain that is missing an intermediate certificate is rejected. */ @Test(expected = CertificateException.class) public void testInvalidChainMissingIntermediate() throws Exception { // Setup fixture. assert validChain.length == 4; final X509Certificate[] input = new X509Certificate[ 3 ]; input[ 0 ] = validChain[ 0 ]; input[ 1 ] = validChain[ 2 ]; input[ 2 ] = validChain[ 3 ]; // Execute system under test. systemUnderTest.checkClientTrusted( input, "RSA" ); } /** * Verifies that a chain that is valid, but does not have its root CA certificate in the trust store, is rejected. */ @Test(expected = CertificateException.class) public void testInvalidChainCAnotTrusted() throws Exception { // Setup fixture. // Execute system under test. systemUnderTest.checkClientTrusted( untrustedCAChain, "RSA" ); } }