org.wildfly.security.ssl.TestingOcspServer.java Source code

Java tutorial

Introduction

Here is the source code for org.wildfly.security.ssl.TestingOcspServer.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2019 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.security.ssl;

import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.Instant;
import java.util.Map;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import org.junit.Assert;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.matchers.Times;
import org.mockserver.model.Header;
import org.mockserver.model.HttpResponse;
import org.mockserver.model.NottableString;
import org.wildfly.common.iteration.ByteIterator;
import org.xipki.datasource.DataSourceFactory;
import org.xipki.datasource.DataSourceWrapper;
import org.xipki.http.servlet.ServletURI;
import org.xipki.http.servlet.SslReverseProxyMode;
import org.xipki.ocsp.server.impl.HttpOcspServlet;
import org.xipki.ocsp.server.impl.OcspServer;
import org.xipki.security.SecurityFactoryImpl;
import org.xipki.security.SignerFactoryRegisterImpl;

/**
 * Trivial XiPKI based OCSP server for OCSP support testing
 */
public class TestingOcspServer {

    private int port;
    private OcspServer ocspServer = null;
    private ClientAndServer server;
    private Connection connection;
    private SecurityFactoryImpl securityFactory = new SecurityFactoryImpl();

    public TestingOcspServer(int port) throws Exception {
        this.port = port;
        initDatabase();
    }

    private void initDatabase() throws Exception {
        DataSourceFactory dataSourceFactory = new DataSourceFactory();
        DataSourceWrapper dataSourceWrapper = dataSourceFactory.createDataSource("datasource1",
                TestingOcspServer.class.getResource("ocsp-db.properties").openStream(),
                securityFactory.getPasswordResolver());
        connection = dataSourceWrapper.getConnection();

        // structure described in:
        // https://github.com/xipki/xipki/blob/v3.0.0/ca-server/src/main/resources/sql/ocsp-init.xml

        connection.prepareStatement("CREATE TABLE ISSUER (\n" + "    ID INT NOT NULL,\n"
                + "    SUBJECT VARCHAR(350) NOT NULL,\n" + "    NBEFORE BIGINT NOT NULL,\n" // notBefore
                + "    NAFTER BIGINT NOT NULL,\n" // notAfter
                + "    S1C CHAR(28) NOT NULL,\n" // base64 encoded SHA1 sum of the certificate
                + "    REV SMALLINT DEFAULT 0,\n" // whether the certificate is revoked
                + "    RR SMALLINT,\n" // revocation reason
                + "    RT BIGINT,\n" // revocation time
                + "    RIT BIGINT,\n" // revocation invalidity time
                + "    CERT VARCHAR(4000) NOT NULL,\n" + "    CRL_INFO VARCHAR(1000)\n" // CRL information if this issuer is imported from a CRL
                + ");").execute();

        connection
                .prepareStatement("CREATE TABLE CERT (\n" + "    ID BIGINT NOT NULL,\n" + "    IID INT NOT NULL,\n" // issuer id (reference into ISSUER table)
                        + "    SN VARCHAR(40) NOT NULL,\n" // serial number
                        + "    LUPDATE BIGINT NOT NULL,\n" // last update
                        + "    NBEFORE BIGINT,\n" // notBefore
                        + "    NAFTER BIGINT,\n" // notAfter
                        + "    REV SMALLINT DEFAULT 0,\n" // whether the certificate is revoked
                        + "    RR SMALLINT,\n" // revocation reason
                        + "    RT BIGINT,\n" // revocation time
                        + "    RIT BIGINT,\n" // revocation invalidity time
                        + "    PN VARCHAR(45)\n" // certificate profile name
                        + ");")
                .execute();
    }

    public void start() throws Exception {
        Assert.assertNull("OCSP server already started", ocspServer);

        ocspServer = new OcspServer();
        ocspServer.setConfFile(TestingOcspServer.class.getResource("ocsp-responder.xml").getFile());

        securityFactory.setSignerFactoryRegister(new SignerFactoryRegisterImpl());
        ocspServer.setSecurityFactory(securityFactory);

        ocspServer.init();
        HttpOcspServlet servlet = new HttpOcspServlet();
        servlet.setServer(ocspServer);

        server = new ClientAndServer(port);
        server.when(request().withMethod("POST").withPath("/ocsp"), Times.unlimited()).respond(request -> {
            ByteBuf buffer = Unpooled.wrappedBuffer(request.getBody().getRawBytes());
            FullHttpRequest nettyRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST,
                    request.getPath().getValue(), buffer);
            for (Header header : request.getHeaderList()) {
                for (NottableString value : header.getValues()) {
                    nettyRequest.headers().add(header.getName().getValue(), value.getValue());
                }
            }

            FullHttpResponse nettyResponse;
            try {
                nettyResponse = servlet.service(nettyRequest, new ServletURI(request.getPath().getValue()), null,
                        SslReverseProxyMode.NONE);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            HttpResponse response = response().withStatusCode(nettyResponse.status().code())
                    .withBody(nettyResponse.content().array());

            for (Map.Entry<String, String> header : nettyResponse.headers()) {
                response.withHeader(header.getKey(), header.getValue());
            }

            return response;
        });
    }

    public void stop() throws SQLException {
        Assert.assertNotNull("OCSP server not started", ocspServer);
        server.stop();
        ocspServer.shutdown();
        connection.close();
        ocspServer = null;
    }

    public void createIssuer(int id, X509Certificate issuer)
            throws SQLException, CertificateException, NoSuchAlgorithmException {
        Assert.assertNull("OCSP server already started", ocspServer);

        MessageDigest digest = MessageDigest.getInstance("SHA-1");
        PreparedStatement statement = connection.prepareStatement(
                "INSERT INTO ISSUER (ID, SUBJECT, NBEFORE, NAFTER, S1C, CERT) VALUES (?, ?, ?, ?, ?, ?)");
        statement.setInt(1, id);
        statement.setString(2, issuer.getSubjectDN().toString());
        statement.setLong(3, issuer.getNotBefore().toInstant().getEpochSecond());
        statement.setLong(4, issuer.getNotAfter().toInstant().getEpochSecond());
        statement.setString(5,
                ByteIterator.ofBytes(digest.digest(issuer.getEncoded())).base64Encode().drainToString());
        statement.setString(6, ByteIterator.ofBytes(issuer.getEncoded()).base64Encode().drainToString());
        statement.execute();
    }

    public void createCertificate(int id, int issuerId, X509Certificate certificate) throws SQLException {
        long time = Instant.now().getEpochSecond();
        PreparedStatement statement = connection.prepareStatement(
                "INSERT INTO CERT (ID, IID, SN, LUPDATE, NBEFORE, NAFTER) VALUES (?, ?, ?, ?, ?, ?)");
        statement.setInt(1, id);
        statement.setInt(2, issuerId);
        statement.setString(3, certificate.getSerialNumber().toString(16));
        statement.setLong(4, time);
        statement.setLong(5, certificate.getNotBefore().toInstant().getEpochSecond());
        statement.setLong(6, certificate.getNotAfter().toInstant().getEpochSecond());
        statement.execute();
    }

    public void revokeCertificate(int id, int reason) throws SQLException {
        long time = Instant.now().getEpochSecond();
        PreparedStatement statement = connection
                .prepareStatement("UPDATE CERT SET REV = 1, RR = ?, RT = ?, RIT = ? WHERE ID = ?");
        statement.setInt(1, reason);
        statement.setLong(2, time);
        statement.setLong(3, time);
        statement.setInt(4, id);
        statement.execute();
    }

}