org.signserver.test.random.impl.Sign.java Source code

Java tutorial

Introduction

Here is the source code for org.signserver.test.random.impl.Sign.java

Source

/*************************************************************************
 *                                                                       *
 *  SignServer: The OpenSource Automated Signing Server                  *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.signserver.test.random.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.Key;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Random;
import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.cmp.PKIStatus;
import org.bouncycastle.tsp.*;
import org.signserver.common.*;
import org.signserver.ejb.interfaces.IWorkerSession;
import org.signserver.test.random.FailedException;
import org.signserver.test.random.Task;
import org.signserver.test.random.WorkerSpec;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Signs a sample document.
 *
 * @author Markus Kils
 * @version $Id: Sign.java 5550 2014-12-05 13:18:27Z netmackan $
 */
public class Sign implements Task {

    /** Logger for this class. */
    private static final Logger LOG = Logger.getLogger(Sign.class);

    private final WorkerSpec signer;
    private final IWorkerSession.IRemote workerSession;
    private final Random random;
    private int counter;
    private final RequestContextPreProcessor preProcessor;

    private static final String TESTXML1 = "<doc>Some sample XML to sign</doc>";

    public Sign(final WorkerSpec signerId, final IWorkerSession.IRemote workerSession, final Random random,
            final RequestContextPreProcessor preProcessor) {
        this.signer = signerId;
        this.workerSession = workerSession;
        this.random = random;
        this.preProcessor = preProcessor;
    }

    @Override
    public void run() throws FailedException {
        LOG.debug(">run");
        try {
            final int reqid = counter++;
            LOG.info("Worker " + signer + " signing: " + counter);
            process(signer, reqid);
        } catch (IllegalRequestException ex) {
            throw new FailedException("Illegal request", ex);
        } catch (CryptoTokenOfflineException ex) {
            throw new FailedException("Worker offline", ex);
        } catch (SignServerException ex) {
            throw new FailedException("Generic error: " + ex.getMessage(), ex);
        }
        LOG.debug("<run");
    }

    private void process(final WorkerSpec signer, final int reqid)
            throws FailedException, IllegalRequestException, CryptoTokenOfflineException, SignServerException {
        final ProcessResponse result;
        final RequestContext requestContext = new RequestContext();
        if (preProcessor != null) {
            preProcessor.preProcess(requestContext);
        }
        switch (signer.getWorkerType()) {
        case xml: {
            // Process
            final GenericSignRequest signRequest = new GenericSignRequest(reqid, TESTXML1.getBytes());
            final ProcessResponse response = workerSession.process(signer.getWorkerId(), signRequest,
                    requestContext);

            // Check result
            GenericSignResponse res = (GenericSignResponse) response;
            final byte[] data = res.getProcessedData();
            // Check that we got a signed XML back
            String xml = new String(data);
            if (!xml.contains("xmldsig")) {
                throw new FailedException("Response was not signed: \"" + xml + "\"");
            }
            validateXMLSignature(xml);
            break;
        }
        case tsa: {
            try {
                // Process
                final TimeStampRequestGenerator timeStampRequestGenerator = new TimeStampRequestGenerator();
                final int nonce = random.nextInt();
                final TimeStampRequest timeStampRequest = timeStampRequestGenerator.generate(TSPAlgorithms.SHA1,
                        new byte[20], BigInteger.valueOf(nonce));
                byte[] requestBytes = timeStampRequest.getEncoded();

                GenericSignRequest signRequest = new GenericSignRequest(reqid, requestBytes);
                final GenericSignResponse res = (GenericSignResponse) workerSession.process(signer.getWorkerId(),
                        signRequest, requestContext);

                // Check result
                if (reqid != res.getRequestID()) {
                    throw new FailedException("Expected request id: " + reqid + " but was " + res.getRequestID());
                }

                final Certificate signercert = res.getSignerCertificate();
                if (signercert == null) {
                    throw new FailedException("No certificate returned");
                }

                final TimeStampResponse timeStampResponse = new TimeStampResponse((byte[]) res.getProcessedData());
                timeStampResponse.validate(timeStampRequest);

                if (timeStampResponse.getStatus() != PKIStatus.GRANTED) {
                    throw new FailedException("Token was not granted: " + timeStampResponse.getStatus());
                }

                if (timeStampResponse.getTimeStampToken() == null) {
                    throw new FailedException("No token returned");
                }
                break;
            } catch (TSPException ex) {
                LOG.error("Verification error", ex);
                throw new FailedException("Response could not be verified: " + ex.getMessage());
            } catch (IOException ex) {
                LOG.error("Could not create request", ex);
                throw new FailedException("Could not create request: " + ex.getMessage());
            }
        }
        default:
            throw new IllegalRequestException("Unsupported workerType: " + signer.getWorkerType());
        }
    }

    /**
     * Validates the XML signature using a certificate in it.
     * Does not check certificate in any other way.
     * @param xml document to check
     * @throws FailedException in case validation failed
     * @throws SignServerException in case testing failed in other ways
     */
    private void validateXMLSignature(String xml) throws FailedException, SignServerException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);

        Document doc;
        try {
            dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
            dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

            doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
        } catch (ParserConfigurationException ex) {
            throw new FailedException("Document parsing error", ex);
        } catch (SAXException ex) {
            throw new FailedException("Document parsing error", ex);
        } catch (IOException ex) {
            throw new FailedException("Document parsing error", ex);
        }
        NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
        if (nl.getLength() == 0) {
            throw new FailedException("No Signature found");
        }

        String providerName = System.getProperty("jsr105Provider",
                "org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI");
        XMLSignatureFactory fac;
        try {
            fac = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
        } catch (InstantiationException e) {
            throw new SignServerException("Problem with JSR105 provider", e);
        } catch (IllegalAccessException e) {
            throw new SignServerException("Problem with JSR105 provider", e);
        } catch (ClassNotFoundException e) {
            throw new SignServerException("Problem with JSR105 provider", e);
        }

        DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(), nl.item(0));

        // enable secure validation
        valContext.setProperty("org.apache.jcp.xml.dsig.secureValidation", Boolean.TRUE);

        try {
            XMLSignature signature = fac.unmarshalXMLSignature(valContext);
            if (!signature.validate(valContext)) {
                throw new FailedException("Signature verification failed");
            }
        } catch (MarshalException ex) {
            throw new FailedException("XML signature validation error", ex);
        } catch (XMLSignatureException ex) {
            throw new FailedException("XML signature validation error", ex);
        }
    }

    /** Key selector just using the first certificate of right type. */
    class X509KeySelector extends KeySelector {

        @Override
        public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method,
                XMLCryptoContext context) throws KeySelectorException {
            Iterator ki = keyInfo.getContent().iterator();
            while (ki.hasNext()) {
                XMLStructure info = (XMLStructure) ki.next();
                if (!(info instanceof X509Data)) {
                    continue;
                }
                X509Data x509Data = (X509Data) info;
                Iterator xi = x509Data.getContent().iterator();
                while (xi.hasNext()) {
                    Object o = xi.next();
                    if (!(o instanceof X509Certificate)) {
                        continue;
                    }
                    final PublicKey key = ((X509Certificate) o).getPublicKey();
                    if (algEquals(method.getAlgorithm(), key.getAlgorithm())) {
                        return new KeySelectorResult() {
                            public Key getKey() {
                                return key;
                            }
                        };
                    }
                }
            }
            throw new KeySelectorException("No key found!");
        }

        private boolean algEquals(String algURI, String algName) {
            if ((algName.equalsIgnoreCase("DSA") && algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1))
                    || (algName.equalsIgnoreCase("RSA") && algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1))) {
                return true;
            } else {
                return false;
            }
        }
    }

}