test.unit.be.fedict.eid.applet.service.AppletServiceServletTest.java Source code

Java tutorial

Introduction

Here is the source code for test.unit.be.fedict.eid.applet.service.AppletServiceServletTest.java

Source

/*
 * eID Applet Project.
 * Copyright (C) 2008-2009 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package test.unit.be.fedict.eid.applet.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.RSAKeyGenParameterSpec;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.joda.time.DateTime;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mortbay.jetty.security.SslSocketConnector;
import org.mortbay.jetty.testing.ServletTester;

import be.fedict.eid.applet.service.Address;
import be.fedict.eid.applet.service.AppletServiceServlet;
import be.fedict.eid.applet.service.Identity;
import be.fedict.eid.applet.shared.AppletProtocolMessageCatalog;
import be.fedict.eid.applet.shared.HelloMessage;
import be.fedict.eid.applet.shared.IdentificationRequestMessage;
import be.fedict.eid.applet.shared.IdentityDataMessage;
import be.fedict.eid.applet.shared.protocol.Transport;
import be.fedict.eid.applet.shared.protocol.Unmarshaller;

public class AppletServiceServletTest {

    private static final Log LOG = LogFactory.getLog(AppletServiceServletTest.class);

    private ServletTester servletTester;

    private String location;

    private String sslLocation;

    private KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024, RSAKeyGenParameterSpec.F4), random);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        return keyPair;
    }

    private SubjectKeyIdentifier createSubjectKeyId(PublicKey publicKey) throws IOException {
        ByteArrayInputStream bais = new ByteArrayInputStream(publicKey.getEncoded());
        SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(bais).readObject());
        return new SubjectKeyIdentifier(info);
    }

    private AuthorityKeyIdentifier createAuthorityKeyId(PublicKey publicKey) throws IOException {

        ByteArrayInputStream bais = new ByteArrayInputStream(publicKey.getEncoded());
        SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(bais).readObject());

        return new AuthorityKeyIdentifier(info);
    }

    private void persistKey(File pkcs12keyStore, PrivateKey privateKey, X509Certificate certificate,
            char[] keyStorePassword, char[] keyEntryPassword) throws KeyStoreException, NoSuchAlgorithmException,
            CertificateException, IOException, NoSuchProviderException {
        KeyStore keyStore = KeyStore.getInstance("pkcs12", BouncyCastleProvider.PROVIDER_NAME);
        keyStore.load(null, keyStorePassword);
        LOG.debug("keystore security provider: " + keyStore.getProvider().getName());
        keyStore.setKeyEntry("default", privateKey, keyEntryPassword, new Certificate[] { certificate });
        FileOutputStream keyStoreOut = new FileOutputStream(pkcs12keyStore);
        keyStore.store(keyStoreOut, keyStorePassword);
        keyStoreOut.close();
    }

    private X509Certificate generateSelfSignedCertificate(KeyPair keyPair, String subjectDn, DateTime notBefore,
            DateTime notAfter) throws IOException, InvalidKeyException, IllegalStateException,
            NoSuchAlgorithmException, SignatureException, CertificateException {
        PublicKey subjectPublicKey = keyPair.getPublic();
        PrivateKey issuerPrivateKey = keyPair.getPrivate();
        String signatureAlgorithm = "SHA1WithRSAEncryption";
        X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
        certificateGenerator.reset();
        certificateGenerator.setPublicKey(subjectPublicKey);
        certificateGenerator.setSignatureAlgorithm(signatureAlgorithm);
        certificateGenerator.setNotBefore(notBefore.toDate());
        certificateGenerator.setNotAfter(notAfter.toDate());
        X509Principal issuerDN = new X509Principal(subjectDn);
        certificateGenerator.setIssuerDN(issuerDN);
        certificateGenerator.setSubjectDN(new X509Principal(subjectDn));
        certificateGenerator.setSerialNumber(new BigInteger(128, new SecureRandom()));

        certificateGenerator.addExtension(X509Extensions.SubjectKeyIdentifier, false,
                createSubjectKeyId(subjectPublicKey));
        PublicKey issuerPublicKey;
        issuerPublicKey = subjectPublicKey;
        certificateGenerator.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
                createAuthorityKeyId(issuerPublicKey));

        certificateGenerator.addExtension(X509Extensions.BasicConstraints, false, new BasicConstraints(true));

        X509Certificate certificate;
        certificate = certificateGenerator.generate(issuerPrivateKey);

        /*
         * Next certificate factory trick is needed to make sure that the
         * certificate delivered to the caller is provided by the default
         * security provider instead of BouncyCastle. If we don't do this trick
         * we might run into trouble when trying to use the CertPath validator.
         */
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        certificate = (X509Certificate) certificateFactory
                .generateCertificate(new ByteArrayInputStream(certificate.getEncoded()));
        return certificate;
    }

    private static int getFreePort() throws Exception {
        ServerSocket serverSocket = new ServerSocket(0);
        int port = serverSocket.getLocalPort();
        serverSocket.close();
        return port;
    }

    @Before
    public void setUp() throws Exception {
        this.servletTester = new ServletTester();
        this.servletTester.addServlet(AppletServiceServlet.class, "/");

        Security.addProvider(new BouncyCastleProvider());

        KeyPair keyPair = generateKeyPair();
        DateTime notBefore = new DateTime();
        DateTime notAfter = notBefore.plusMonths(1);
        X509Certificate certificate = generateSelfSignedCertificate(keyPair, "CN=localhost", notBefore, notAfter);
        File tmpP12File = File.createTempFile("ssl-", ".p12");
        LOG.debug("p12 file: " + tmpP12File.getAbsolutePath());
        persistKey(tmpP12File, keyPair.getPrivate(), certificate, "secret".toCharArray(), "secret".toCharArray());

        SslSocketConnector sslSocketConnector = new SslSocketConnector();
        sslSocketConnector.setKeystore(tmpP12File.getAbsolutePath());
        sslSocketConnector.setTruststore(tmpP12File.getAbsolutePath());
        sslSocketConnector.setTruststoreType("pkcs12");
        sslSocketConnector.setKeystoreType("pkcs12");
        sslSocketConnector.setPassword("secret");
        sslSocketConnector.setKeyPassword("secret");
        sslSocketConnector.setTrustPassword("secret");
        sslSocketConnector.setMaxIdleTime(30000);
        int sslPort = getFreePort();
        sslSocketConnector.setPort(sslPort);
        this.servletTester.getContext().getServer().addConnector(sslSocketConnector);
        this.sslLocation = "https://localhost:" + sslPort + "/";

        this.servletTester.start();
        this.location = this.servletTester.createSocketConnector(true);

        SSLContext sslContext = SSLContext.getInstance("TLS");
        TrustManager trustManager = new TestTrustManager(certificate);
        sslContext.init(null, new TrustManager[] { trustManager }, null);
        SSLContext.setDefault(sslContext);
    }

    private static class TestTrustManager implements X509TrustManager {

        private final X509Certificate serverCertificate;

        public TestTrustManager(X509Certificate serverCertificate) {
            this.serverCertificate = serverCertificate;
        }

        public void checkClientTrusted(X509Certificate[] chain, String authnType) throws CertificateException {
            throw new CertificateException("not implemented");
        }

        public void checkServerTrusted(X509Certificate[] chain, String authnType) throws CertificateException {
            if (false == this.serverCertificate.equals(chain[0])) {
                throw new CertificateException("server certificate not trusted");
            }
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

    @After
    public void tearDown() throws Exception {
        this.servletTester.stop();
    }

    @Test
    public void get() throws Exception {
        // setup
        LOG.debug("URL: " + this.location);
        HttpClient httpClient = new HttpClient();
        GetMethod getMethod = new GetMethod(this.location);

        // operate
        int result = httpClient.executeMethod(getMethod);

        // verify
        assertEquals(HttpServletResponse.SC_OK, result);
        String responseBody = getMethod.getResponseBodyAsString();
        LOG.debug("Response body: " + responseBody);
        assertTrue(responseBody.indexOf("applet service") != 1);
    }

    @Test
    public void doPostRequiresSSL() throws Exception {
        // setup
        LOG.debug("URL: " + this.location);
        HttpClient httpClient = new HttpClient();
        PostMethod postMethod = new PostMethod(this.location);

        // operate
        int result = httpClient.executeMethod(postMethod);

        // verify
        assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, result);
    }

    @Test
    public void sslPostIdentityMessage() throws Exception {
        // setup
        byte[] idFile = IOUtils.toByteArray(AppletServiceServletTest.class.getResourceAsStream("/id-alice.tlv")); // XXX: expired

        LOG.debug("SSL URL: " + this.sslLocation);
        HttpClient httpClient = new HttpClient();

        HelloMessage helloMessage = new HelloMessage();
        PostMethod postMethod = new PostMethod(this.sslLocation);
        PostMethodHttpTransmitter httpTransmitter = new PostMethodHttpTransmitter(postMethod);
        Transport.transfer(helloMessage, httpTransmitter);
        int result = httpClient.executeMethod(postMethod);
        assertEquals(HttpServletResponse.SC_OK, result);

        Header setCookieHeader = postMethod.getResponseHeader("Set-Cookie");
        String setCookieValue = setCookieHeader.getValue();
        int sessionIdIdx = setCookieValue.indexOf("JSESSIONID=") + "JESSSIONID=".length();
        String sessionId = setCookieValue.substring(sessionIdIdx, setCookieValue.indexOf(";", sessionIdIdx));
        LOG.debug("session id: " + sessionId);

        postMethod = new PostMethod(this.sslLocation);
        postMethod.addRequestHeader("X-AppletProtocol-Version", "1");
        postMethod.addRequestHeader("X-AppletProtocol-Type", "IdentityDataMessage");
        postMethod.addRequestHeader("X-AppletProtocol-IdentityFileSize", Integer.toString(idFile.length));
        RequestEntity requestEntity = new ByteArrayRequestEntity(idFile);
        postMethod.setRequestEntity(requestEntity);

        // operate
        result = httpClient.executeMethod(postMethod);

        // verify
        assertEquals(HttpServletResponse.SC_OK, result);

        HttpSession httpSession = this.servletTester.getContext().getSessionHandler().getSessionManager()
                .getHttpSession(sessionId);
        Identity identity = (Identity) httpSession.getAttribute("eid.identity");
        assertNotNull(identity);
        assertEquals("Alice Geldigekaart2266", identity.firstName);

        Address address = (Address) httpSession.getAttribute("eid.address");
        assertNull(address);
    }

    @Test
    public void sslPostIdentityMessageViaTransport() throws Exception {
        // setup
        byte[] idFile = IOUtils.toByteArray(AppletServiceServletTest.class.getResourceAsStream("/id-alice.tlv")); // XXX: expired

        LOG.debug("SSL URL: " + this.sslLocation);
        HttpClient httpClient = new HttpClient();

        HelloMessage helloMessage = new HelloMessage();
        PostMethod postMethod = new PostMethod(this.sslLocation);
        PostMethodHttpTransmitter httpTransmitter = new PostMethodHttpTransmitter(postMethod);
        Transport.transfer(helloMessage, httpTransmitter);
        int result = httpClient.executeMethod(postMethod);
        assertEquals(HttpServletResponse.SC_OK, result);

        Header setCookieHeader = postMethod.getResponseHeader("Set-Cookie");
        String setCookieValue = setCookieHeader.getValue();
        int sessionIdIdx = setCookieValue.indexOf("JSESSIONID=") + "JSESSIONID=".length();
        String sessionId = setCookieValue.substring(sessionIdIdx, setCookieValue.indexOf(";", sessionIdIdx));
        LOG.debug("session id: " + sessionId);

        postMethod = new PostMethod(this.sslLocation);
        httpTransmitter = new PostMethodHttpTransmitter(postMethod);
        IdentityDataMessage identityDataMessage = new IdentityDataMessage();
        identityDataMessage.identityFileSize = idFile.length;
        identityDataMessage.body = idFile;
        Transport.transfer(identityDataMessage, httpTransmitter);

        // operate
        result = httpClient.executeMethod(postMethod);

        // verify
        assertEquals(HttpServletResponse.SC_OK, result);

        HttpSession httpSession = this.servletTester.getContext().getSessionHandler().getSessionManager()
                .getHttpSession(sessionId);
        Identity identity = (Identity) httpSession.getAttribute("eid.identity");
        assertNotNull(identity);
        assertEquals("Alice Geldigekaart2266", identity.firstName);

        Address address = (Address) httpSession.getAttribute("eid.address");
        assertNull(address);
    }

    @Test
    public void helloMessage() throws Exception {
        // setup
        HttpClient httpClient = new HttpClient();
        PostMethod postMethod = new PostMethod(this.sslLocation);
        HelloMessage helloMessage = new HelloMessage();
        PostMethodHttpTransmitter httpTransmitter = new PostMethodHttpTransmitter(postMethod);
        Transport.transfer(helloMessage, httpTransmitter);

        // operate
        int result = httpClient.executeMethod(postMethod);

        // verify
        assertEquals(HttpServletResponse.SC_OK, result);

        Unmarshaller unmarshaller = new Unmarshaller(new AppletProtocolMessageCatalog());

        PostMethodHttpReceiver httpReceiver = new PostMethodHttpReceiver(postMethod);
        Object resultMessageObject = unmarshaller.receive(httpReceiver);
        assertTrue(resultMessageObject instanceof IdentificationRequestMessage);
    }
}