package org.jscep.message;

import static org.slf4j.LoggerFactory.getLogger;

import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.cms.EnvelopedData;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.RecipientOperator;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
import org.bouncycastle.operator.InputDecryptor;
import org.slf4j.Logger;

/**
 * This class is used to decrypt the <tt>pkcsPkiEnvelope</tt> of a SCEP secure
 * message object and extract the <tt>messageData</tt> from within.
 *
 * @see PkcsPkiEnvelopeEncoder
 */
public final class PkcsPkiEnvelopeDecoder {
    private static final String RSA = "RSA";
    private static final Logger LOGGER = getLogger(PkcsPkiEnvelopeDecoder.class);
    private final X509Certificate recipient;
    private final PrivateKey privKey;

    /**
     * Creates a <tt>PkcsPkiEnveloperDecoder</tt> for the provided certificate
     * and key.
     * <p>
     * The provided certificate is used to identify the envelope recipient info,
     * which is then used with the key unwrapped using the provided key.
     *
     * @param recipient
     *            the entity for whom the message was enveloped.
     * @param privKey
     *            the key to unwrap the symmetric encrypting key.
     */
    public PkcsPkiEnvelopeDecoder(final X509Certificate recipient,
            final PrivateKey privKey) {
        this.recipient = recipient;
        this.privKey = privKey;
    }

    /**
     * Decrypts the provided <tt>pkcsPkiEnvelope</tt>, and extracts the content.
     *
     * @param pkcsPkiEnvelope
     *            the envelope to decrypt and open.
     * @return the content of the <tt>pkcsPkiEnvelope</tt>, the SCEP
     *         <tt>messageData</tt>.
     * @throws MessageDecodingException
     *             if the envelope cannot be decoded.
     */
    public byte[] decode(final CMSEnvelopedData pkcsPkiEnvelope)
            throws MessageDecodingException {
        LOGGER.debug("Decoding pkcsPkiEnvelope");
        validate(pkcsPkiEnvelope);

        LOGGER.debug(
                "Decrypting pkcsPkiEnvelope using key belonging to [dn={}; serial={}]",
                recipient.getSubjectDN(), recipient.getSerialNumber());
        final RecipientInformationStore recipientInfos = pkcsPkiEnvelope
                .getRecipientInfos();
        RecipientInformation info = recipientInfos
                .get(new JceKeyTransRecipientId(recipient));

        if (info == null) {
            throw new MessageDecodingException(
                    "Missing expected key transfer recipient " + recipient.getSubjectDN());
        }

        LOGGER.debug("pkcsPkiEnvelope encryption algorithm: {}", info
                .getKeyEncryptionAlgorithm().getAlgorithm());

        try {
            byte[] messageData = info.getContent(getKeyTransRecipient());
            LOGGER.debug("Finished decoding pkcsPkiEnvelope");
            return messageData;
        } catch (CMSException e) {
            throw new MessageDecodingException(e);
        }
    }

    private JceKeyTransEnvelopedRecipient getKeyTransRecipient() {
        return new InternalKeyTransEnvelopedRecipient(privKey);
    }

    private void validate(final CMSEnvelopedData pkcsPkiEnvelope) {
        EnvelopedData ed = EnvelopedData.getInstance(pkcsPkiEnvelope
                .toASN1Structure().getContent());
        LOGGER.debug("pkcsPkiEnvelope version: {}", ed.getVersion());
        LOGGER.debug("pkcsPkiEnvelope encryptedContentInfo contentType: {}", ed
                .getEncryptedContentInfo().getContentType());
    }
    
    private static class InternalKeyTransEnvelopedRecipient(privKey) {
        private final PrivateKey privKey;
        
        public InternalKeyTransEnvelopedRecipient(PrivateKey privKey) {
            this.privKey = privKey;
        }
        
        @Override
        public RecipientOperator getRecipientOperator(
            final AlgorithmIdentifier keyEncryptionAlgorithm,
            final AlgorithmIdentifier contentEncryptionAlgorithm,
            final byte[] encryptedContentEncryptionKey)
            throws CMSException {
            if ("1.3.14.3.2.7".equals(contentEncryptionAlgorithm.getAlgorithm().getId())) {
                final Cipher dataCipher;
                try {
                    Key encKey = unwrapKey(privKey, encryptedContentEncryptionKey);
                    ASN1Encodable sParams = contentEncryptionAlgorithm.getParameters();
                    dataCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
                    dataCipher.init(Cipher.DECRYPT_MODE, encKey, new IvParameterSpec(ASN1OctetString.getInstance(sParams).getOctets()));
                } catch (GeneralSecurityException e) {
                    throw new CMSException("Could not create DES cipher", e);
                }
                    
                private Key unwrapKey(PrivateKey wrappingKey, byte[] wrappedKey) throws GeneralSecurityException {
                    Cipher unwrapper = Cipher.getInstance(RSA);
                    unwrapper.init(Cipher.UNWRAP_MODE, wrappingKey);
                    return unwrapper.unwrap(wrappedKey, "DES",Cipher.SECRET_KEY);
                }

                return new RecipientOperator(new InputDecryptor() {
                    @Override
                    public AlgorithmIdentifier getAlgorithmIdentifier() {
                        return contentEncryptionAlgorithm;
                    }

                    @Override
                    public InputStream getInputStream(final InputStream dataIn) {
                        return new CipherInputStream(dataIn, dataCipher);
                    }
                });
            }
            return super.getRecipientOperator(keyEncryptionAlgorithm, contentEncryptionAlgorithm, encryptedContentEncryptionKey);
        }
    }
}

@@ -4273,23 +4273,16 @@
 cipient(
-privKey
 ) %7B%0A    

Update PkcsPkiEnvelopeDecoder.java