org.wildfly.elytron.web.undertow.server.ClientCertAuthenticationTest.java Source code

Java tutorial

Introduction

Here is the source code for org.wildfly.elytron.web.undertow.server.ClientCertAuthenticationTest.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2016 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.wildfly.elytron.web.undertow.server;

import static org.junit.Assert.assertEquals;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Principal;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.wildfly.elytron.web.undertow.server.util.UndertowServer;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.permission.LoginPermission;
import org.wildfly.security.auth.realm.KeyStoreBackedSecurityRealm;
import org.wildfly.security.auth.server.PrincipalDecoder;
import org.wildfly.security.auth.server.RealmIdentity;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.SecurityRealm;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.permission.PermissionVerifier;
import org.wildfly.security.ssl.SSLContextBuilder;
import org.wildfly.security.x500.X500AttributePrincipalDecoder;

/**
 * Test case for CLIENT_CERT authentication.
 *
 * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
 */
public class ClientCertAuthenticationTest extends AbstractHttpServerMechanismTest {

    private SecurityRealm securityRealm;

    @Rule
    public UndertowServer serverA = new UndertowServer(createRootHttpHandler(), () -> {
        try {
            return new SSLContextBuilder().setSecurityDomain(getSecurityDomain())
                    .setKeyManager(getKeyManager("/tls/scarab.keystore")).setTrustManager(getCATrustManager())
                    .build().create();
        } catch (Exception cause) {
            throw new RuntimeException("Could not create server ssl context.", cause);
        }
    });

    @Rule
    public UndertowServer serverB = new UndertowServer(createRootHttpHandler(), 7777, () -> {
        try {
            return new SSLContextBuilder().setKeyManager(getKeyManager("/tls/scarab.keystore"))
                    .setTrustManager(getCATrustManager()).setWantClientAuth(true).build().create();
        } catch (Exception cause) {
            throw new RuntimeException("Could not create server ssl context.", cause);
        }
    });

    private AtomicInteger realmIdentityInvocationCount = new AtomicInteger(0);

    @Test
    public void testSuccessfulAuthentication() throws Exception {
        HttpClient httpClient = HttpClientBuilder.create().setSSLContext(createRecognizedSSLContext())
                .setSSLHostnameVerifier((String h, SSLSession s) -> true).build();

        assertSuccessfulResponse(httpClient.execute(new HttpGet(serverA.createUri())), "ladybird");
    }

    @Test
    public void testClientCertAfterSession() throws Exception {
        HttpClient httpClient = HttpClientBuilder.create().setSSLContext(createRecognizedSSLContext())
                .setSSLHostnameVerifier((String h, SSLSession s) -> true).build();
        assertSuccessfulResponse(httpClient.execute(new HttpGet(serverB.createUri())), "ladybird");
    }

    @Test
    public void testSSLSessionIdentityCacheHit() throws Exception {
        HttpClient httpClient = HttpClientBuilder.create().setSSLContext(createRecognizedSSLContext())
                .setSSLHostnameVerifier((String h, SSLSession s) -> true).build();

        assertSuccessfulResponse(httpClient.execute(new HttpGet(serverA.createUri())), "ladybird");

        for (int i = 0; i < 10; i++) {
            assertSuccessfulResponse(httpClient.execute(new HttpGet(serverA.createUri())), "ladybird");
        }

        // two hits during the first interaction, after that we should expect no more hits to the realm
        assertEquals(2, this.realmIdentityInvocationCount.get());
    }

    @Test
    public void testFailedAuthentication() throws Exception {
        HttpClient httpClient = HttpClientBuilder.create().setSSLContext(createUnrecognizedSSLContext())
                .setSSLHostnameVerifier((String h, SSLSession s) -> true).build();
        assertEquals(HttpStatus.SC_FORBIDDEN,
                httpClient.execute(new HttpGet(serverA.createUri())).getStatusLine().getStatusCode());
    }

    @Override
    protected String getMechanismName() {
        return "CLIENT-CERT";
    }

    @Override
    protected SecurityDomain doCreateSecurityDomain() throws Exception {
        KeyStoreBackedSecurityRealm delegate = new KeyStoreBackedSecurityRealm(
                loadKeyStore("/tls/beetles.keystore"));

        this.securityRealm = new SecurityRealm() {
            @Override
            public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException {
                realmIdentityInvocationCount.incrementAndGet();
                return delegate.getRealmIdentity(principal);
            }

            @Override
            public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType,
                    String algorithmName) throws RealmUnavailableException {
                return delegate.getCredentialAcquireSupport(credentialType, algorithmName);
            }

            @Override
            public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType,
                    String algorithmName) throws RealmUnavailableException {
                return delegate.getEvidenceVerifySupport(evidenceType, algorithmName);
            }
        };

        SecurityDomain.Builder builder = SecurityDomain.builder().addRealm("KeystoreRealm", securityRealm).build()
                .setDefaultRealmName("KeystoreRealm")
                .setPrincipalDecoder(PrincipalDecoder.aggregate(new X500AttributePrincipalDecoder("2.5.4.3", 1),
                        PrincipalDecoder.DEFAULT))
                .setPreRealmRewriter((String s) -> s.toLowerCase())
                .setPermissionMapper((principal, roles) -> PermissionVerifier.from(new LoginPermission()));

        return builder.build();
    }

    private SSLContext createRecognizedSSLContext() throws Exception {
        SSLContext sslContext = SSLContext.getInstance("TLS");

        sslContext.init(new KeyManager[] { getKeyManager("/tls/ladybird.keystore") },
                new TrustManager[] { getCATrustManager() }, null);

        return sslContext;
    }

    private SSLContext createUnrecognizedSSLContext() throws Exception {
        SSLContext sslContext = SSLContext.getInstance("TLS");

        sslContext.init(new KeyManager[] { getKeyManager("/tls/tiger.keystore") },
                new TrustManager[] { getCATrustManager() }, null);

        return sslContext;
    }

    /**
     * Get the key manager backed by the specified key store.
     *
     * @param keystoreName the name of the key store to load.
     * @return the initialised key manager.
     */
    private X509ExtendedKeyManager getKeyManager(final String keystorePath) throws Exception {
        KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(loadKeyStore(keystorePath), "Elytron".toCharArray());

        for (KeyManager current : keyManagerFactory.getKeyManagers()) {
            if (current instanceof X509ExtendedKeyManager) {
                return (X509ExtendedKeyManager) current;
            }
        }

        throw new IllegalStateException("Unable to obtain X509ExtendedKeyManager.");
    }

    /**
     * Get the trust manager that trusts all certificates signed by the certificate authority.
     *
     * @return the trust manager that trusts all certificates signed by the certificate authority.
     * @throws KeyStoreException
     */
    private X509TrustManager getCATrustManager() throws Exception {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(loadKeyStore("/tls/ca.truststore"));

        for (TrustManager current : trustManagerFactory.getTrustManagers()) {
            if (current instanceof X509TrustManager) {
                return (X509TrustManager) current;
            }
        }

        throw new IllegalStateException("Unable to obtain X509TrustManager.");
    }

    private KeyStore loadKeyStore(final String path) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("jks");
        try (InputStream caTrustStoreFile = ClientCertAuthenticationTest.class.getResourceAsStream(path)) {
            keyStore.load(caTrustStoreFile, "Elytron".toCharArray());
        }

        return keyStore;
    }
}