be.e_contract.eid.applet.service.impl.handler.SignatureDataMessageHandler.java Source code

Java tutorial

Introduction

Here is the source code for be.e_contract.eid.applet.service.impl.handler.SignatureDataMessageHandler.java

Source

/*
 * eID Applet Project.
 * Copyright (C) 2015 e-Contract.be BVBA.
 *
 * 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 be.e_contract.eid.applet.service.impl.handler;

import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.util.List;
import java.util.Map;

import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import be.e_contract.eid.applet.service.impl.BeIDContextQualifier;
import be.e_contract.eid.applet.service.impl.Handles;
import be.fedict.eid.applet.service.cdi.SecurityAuditEvent;
import be.fedict.eid.applet.service.cdi.SecurityAuditEvent.Incident;
import be.fedict.eid.applet.service.cdi.SignatureEvent;
import be.fedict.eid.applet.service.impl.handler.MessageHandler;
import be.fedict.eid.applet.service.spi.CertificateSecurityException;
import be.fedict.eid.applet.service.spi.ExpiredCertificateSecurityException;
import be.fedict.eid.applet.service.spi.RevokedCertificateSecurityException;
import be.fedict.eid.applet.service.spi.TrustCertificateSecurityException;
import be.fedict.eid.applet.shared.ErrorCode;
import be.fedict.eid.applet.shared.FinishedMessage;
import be.fedict.eid.applet.shared.SignatureDataMessage;

@Handles(SignatureDataMessage.class)
public class SignatureDataMessageHandler implements MessageHandler<SignatureDataMessage>, Serializable {

    private static final long serialVersionUID = 1L;

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

    public static final byte[] SHA1_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
            0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };

    public static final byte[] SHA224_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60,
            (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c };

    public static final byte[] SHA256_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
            (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };

    public static final byte[] SHA384_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60,
            (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };

    public static final byte[] SHA512_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60,
            (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };

    public static final byte[] RIPEMD160_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
            0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };

    public static final byte[] RIPEMD128_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x1d, 0x30, 0x09, 0x06, 0x05, 0x2b,
            0x24, 0x03, 0x02, 0x02, 0x05, 0x00, 0x04, 0x10 };

    public static final byte[] RIPEMD256_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x2d, 0x30, 0x09, 0x06, 0x05, 0x2b,
            0x24, 0x03, 0x02, 0x03, 0x05, 0x00, 0x04, 0x20 };

    @Inject
    private SignatureState signatureState;

    @Inject
    private Event<SignatureEvent> signatureEvent;

    @Inject
    private Event<SecurityAuditEvent> securityAuditEvent;

    @Override
    public Object handleMessage(SignatureDataMessage message, Map<String, String> httpHeaders,
            HttpServletRequest request, HttpSession session) throws ServletException {
        byte[] signatureValue = message.signatureValue;
        List<X509Certificate> certificateChain = message.certificateChain;
        if (certificateChain.isEmpty()) {
            throw new ServletException("certificate chain is empty");
        }
        X509Certificate signingCertificate = certificateChain.get(0);
        if (null == signingCertificate) {
            throw new ServletException("non-repudiation certificate missing");
        }
        LOG.debug("non-repudiation signing certificate: " + signingCertificate.getSubjectX500Principal());
        PublicKey signingPublicKey = signingCertificate.getPublicKey();

        BeIDContextQualifier contextQualifier = new BeIDContextQualifier(request);

        /*
         * Verify the signature.
         */
        String digestAlgo = this.signatureState.getDigestAlgo();
        byte[] expectedDigestValue = this.signatureState.getDigestValue();
        if (digestAlgo.endsWith("-PSS")) {
            LOG.debug("verifying RSA/PSS signature");
            try {
                Signature signature = Signature.getInstance("RAWRSASSA-PSS", BouncyCastleProvider.PROVIDER_NAME);
                if ("SHA-256-PSS".equals(digestAlgo)) {
                    LOG.debug("RSA/PSS SHA256");
                    signature.setParameter(
                            new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 32, 1));
                }
                signature.initVerify(signingPublicKey);
                signature.update(expectedDigestValue);
                boolean result = signature.verify(signatureValue);
                if (false == result) {
                    SecurityAuditEvent securityAuditEvent = new SecurityAuditEvent(Incident.SIGNATURE,
                            signingCertificate, signatureValue);
                    this.securityAuditEvent.select(contextQualifier).fire(securityAuditEvent);
                    throw new SecurityException("signature incorrect");
                }
            } catch (Exception e) {
                LOG.debug("signature verification error: " + e.getMessage(), e);
                SecurityAuditEvent securityAuditEvent = new SecurityAuditEvent(Incident.SIGNATURE,
                        signingCertificate, signatureValue);
                this.securityAuditEvent.select(contextQualifier).fire(securityAuditEvent);
                throw new ServletException("signature verification error: " + e.getMessage(), e);
            }
        } else {
            try {
                Signature signature = Signature.getInstance("RawRSA", BouncyCastleProvider.PROVIDER_NAME);
                signature.initVerify(signingPublicKey);
                ByteArrayOutputStream digestInfo = new ByteArrayOutputStream();
                if ("SHA-1".equals(digestAlgo) || "SHA1".equals(digestAlgo)) {
                    digestInfo.write(SHA1_DIGEST_INFO_PREFIX);
                } else if ("SHA-224".equals(digestAlgo)) {
                    digestInfo.write(SHA224_DIGEST_INFO_PREFIX);
                } else if ("SHA-256".equals(digestAlgo)) {
                    digestInfo.write(SHA256_DIGEST_INFO_PREFIX);
                } else if ("SHA-384".equals(digestAlgo)) {
                    digestInfo.write(SHA384_DIGEST_INFO_PREFIX);
                } else if ("SHA-512".equals(digestAlgo)) {
                    digestInfo.write(SHA512_DIGEST_INFO_PREFIX);
                } else if ("RIPEMD160".equals(digestAlgo)) {
                    digestInfo.write(RIPEMD160_DIGEST_INFO_PREFIX);
                } else if ("RIPEMD128".equals(digestAlgo)) {
                    digestInfo.write(RIPEMD128_DIGEST_INFO_PREFIX);
                } else if ("RIPEMD256".equals(digestAlgo)) {
                    digestInfo.write(RIPEMD256_DIGEST_INFO_PREFIX);
                }
                digestInfo.write(expectedDigestValue);
                signature.update(digestInfo.toByteArray());
                boolean result = signature.verify(signatureValue);
                if (false == result) {
                    SecurityAuditEvent securityAuditEvent = new SecurityAuditEvent(Incident.SIGNATURE,
                            signingCertificate, signatureValue);
                    this.securityAuditEvent.select(contextQualifier).fire(securityAuditEvent);
                    throw new SecurityException("signature incorrect");
                }
            } catch (Exception e) {
                LOG.debug("signature verification error: " + e.getMessage());
                SecurityAuditEvent securityAuditEvent = new SecurityAuditEvent(Incident.SIGNATURE,
                        signingCertificate, signatureValue);
                this.securityAuditEvent.select(contextQualifier).fire(securityAuditEvent);
                throw new ServletException("signature verification error: " + e.getMessage(), e);
            }
        }

        SignatureEvent signatureEvent = new SignatureEvent(signatureValue, certificateChain);
        try {
            this.signatureEvent.select(contextQualifier).fire(signatureEvent);
        } catch (ExpiredCertificateSecurityException e) {
            return new FinishedMessage(ErrorCode.CERTIFICATE_EXPIRED);
        } catch (RevokedCertificateSecurityException e) {
            return new FinishedMessage(ErrorCode.CERTIFICATE_REVOKED);
        } catch (TrustCertificateSecurityException e) {
            return new FinishedMessage(ErrorCode.CERTIFICATE_NOT_TRUSTED);
        } catch (CertificateSecurityException e) {
            return new FinishedMessage(ErrorCode.CERTIFICATE);
        }

        if (null != signatureEvent.getError()) {
            SecurityAuditEvent securityAuditEvent = new SecurityAuditEvent(Incident.TRUST, signingCertificate);
            this.securityAuditEvent.select(contextQualifier).fire(securityAuditEvent);
            return new FinishedMessage(signatureEvent.getError());
        }
        return new FinishedMessage();
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
    }
}