All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jscep.message.PkiMessageDecoder Maven / Gradle / Ivy

There is a newer version: 2.5.6
Show newest version
package org.jscep.message;

import static org.jscep.asn1.ScepObjectIdentifier.*;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map.Entry;

import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSignerId;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.StoreException;
import org.jscep.asn1.IssuerAndSubject;
import org.jscep.asn1.ScepObjectIdentifier;
import org.jscep.transaction.FailInfo;
import org.jscep.transaction.MessageType;
import org.jscep.transaction.Nonce;
import org.jscep.transaction.PkiStatus;
import org.jscep.transaction.TransactionId;
import org.slf4j.Logger;

/**
 * This class is used to decode a PKCS #7 signedData object into a
 * pkiMessage.
 *
 * @see PkiMessageEncoder
 */
public final class PkiMessageDecoder {
    private static final Logger LOGGER = getLogger(PkiMessageDecoder.class);
    private final PkcsPkiEnvelopeDecoder decoder;
    private final X509Certificate signer;

    /**
     * Creates a new PkiMessageDecoder.
     *
     * @param signer
     *            the certificate used for verifying the signedData
     *            signature.
     * @param decoder
     *            the decoder used for extracting the pkiMessage.
     */
    public PkiMessageDecoder(final X509Certificate signer,
            final PkcsPkiEnvelopeDecoder decoder) {
        this.decoder = decoder;
        this.signer = signer;
    }

    /**
     * Decodes the provided PKCS #7 signedData into a
     * PkiMessage
     *
     * @param pkiMessage
     *            the signedData to decode.
     * @return the decoded PkiMessage
     * @throws MessageDecodingException
     *             if there is a problem decoding the signedData
     */
    @SuppressWarnings("unchecked")
    public PkiMessage decode(final CMSSignedData pkiMessage)
            throws MessageDecodingException {
        LOGGER.debug("Decoding pkiMessage");
        validate(pkiMessage);

        // The signed content is always an octet string
        CMSProcessable signedContent = pkiMessage.getSignedContent();

        SignerInformationStore signerStore = pkiMessage.getSignerInfos();
        SignerInformation signerInfo = signerStore.get(new JcaSignerId(signer));
        if (signerInfo == null) {
            throw new MessageDecodingException("Could not for signerInfo for "
                    + signer.getSubjectDN());
        }

        LOGGER.debug("pkiMessage digest algorithm: {}", signerInfo
                .getDigestAlgorithmID().getAlgorithm());
        LOGGER.debug("pkiMessage encryption algorithm: {}",
                signerInfo.getEncryptionAlgOID());

        Store store = pkiMessage.getCertificates();
        Collection certColl;
        try {
            certColl = store.getMatches(signerInfo.getSID());
        } catch (StoreException e) {
            throw new MessageDecodingException(e);
        }
        if (certColl.size() > 0) {
            X509CertificateHolder cert = (X509CertificateHolder) certColl
                    .iterator().next();
            LOGGER.debug(
                    "Verifying pkiMessage using key belonging to [dn={}; serial={}]",
                    cert.getSubject(), cert.getSerialNumber());
            SignerInformationVerifier verifier;
            try {
                verifier = new JcaSimpleSignerInfoVerifierBuilder().build(cert);
                signerInfo.verify(verifier);

                LOGGER.debug("pkiMessage verified.");
            } catch (CMSException e) {
                throw new MessageDecodingException(e);
            } catch (OperatorCreationException e) {
                throw new MessageDecodingException(e);
            } catch (CertificateException e) {
                throw new MessageDecodingException(e);
            }
        } else {
            LOGGER.warn("Unable to verify message because the signedData contained no certificates.");
        }

        Hashtable attrTable = signerInfo
                .getSignedAttributes().toHashtable();

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("pkiMessage has {} signed attributes:", signerInfo
                    .getSignedAttributes().size());
            for (Entry entry : attrTable
                    .entrySet()) {
                LOGGER.debug("  {}: {}", entry.getKey().getId(), entry
                        .getValue().getAttrValues());
            }
        }

        MessageType messageType = toMessageType(attrTable
                .get(toOid(MESSAGE_TYPE)));
        Nonce senderNonce = toNonce(attrTable.get(toOid(SENDER_NONCE)));
        TransactionId transId = toTransactionId(attrTable.get(toOid(TRANS_ID)));

        if (messageType == MessageType.CERT_REP) {
            PkiStatus pkiStatus = toPkiStatus(attrTable.get(toOid(PKI_STATUS)));
            Nonce recipientNonce = toNonce(attrTable
                    .get(toOid(RECIPIENT_NONCE)));

            if (pkiStatus == PkiStatus.FAILURE) {
                FailInfo failInfo = toFailInfo(attrTable.get(toOid(FAIL_INFO)));
                LOGGER.debug("Finished decoding pkiMessage");
                return new CertRep(transId, senderNonce, recipientNonce,
                        failInfo);
            } else if (pkiStatus == PkiStatus.PENDING) {
                LOGGER.debug("Finished decoding pkiMessage");
                return new CertRep(transId, senderNonce, recipientNonce);
            } else {
                final CMSEnvelopedData ed = getEnvelopedData(signedContent
                        .getContent());
                final byte[] envelopedContent = decoder.decode(ed);
                CMSSignedData messageData;
                try {
                    messageData = new CMSSignedData(envelopedContent);
                } catch (CMSException e) {
                    throw new MessageDecodingException(e);
                }
                LOGGER.debug("Finished decoding pkiMessage");
                return new CertRep(transId, senderNonce, recipientNonce,
                        messageData);
            }
        } else {
            CMSEnvelopedData ed = getEnvelopedData(signedContent.getContent());
            byte[] decoded = decoder.decode(ed);
            if (messageType == MessageType.GET_CERT) {
                IssuerAndSerialNumber messageData = IssuerAndSerialNumber
                        .getInstance(decoded);
                LOGGER.debug("Finished decoding pkiMessage");
                return new GetCert(transId, senderNonce, messageData);
            } else if (messageType == MessageType.GET_CERT_INITIAL) {
                IssuerAndSubject messageData = new IssuerAndSubject(decoded);
                LOGGER.debug("Finished decoding pkiMessage");
                return new GetCertInitial(transId, senderNonce, messageData);
            } else if (messageType == MessageType.GET_CRL) {
                IssuerAndSerialNumber messageData = IssuerAndSerialNumber
                        .getInstance(decoded);
                LOGGER.debug("Finished decoding pkiMessage");
                return new GetCrl(transId, senderNonce, messageData);
            } else {
                PKCS10CertificationRequest messageData;
                try {
                    messageData = new PKCS10CertificationRequest(decoded);
                } catch (IOException e) {
                    throw new MessageDecodingException(e);
                }
                LOGGER.debug("Finished decoding pkiMessage");
                return new PkcsReq(transId, senderNonce, messageData);
            }
        }
    }

    private void validate(final CMSSignedData pkiMessage) {
        SignedData sd = SignedData.getInstance(pkiMessage.toASN1Structure()
                .getContent());
        LOGGER.debug("pkiMessage version: {}", sd.getVersion());
        LOGGER.debug("pkiMessage contentInfo contentType: {}", sd
                .getEncapContentInfo().getContentType());
    }

    private DERObjectIdentifier toOid(final ScepObjectIdentifier oid) {
        return new DERObjectIdentifier(oid.id());
    }

    private CMSEnvelopedData getEnvelopedData(final Object bytes)
            throws MessageDecodingException {
        // We expect the byte array to be a sequence
        // ... and that sequence to be a ContentInfo (but might be the
        // EnvelopedData)
        try {
            return new CMSEnvelopedData((byte[]) bytes);
        } catch (CMSException e) {
            throw new MessageDecodingException(e);
        }
    }

    private Nonce toNonce(final Attribute attr) {
        // Sometimes we don't get a sender nonce.
        if (attr == null) {
            return null;
        }
        final DEROctetString octets = (DEROctetString) attr.getAttrValues()
                .getObjectAt(0);

        return new Nonce(octets.getOctets());
    }

    private MessageType toMessageType(final Attribute attr) {
        final DERPrintableString string = (DERPrintableString) attr
                .getAttrValues().getObjectAt(0);

        return MessageType.valueOf(Integer.valueOf(string.getString()));
    }

    private TransactionId toTransactionId(final Attribute attr) {
        final DERPrintableString string = (DERPrintableString) attr
                .getAttrValues().getObjectAt(0);

        return new TransactionId(string.getOctets());
    }

    private PkiStatus toPkiStatus(final Attribute attr) {
        final DERPrintableString string = (DERPrintableString) attr
                .getAttrValues().getObjectAt(0);

        return PkiStatus.valueOf(Integer.valueOf(string.getString()));
    }

    private FailInfo toFailInfo(final Attribute attr) {
        final DERPrintableString string = (DERPrintableString) attr
                .getAttrValues().getObjectAt(0);

        return FailInfo.valueOf(Integer.valueOf(string.getString()));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy