Controllers.OCSPController.java Source code

Java tutorial

Introduction

Here is the source code for Controllers.OCSPController.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 Controllers;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.ocsp.*;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * Created by evilisn(kasimok@163.com)) on 2016/6/6.
 */
@Controller
@ComponentScan({ "CA" })
public class OCSPController {

    private static Logger LOG = LoggerFactory.getLogger(OCSPController.class);

    private boolean bRequireNonce = true;

    private enum OCSP_PROCESS_MODE {
        AUTO(0, "AUTO"), GOOD(1, "GOOD"), REVOKED(2, "REVOKED"), UNKNOWN(3, "UNKNOWN");

        private final int MODE;
        private final String DISP;

        OCSP_PROCESS_MODE(int mode, String disp) {
            this.MODE = mode;
            DISP = disp;
        }

        public static OCSP_PROCESS_MODE getOCSP_PROCESS_MODE(int ocsp_process_mode) {
            for (OCSP_PROCESS_MODE mode : OCSP_PROCESS_MODE.values()) {
                if (mode.MODE == ocsp_process_mode)
                    return mode;
            }
            throw new IllegalArgumentException("Mode not supported.");
        }

        @Override
        public String toString() {
            return "OCSP_PROCESS_MODE{" + "MODE=" + MODE + ", DISP='" + DISP + '\'' + '}';
        }
    }

    public static void setOcsp_process_mode(OCSP_PROCESS_MODE iocsp_process_mode) {
        ocsp_process_mode = iocsp_process_mode;
    }

    private static OCSP_PROCESS_MODE ocsp_process_mode = OCSP_PROCESS_MODE.GOOD;

    /**
     * OCSP Signer's cert.
     */
    private static X509CertificateHolder x509CertificateHolder;

    /**
     * OCSP Signer's private keys.
     */

    private static PrivateKey privateKey;

    @Autowired
    public void setX509CertificateHolder(X509CertificateHolder x509CertificateHolder) {
        this.x509CertificateHolder = x509CertificateHolder;
    }

    @Autowired
    public void setPrivateKey(PrivateKey privateKey) {
        this.privateKey = privateKey;
    }

    /**
     * Method to deal with a ocsp query request. The OCSP request method can be GET or POST.
     *
     * @return a valid OCSP response.
     */
    @RequestMapping(value = "/verify-mocked-auto", method = { RequestMethod.POST,
            RequestMethod.GET }, produces = "application/ocsp-response")
    @ResponseBody
    public byte[] doOCSPAuto(HttpServletRequest httpServletRequest) throws IOException {
        InputStream inputStream = httpServletRequest.getInputStream();
        byte[] requestBytes = new byte[10000];
        int requestSize = inputStream.read(requestBytes);
        LOG.info("Received OCSP request, size: " + requestSize);
        byte[] responseBytes = processOcspRequest(requestBytes, OCSP_PROCESS_MODE.AUTO);
        return responseBytes;
    }

    /**
     * Mocked method to deal with a ocsp query request. The OCSP request method can be GET or POST.
     *
     * @return a valid OCSP response to tell the client OCSP is <b>Good</b>!
     */
    @RequestMapping(value = "/verify-mocked-good", method = { RequestMethod.POST,
            RequestMethod.GET }, produces = "application/ocsp-response")
    @ResponseBody
    public byte[] doOCSPGood(HttpServletRequest httpServletRequest) throws IOException {
        InputStream inputStream = httpServletRequest.getInputStream();
        byte[] requestBytes = new byte[10000];
        int requestSize = inputStream.read(requestBytes);
        LOG.info("Received OCSP request, size: " + requestSize);
        byte[] responseBytes = processOcspRequest(requestBytes, OCSP_PROCESS_MODE.GOOD);
        return responseBytes;
    }

    /**
     * Mocked method to deal with a ocsp query request. The OCSP request method can be GET or POST.
     *
     * @return a valid OCSP response to tell the client OCSP is <b>Revoked</b>!
     */
    @RequestMapping(value = "/verify-mocked-revoked", method = { RequestMethod.POST,
            RequestMethod.GET }, produces = "application/ocsp-response")
    @ResponseBody
    public byte[] doOCSPRevoked(HttpServletRequest httpServletRequest) throws IOException {
        InputStream inputStream = httpServletRequest.getInputStream();
        byte[] requestBytes = new byte[10000];
        int requestSize = inputStream.read(requestBytes);
        LOG.info("Received OCSP request, size: " + requestSize);
        byte[] responseBytes = processOcspRequest(requestBytes, OCSP_PROCESS_MODE.REVOKED);
        return responseBytes;
    }

    /**
     * Mocked method to deal with a ocsp query request. The OCSP request method can be GET or POST.
     *
     * @return a valid OCSP response to tell the client OCSP is <b>Unknown</b>!
     */
    @RequestMapping(value = "/verify-mocked-unknown", method = { RequestMethod.POST,
            RequestMethod.GET }, produces = "application/ocsp-response")
    @ResponseBody
    public byte[] doOCSPUnknown(HttpServletRequest httpServletRequest) throws IOException {
        InputStream inputStream = httpServletRequest.getInputStream();
        byte[] requestBytes = new byte[10000];
        int requestSize = inputStream.read(requestBytes);
        LOG.info("Received OCSP request, size: " + requestSize);
        byte[] responseBytes = processOcspRequest(requestBytes, OCSP_PROCESS_MODE.UNKNOWN);
        return responseBytes;
    }

    /**
     * Manually set the OCSP response mode for testing purpose.
     *
     * @return
     */
    @RequestMapping(value = "/set-response-mode", method = { RequestMethod.GET })
    @ResponseBody
    public String doSetMode(@RequestParam(value = "mode", defaultValue = "0") String mode) {
        try {
            final int iOcspMode = Integer.valueOf(mode);
            OCSP_PROCESS_MODE MODE = OCSP_PROCESS_MODE.getOCSP_PROCESS_MODE(iOcspMode);
            setOcsp_process_mode(MODE);
            String string = String.format("Server mode has been changed to [%s].", MODE);
            LOG.warn(string);
            return string;
        } catch (IllegalArgumentException e) {
            LOG.error(String.format("Illegal mode:[%s]", mode));
            return "Illegal mode.";
        }
    }

    /**
     * Forwarding request to certain according to current set mode.
     *
     * @return
     */
    @RequestMapping({ "/" })
    public String execute() {
        LOG.info(String.format("System ocsp mode:[%s]", ocsp_process_mode));
        switch (ocsp_process_mode) {
        case GOOD:
            return "forward:/verify-mocked-good";
        case REVOKED:
            return "forward:/verify-mocked-revoked";
        case UNKNOWN:
            return "forward:/verify-mocked-unknown";
        case AUTO:
            return "forward:/verify-mocked-auto";
        default:
            return "forward:/verify-mocked-good";
        }
    }

    /**
     * Method to do OCSP response to client.
     *
     * @param requestBytes
     * @param mode
     *
     * @return
     *
     * @throws NotImplementedException
     */
    private byte[] processOcspRequest(byte[] requestBytes, OCSP_PROCESS_MODE mode) throws NotImplementedException {
        try {
            // get request info
            OCSPReq ocspRequest = new OCSPReq(requestBytes);
            X509CertificateHolder[] requestCerts = ocspRequest.getCerts();
            Req[] requestList = ocspRequest.getRequestList();
            // setup response
            BasicOCSPRespBuilder responseBuilder = new BasicOCSPRespBuilder(
                    new RespID(x509CertificateHolder.getSubject()));
            LOG.info("OCSP request version: " + ocspRequest.getVersionNumber() + ", Requester name: "
                    + ocspRequest.getRequestorName() + ", is signed: " + ocspRequest.isSigned()
                    + ", has extensions: " + ocspRequest.hasExtensions() + ", number of additional certificates: "
                    + requestCerts.length + ", number of certificate ids to verify: " + requestList.length);
            int ocspResult = OCSPRespBuilder.SUCCESSFUL;
            switch (mode) {
            case AUTO:
                LOG.error("Auto OCSP server is not implemented in this version.");
                throw new NotImplementedException();
            case GOOD:
                LOG.warn("Mocked mode, server will always return Good ocsp response");
                for (Req req : requestList) {
                    CertificateID certId = req.getCertID();
                    String serialNumber = "0x" + certId.getSerialNumber().toString(16);
                    LOG.debug(String.format("Processing request for cert serial number:[%s]", serialNumber));
                    CertificateStatus certificateStatus = CertificateStatus.GOOD;
                    Calendar thisUpdate = new GregorianCalendar();
                    Date now = thisUpdate.getTime();
                    thisUpdate.add(Calendar.DAY_OF_MONTH, 7);
                    Date nexUpdate = thisUpdate.getTime();
                    responseBuilder.addResponse(certId, certificateStatus, now, nexUpdate, null);
                }
                break;
            case REVOKED:
                LOG.warn("Mocked mode, server will always return REVOKED ocsp response");
                for (Req req : requestList) {
                    CertificateID certId = req.getCertID();
                    String serialNumber = "0x" + certId.getSerialNumber().toString(16);
                    LOG.debug(String.format("Processing request for cert serial number:[%s]", serialNumber));
                    Calendar cal = new GregorianCalendar();
                    cal.add(Calendar.DAY_OF_MONTH, -7);//Set revoked 7 days ago.
                    CertificateStatus certificateStatus = new RevokedStatus(cal.getTime(), 16);
                    Calendar thisUpdate = new GregorianCalendar();
                    Date now = thisUpdate.getTime();
                    thisUpdate.add(Calendar.DAY_OF_MONTH, 7);
                    Date nexUpdate = thisUpdate.getTime();
                    responseBuilder.addResponse(certId, certificateStatus, now, nexUpdate, null);
                }
                break;
            case UNKNOWN:
                LOG.warn("Mocked mode, server will always return Known ocsp response");
                for (Req req : requestList) {
                    CertificateID certId = req.getCertID();
                    String serialNumber = "0x" + certId.getSerialNumber().toString(16);
                    LOG.debug(String.format("Processing request for cert serial number:[%s]", serialNumber));
                    CertificateStatus certificateStatus = new UnknownStatus();
                    Calendar thisUpdate = new GregorianCalendar();
                    Date now = thisUpdate.getTime();
                    thisUpdate.add(Calendar.DAY_OF_MONTH, 7);
                    Date nexUpdate = thisUpdate.getTime();
                    responseBuilder.addResponse(certId, certificateStatus, now, nexUpdate, null);
                }
                break;
            }
            // process nonce
            Extension extNonce = ocspRequest.getExtension(new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.2"));
            if (extNonce != null) {
                LOG.debug("Nonce is present in the request");
                responseBuilder.setResponseExtensions(new Extensions(extNonce));
            } else {
                LOG.info("Nonce is not present in the request");
                if (bRequireNonce) {
                    LOG.info("Nonce is required, fail the request");
                    ocspResult = OCSPRespBuilder.UNAUTHORIZED;
                }
            }
            X509CertificateHolder[] chain = { x509CertificateHolder };
            ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privateKey);
            BasicOCSPResp ocspResponse = responseBuilder.build(signer, chain, Calendar.getInstance().getTime());
            OCSPRespBuilder ocspResponseBuilder = new OCSPRespBuilder();
            byte[] encoded = ocspResponseBuilder.build(ocspResult, ocspResponse).getEncoded();
            LOG.info("Sending OCSP response to client, size: " + encoded.length);
            return encoded;

        } catch (Exception e) {
            LOG.error("Exception during processing OCSP request: " + e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

}