org.jscep.message.PkiMessageEncoder.java Source code

Java tutorial

Introduction

Here is the source code for org.jscep.message.PkiMessageEncoder.java

Source

/*
 * Copyright (c) 2010 ThruPoint Ltd
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.jscep.message;

import java.io.IOException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.LinkedList;

import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSAbsentContent;
import org.bouncycastle.cms.CMSAttributeTableGenerator;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.jscep.transaction.PkiStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is used to encode a <tt>pkiMessage</tt> into a PKCS #7 signedData
 * object.
 *
 * @see PkiMessageDecoder
 */
public final class PkiMessageEncoder {
    private static final String DATA = "1.2.840.113549.1.7.1";
    private static final Logger LOGGER = LoggerFactory.getLogger(PkiMessageEncoder.class);
    private final PrivateKey signerKey;
    private final X509Certificate signerId;
    private X509Certificate[] chain = null;
    private final PkcsPkiEnvelopeEncoder enveloper;
    private final String signatureAlgorithm;

    /**
     * Creates a new <tt>PkiMessageEncoder</tt> instance.
     *
     * @param signerKey
     *            the key to use to sign the <tt>signedData</tt>.
     * @param signerId
     *            the certificate to use to identify the signer.
     * @param enveloper
     *            the enveloper used for encoding the <tt>messageData</tt>
     */
    public PkiMessageEncoder(final PrivateKey signerKey, final X509Certificate signerId,
            final PkcsPkiEnvelopeEncoder enveloper) {
        this.signerKey = signerKey;
        this.signerId = signerId;
        this.enveloper = enveloper;
        this.signatureAlgorithm = "SHA1withRSA";
    }

    /**
     * Creates a new <tt>PkiMessageEncoder</tt> instance.
     *
     * @param signerKey
     *            the key to use to sign the <tt>signedData</tt>.
     * @param signerId
     *            the certificate to use to identify the signer.
     * @param chain
     *            the chain of ca certicate[s] to add to the signedData
     * @param enveloper
     *            the enveloper used for encoding the <tt>messageData</tt>
     */
    public PkiMessageEncoder(final PrivateKey signerKey, final X509Certificate signerId,
            final X509Certificate[] chain, final PkcsPkiEnvelopeEncoder enveloper) {
        this.signerKey = signerKey;
        this.signerId = signerId;
        this.chain = chain;
        this.enveloper = enveloper;
        this.signatureAlgorithm = "SHA1withRSA";
    }

    /**
     * Creates a new <tt>PkiMessageEncoder</tt> instance.
     *
     * @param signerKey
     *            the key to use to sign the <tt>signedData</tt>.
     * @param signerId
     *            the certificate to use to identify the signer.
     * @param enveloper
     *            the enveloper used for encoding the <tt>messageData</tt>
     * @param signatureAlgorithm
     *            the algorithm used for signing the <tt>messageData</tt>
     */
    public PkiMessageEncoder(final PrivateKey signerKey, final X509Certificate signerId,
            final PkcsPkiEnvelopeEncoder enveloper, final String signatureAlgorithm) {
        this.signerKey = signerKey;
        this.signerId = signerId;
        this.enveloper = enveloper;
        this.signatureAlgorithm = signatureAlgorithm;
    }

    /**
     * Encodes the provided <tt>PkiMessage</tt> into a PKCS #7
     * <tt>signedData</tt>.
     *
     * @param message
     *            the <tt>PkiMessage</tt> to encode.
     * @return the encoded <tt>signedData</tt>
     * @throws MessageEncodingException
     *             if there is a problem encoding the <tt>PkiMessage</tt>
     */
    public CMSSignedData encode(final PkiMessage<?> message) throws MessageEncodingException {
        LOGGER.debug("Encoding pkiMessage");
        LOGGER.debug("Encoding message: {}", message);

        CMSTypedData content = getContent(message);
        LOGGER.debug("Signing pkiMessage using key belonging to [dn={}; serial={}]", signerId.getSubjectDN(),
                signerId.getSerialNumber());
        try {
            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
            generator.addSignerInfoGenerator(getSignerInfo(message));
            generator.addCertificates(getCertificates());
            LOGGER.debug("Signing {} content", content);
            CMSSignedData pkiMessage = generator.generate(content, true);
            LOGGER.debug("Finished encoding pkiMessage");

            return pkiMessage;
        } catch (CMSException e) {
            throw new MessageEncodingException(e);
        } catch (Exception e) {
            throw new MessageEncodingException(e);
        }
    }

    private CMSTypedData getContent(final PkiMessage<?> message) throws MessageEncodingException {
        CMSTypedData signable;

        boolean hasMessageData = true;
        if (message instanceof CertRep) {
            CertRep response = (CertRep) message;
            if (response.getPkiStatus() != PkiStatus.SUCCESS) {
                hasMessageData = false;
            }
        }
        if (hasMessageData) {
            try {
                CMSEnvelopedData ed = encodeMessage(message);
                signable = new CMSProcessableByteArray(ed.getEncoded());
            } catch (IOException e) {
                throw new MessageEncodingException(e);
            }
        } else {
            signable = new CMSAbsentContent();
        }
        return signable;
    }

    private CMSEnvelopedData encodeMessage(final PkiMessage<?> message) throws MessageEncodingException {
        Object messageData = message.getMessageData();
        byte[] bytes;
        if (messageData instanceof byte[]) {
            bytes = (byte[]) messageData;
        } else if (messageData instanceof PKCS10CertificationRequest) {
            try {
                bytes = ((PKCS10CertificationRequest) messageData).getEncoded();
            } catch (IOException e) {
                throw new MessageEncodingException(e);
            }
        } else if (messageData instanceof CMSSignedData) {
            try {
                bytes = ((CMSSignedData) messageData).getEncoded();
            } catch (IOException e) {
                throw new MessageEncodingException(e);
            }
        } else {
            try {
                bytes = ((ASN1Object) messageData).getEncoded();
            } catch (IOException e) {
                throw new MessageEncodingException(e);
            }
        }
        return enveloper.encode(bytes);
    }

    private JcaCertStore getCertificates() throws MessageEncodingException {
        Collection<X509Certificate> certColl = new LinkedList<X509Certificate>();
        certColl.add(signerId);
        if (this.chain != null) {
            for (X509Certificate c : this.chain) {
                certColl.add(c);
                LOGGER.debug("Add ca certificate {} to signed data", c.getSubjectX500Principal().toString());
            }
        }
        JcaCertStore certStore;
        try {
            certStore = new JcaCertStore(certColl);
        } catch (CertificateEncodingException e) {
            throw new MessageEncodingException(e);
        }
        return certStore;
    }

    private SignerInfoGenerator getSignerInfo(final PkiMessage<?> message) throws MessageEncodingException {
        JcaSignerInfoGeneratorBuilder signerInfoBuilder = new JcaSignerInfoGeneratorBuilder(getDigestCalculator());
        signerInfoBuilder.setSignedAttributeGenerator(getTableGenerator(message));
        SignerInfoGenerator signerInfo;
        try {
            signerInfo = signerInfoBuilder.build(getContentSigner(), signerId);
        } catch (Exception e) {
            throw new MessageEncodingException(e);
        }
        return signerInfo;
    }

    private CMSAttributeTableGenerator getTableGenerator(final PkiMessage<?> message) {
        AttributeTableFactory attrFactory = new AttributeTableFactory();
        AttributeTable signedAttrs = attrFactory.fromPkiMessage(message);
        CMSAttributeTableGenerator atGen = new DefaultSignedAttributeTableGenerator(signedAttrs);
        return atGen;
    }

    private DigestCalculatorProvider getDigestCalculator() throws MessageEncodingException {
        try {
            return new JcaDigestCalculatorProviderBuilder().build();
        } catch (OperatorCreationException e) {
            throw new MessageEncodingException(e);
        }
    }

    private ContentSigner getContentSigner() throws OperatorCreationException {
        return new JcaContentSignerBuilder(signatureAlgorithm).build(signerKey);
    }
}