net.ripe.rpki.commons.provisioning.cms.ProvisioningCmsObjectBuilder.java Source code

Java tutorial

Introduction

Here is the source code for net.ripe.rpki.commons.provisioning.cms.ProvisioningCmsObjectBuilder.java

Source

/**
 * The BSD License
 *
 * Copyright (c) 2010-2012 RIPE NCC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   - Neither the name of the RIPE NCC nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package net.ripe.rpki.commons.provisioning.cms;

import net.ripe.rpki.commons.crypto.util.BouncyCastleUtil;
import net.ripe.rpki.commons.crypto.x509cert.X509CertificateBuilderHelper;
import net.ripe.rpki.commons.crypto.x509cert.X509CertificateUtil;
import net.ripe.rpki.commons.provisioning.payload.AbstractProvisioningPayload;
import net.ripe.rpki.commons.provisioning.payload.PayloadParser;
import net.ripe.rpki.commons.validation.ValidationCheck;
import net.ripe.rpki.commons.validation.ValidationLocation;
import net.ripe.rpki.commons.validation.ValidationResult;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.Time;
import org.bouncycastle.cert.jcajce.JcaCRLStore;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
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.joda.time.DateTimeUtils;

import java.io.IOException;
import java.nio.charset.Charset;
import java.security.PrivateKey;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.security.cert.X509Extension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;

public class ProvisioningCmsObjectBuilder {

    private static final ASN1ObjectIdentifier CONTENT_TYPE = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1.28");

    private X509Certificate cmsCertificate;

    private X509CRL crl;

    private String signatureProvider = X509CertificateBuilderHelper.DEFAULT_SIGNATURE_PROVIDER;

    private String payloadContent;

    public ProvisioningCmsObjectBuilder withCmsCertificate(X509Certificate cmsCertificate) {
        this.cmsCertificate = cmsCertificate;
        return this;
    }

    public ProvisioningCmsObjectBuilder withCrl(X509CRL crl) {
        this.crl = crl;
        return this;
    }

    public ProvisioningCmsObjectBuilder withSignatureProvider(String signatureProvider) {
        this.signatureProvider = signatureProvider;
        return this;
    }

    public ProvisioningCmsObjectBuilder withPayloadContent(AbstractProvisioningPayload payload) {
        this.payloadContent = PayloadParser.serialize(payload);
        return this;
    }

    public ProvisioningCmsObject build(PrivateKey privateKey) {
        Validate.notEmpty(payloadContent, "Payload content is required");

        Validate.notNull(cmsCertificate, "cms certificate is required");
        Validate.notNull(crl, "crl is required");

        ProvisioningCmsObjectParser parser = new ProvisioningCmsObjectParser();
        parser.parseCms("n/a", generateCms(privateKey));

        ValidationResult validationResult = parser.getValidationResult();
        if (validationResult.hasFailures()) {
            List<String> failureMessages = new ArrayList<String>();
            List<ValidationCheck> failures = validationResult.getFailures(new ValidationLocation("generated.cms"));
            for (ValidationCheck check : failures) {
                failureMessages.add(check.getKey());
            }
            Validate.isTrue(false, "Validation of generated CMS object failed with following errors: "
                    + StringUtils.join(failureMessages, ","));
        }

        return parser.getProvisioningCmsObject();
    }

    private byte[] generateCms(PrivateKey privateKey) {
        try {
            return doGenerate(privateKey);
        } catch (CMSException e) {
            throw new ProvisioningCmsObjectBuilderException(e);
        } catch (IOException e) {
            throw new ProvisioningCmsObjectBuilderException(e);
        } catch (OperatorCreationException e) {
            throw new ProvisioningCmsObjectBuilderException(e);
        } catch (CRLException e) {
            throw new ProvisioningCmsObjectBuilderException(e);
        } catch (CertificateEncodingException e) {
            throw new ProvisioningCmsObjectBuilderException(e);
        }
    }

    private byte[] doGenerate(PrivateKey privateKey) throws CMSException, IOException, CertificateEncodingException,
            CRLException, OperatorCreationException {
        CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
        addCertificateAndCrl(generator);
        addSignerInfo(generator, privateKey);

        CMSSignedData data = generator.generate(
                new CMSProcessableByteArray(CONTENT_TYPE, payloadContent.getBytes(Charset.forName("UTF-8"))), true);

        return data.getEncoded();
    }

    private void addSignerInfo(CMSSignedDataGenerator generator, PrivateKey privateKey)
            throws OperatorCreationException {
        ContentSigner signer = new JcaContentSignerBuilder(X509CertificateBuilderHelper.DEFAULT_SIGNATURE_ALGORITHM)
                .setProvider(signatureProvider).build(privateKey);
        DigestCalculatorProvider digestProvider = BouncyCastleUtil.DIGEST_CALCULATOR_PROVIDER;
        SignerInfoGenerator gen = new JcaSignerInfoGeneratorBuilder(digestProvider)
                .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(createSignedAttributes()))
                .build(signer, X509CertificateUtil.getSubjectKeyIdentifier(cmsCertificate));
        generator.addSignerInfoGenerator(gen);
    }

    private void addCertificateAndCrl(CMSSignedDataGenerator generator)
            throws CertificateEncodingException, CMSException, CRLException {
        List<X509Extension> certificates = new ArrayList<X509Extension>();
        certificates.add(cmsCertificate);

        generator.addCertificates(new JcaCertStore(certificates));
        generator.addCRLs(new JcaCRLStore(Collections.singleton(crl)));
    }

    private AttributeTable createSignedAttributes() {
        Hashtable<ASN1ObjectIdentifier, Attribute> attributes = new Hashtable<ASN1ObjectIdentifier, Attribute>(); // NOPMD
        // -
        // ReplaceHashtableWithMap
        Attribute signingTimeAttribute = new Attribute(CMSAttributes.signingTime,
                new DERSet(new Time(new Date(DateTimeUtils.currentTimeMillis()))));
        attributes.put(CMSAttributes.signingTime, signingTimeAttribute);
        return new AttributeTable(attributes);
    }

}