org.xipki.pki.scep.message.PkiMessage.java Source code

Java tutorial

Introduction

Here is the source code for org.xipki.pki.scep.message.PkiMessage.java

Source

/*
 *
 * Copyright (c) 2013 - 2016 Lijun Liao
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 *
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
 * OF THIRD PARTY RIGHTS.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the XiPKI software without
 * disclosing the source code of your own applications.
 *
 * For more information, please contact Lijun Liao at this
 * address: lijun.liao@gmail.com
 */

package org.xipki.pki.scep.message;

import java.io.IOException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERPrintableString;
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.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.cms.CMSAbsentContent;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSException;
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.RecipientInfoGenerator;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.SimpleAttributeTableGenerator;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.pki.scep.exception.MessageEncodingException;
import org.xipki.pki.scep.transaction.FailInfo;
import org.xipki.pki.scep.transaction.MessageType;
import org.xipki.pki.scep.transaction.Nonce;
import org.xipki.pki.scep.transaction.PkiStatus;
import org.xipki.pki.scep.transaction.TransactionId;
import org.xipki.pki.scep.util.ScepUtil;

/**
 * @author Lijun Liao
 * @since 2.0.0
 */

public class PkiMessage {

    private static final Set<ASN1ObjectIdentifier> SCEP_ATTR_TYPES = new HashSet<ASN1ObjectIdentifier>();

    private final MessageType messageType;

    private final Nonce senderNonce;

    private final TransactionId transactionId;

    private Nonce recipientNonce;

    private PkiStatus pkiStatus;

    private FailInfo failInfo;

    private ASN1Encodable messageData;

    private final Map<ASN1ObjectIdentifier, ASN1Encodable> signedAttributes = new HashMap<ASN1ObjectIdentifier, ASN1Encodable>();

    private final Map<ASN1ObjectIdentifier, ASN1Encodable> unsignedAttributes = new HashMap<ASN1ObjectIdentifier, ASN1Encodable>();

    static {
        SCEP_ATTR_TYPES.add(ScepObjectIdentifiers.ID_FAILINFO);
        SCEP_ATTR_TYPES.add(ScepObjectIdentifiers.ID_MESSAGE_TYPE);
        SCEP_ATTR_TYPES.add(ScepObjectIdentifiers.ID_PKI_STATUS);
        SCEP_ATTR_TYPES.add(ScepObjectIdentifiers.ID_RECIPIENT_NONCE);
        SCEP_ATTR_TYPES.add(ScepObjectIdentifiers.ID_SENDER_NONCE);
        SCEP_ATTR_TYPES.add(ScepObjectIdentifiers.ID_TRANSACTION_ID);
        SCEP_ATTR_TYPES.add(CMSAttributes.signingTime);
    }

    public PkiMessage(final TransactionId transactionId, final MessageType messageType) {
        this(transactionId, messageType, Nonce.randomNonce());
    }

    public PkiMessage(final TransactionId transactionId, final MessageType messageType, final Nonce senderNonce) {
        this.transactionId = ParamUtil.requireNonNull("transactionId", transactionId);
        this.messageType = ParamUtil.requireNonNull("messageType", messageType);
        this.senderNonce = ParamUtil.requireNonNull("senderNonce", senderNonce);
    }

    public TransactionId getTransactionId() {
        return transactionId;
    }

    public Nonce getSenderNonce() {
        return senderNonce;
    }

    public final MessageType getMessageType() {
        return messageType;
    }

    public Nonce getRecipientNonce() {
        return recipientNonce;
    }

    public void setRecipientNonce(final Nonce recipientNonce) {
        this.recipientNonce = recipientNonce;
    }

    public PkiStatus getPkiStatus() {
        return pkiStatus;
    }

    public void setPkiStatus(final PkiStatus pkiStatus) {
        this.pkiStatus = pkiStatus;
    }

    public FailInfo getFailInfo() {
        return failInfo;
    }

    public void setFailInfo(final FailInfo failInfo) {
        this.failInfo = failInfo;
    }

    public ASN1Encodable getMessageData() {
        return messageData;
    }

    public void setMessageData(final ASN1Encodable messageData) {
        this.messageData = messageData;
    }

    public ASN1Encodable addSignendAttribute(final ASN1ObjectIdentifier type, final ASN1Encodable value) {
        if (SCEP_ATTR_TYPES.contains(type)) {
            throw new IllegalArgumentException(
                    "Adding SCEP attribute via addSignedAttribute() method is not permitted");
        }
        return signedAttributes.put(type, value);
    }

    public ASN1Encodable addUnsignendAttribute(final ASN1ObjectIdentifier type, final ASN1Encodable value) {
        return unsignedAttributes.put(type, value);
    }

    public ASN1Encodable removeSignedAttribute(final ASN1ObjectIdentifier type) {
        return signedAttributes.remove(type);
    }

    public ASN1Encodable removeUnsignedAttribute(final ASN1ObjectIdentifier type) {
        return unsignedAttributes.remove(type);
    }

    public ASN1Encodable getSignedAtrributeValue(final ASN1ObjectIdentifier type) {
        return signedAttributes.get(type);
    }

    public ASN1Encodable getUnsignedAtrributeValue(final ASN1ObjectIdentifier type) {
        return unsignedAttributes.get(type);
    }

    private AttributeTable getSignedAttributes() {
        ASN1EncodableVector vec = new ASN1EncodableVector();
        // messageType
        addAttribute(vec, ScepObjectIdentifiers.ID_MESSAGE_TYPE,
                new DERPrintableString(Integer.toString(messageType.getCode())));

        // senderNonce
        addAttribute(vec, ScepObjectIdentifiers.ID_SENDER_NONCE, new DEROctetString(senderNonce.getBytes()));

        // transactionID
        addAttribute(vec, ScepObjectIdentifiers.ID_TRANSACTION_ID, new DERPrintableString(transactionId.getId()));

        // failInfo
        if (failInfo != null) {
            addAttribute(vec, ScepObjectIdentifiers.ID_FAILINFO,
                    new DERPrintableString(Integer.toString(failInfo.getCode())));
        }

        // pkiStatus
        if (pkiStatus != null) {
            addAttribute(vec, ScepObjectIdentifiers.ID_PKI_STATUS,
                    new DERPrintableString(Integer.toString(pkiStatus.getCode())));
        }

        // recipientNonce
        if (recipientNonce != null) {
            addAttribute(vec, ScepObjectIdentifiers.ID_RECIPIENT_NONCE,
                    new DEROctetString(recipientNonce.getBytes()));
        }

        for (ASN1ObjectIdentifier type : signedAttributes.keySet()) {
            addAttribute(vec, type, signedAttributes.get(type));
        }
        return new AttributeTable(vec);
    }

    private AttributeTable getUnsignedAttributes() {
        if (unsignedAttributes.isEmpty()) {
            return null;
        }
        ASN1EncodableVector vec = new ASN1EncodableVector();

        for (ASN1ObjectIdentifier type : unsignedAttributes.keySet()) {
            addAttribute(vec, type, unsignedAttributes.get(type));
        }
        return new AttributeTable(vec);
    }

    public ContentInfo encode(final PrivateKey signerKey, final String signatureAlgorithm,
            final X509Certificate signerCert, final X509Certificate[] signerCertSet,
            final X509Certificate recipientCert, final ASN1ObjectIdentifier encAlgId)
            throws MessageEncodingException {
        ParamUtil.requireNonNull("signerKey", signerKey);
        ContentSigner signer;
        try {
            signer = new JcaContentSignerBuilder(signatureAlgorithm).build(signerKey);
        } catch (OperatorCreationException ex) {
            throw new MessageEncodingException(ex);
        }
        return encode(signer, signerCert, signerCertSet, recipientCert, encAlgId);
    }

    public ContentInfo encode(final ContentSigner signer, final X509Certificate signerCert,
            final X509Certificate[] cmsCertSet, final X509Certificate recipientCert,
            final ASN1ObjectIdentifier encAlgId) throws MessageEncodingException {
        ParamUtil.requireNonNull("signer", signer);
        ParamUtil.requireNonNull("signerCert", signerCert);
        ParamUtil.requireNonNull("recipientCert", recipientCert);
        ParamUtil.requireNonNull("encAlgId", encAlgId);

        CMSTypedData content;
        if (messageData == null) {
            content = new CMSAbsentContent();
        } else {
            CMSEnvelopedData envelopedData = encrypt(recipientCert, encAlgId);
            byte[] encoded;
            try {
                encoded = envelopedData.getEncoded();
            } catch (IOException ex) {
                throw new MessageEncodingException(ex);
            }
            content = new CMSProcessableByteArray(CMSObjectIdentifiers.envelopedData, encoded);
        }

        try {
            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();

            // signerInfo
            JcaSignerInfoGeneratorBuilder signerInfoBuilder = new JcaSignerInfoGeneratorBuilder(
                    new BcDigestCalculatorProvider());

            signerInfoBuilder
                    .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(getSignedAttributes()));

            AttributeTable attrTable = getUnsignedAttributes();
            if (attrTable != null) {
                signerInfoBuilder.setUnsignedAttributeGenerator(new SimpleAttributeTableGenerator(attrTable));
            }

            // certificateSet
            ScepUtil.addCmsCertSet(generator, cmsCertSet);

            SignerInfoGenerator signerInfo;
            try {
                signerInfo = signerInfoBuilder.build(signer, signerCert);
            } catch (Exception ex) {
                throw new MessageEncodingException(ex);
            }

            generator.addSignerInfoGenerator(signerInfo);

            CMSSignedData signedData = generator.generate(content, true);
            return signedData.toASN1Structure();
        } catch (CMSException ex) {
            throw new MessageEncodingException(ex);
        } catch (Exception ex) {
            throw new MessageEncodingException(ex);
        }
    } // method encode

    private CMSEnvelopedData encrypt(final X509Certificate recipient, final ASN1ObjectIdentifier encAlgId)
            throws MessageEncodingException {
        ParamUtil.requireNonNull("recipient", recipient);
        ParamUtil.requireNonNull("encAlgId", encAlgId);

        byte[] messageDataBytes;
        try {
            messageDataBytes = messageData.toASN1Primitive().getEncoded();
        } catch (IOException ex) {
            throw new MessageEncodingException(ex);
        }

        CMSEnvelopedDataGenerator edGenerator = new CMSEnvelopedDataGenerator();
        CMSTypedData envelopable = new CMSProcessableByteArray(messageDataBytes);
        RecipientInfoGenerator recipientGenerator;
        try {
            recipientGenerator = new JceKeyTransRecipientInfoGenerator(recipient);
        } catch (CertificateEncodingException ex) {
            throw new MessageEncodingException(ex);
        }
        edGenerator.addRecipientInfoGenerator(recipientGenerator);
        try {

            OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(encAlgId).build();

            CMSEnvelopedData pkcsPkiEnvelope = edGenerator.generate(envelopable, encryptor);
            return pkcsPkiEnvelope;
        } catch (CMSException ex) {
            throw new MessageEncodingException(ex);
        }
    }

    private static void addAttribute(final ASN1EncodableVector vector, final ASN1ObjectIdentifier attrType,
            final ASN1Encodable attrValue) {
        vector.add(new Attribute(attrType, new DERSet(attrValue)));
    }

}