mitm.application.djigzo.james.mailets.SMIMEEncrypt.java Source code

Java tutorial

Introduction

Here is the source code for mitm.application.djigzo.james.mailets.SMIMEEncrypt.java

Source

/*
 * Copyright (c) 2008-2012, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo 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 Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, 
 * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, 
 * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, 
 * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Eclipse Public License, 
 * tyrex license, freemarker license, dom4j license, mx4j license,
 * Spice Software License, Common Development and Distribution License
 * (CDDL), Common Public License (CPL) the licensors of this Program grant 
 * you additional permission to convey the resulting work.
 */
package mitm.application.djigzo.james.mailets;

import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Collection;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import mitm.application.djigzo.james.Certificates;
import mitm.application.djigzo.james.DjigzoMailAttributes;
import mitm.application.djigzo.james.DjigzoMailAttributesImpl;
import mitm.common.mail.MimeMessageWithID;
import mitm.common.security.smime.SMIMEBuilder;
import mitm.common.security.smime.SMIMEBuilderException;
import mitm.common.security.smime.SMIMEBuilderImpl;
import mitm.common.security.smime.SMIMEEncryptionAlgorithm;
import mitm.common.security.smime.SMIMERecipientMode;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.mailet.Mail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * S/MIME encrypts the message using the certificates from the mail attributes (See {@link DjigzoMailAttributes#getCertificates()})
 * If there are no certificates the mail will not be encrypted.
 * 
 * The following mailet parameters are supported:
 * 
 * algorithm               : The secret key encryption algorithm to use (default: 3DES)
 * algorithmAttribute      : The Mail attribute to read the secret key encryption algorithm from 
 * keySize                 : The size of the secret key (default: default (max) key size for the algorithm)
 * recipientMode           : What method to use for certificate id. One of ISSUER_SERIAL, SUBJECT_KEY_ID_IF_AVAILABLE or BOTH 
 *                           (default: ISSUER_SERIAL)
 * protectedHeader         : Header that should be add to the encrypted blob (can be more than one protectedHeader)      
 * retainMessageID         : If true the original Message-ID will be used for the encrypted message                    
 * catchRuntimeExceptions  : If true all RunTimeExceptions are caught (default: true)
 * catchErrors             : If true all Errors are caught (default: true)
 * 
 * @author Martijn Brinkers
 *
 */
public class SMIMEEncrypt extends AbstractDjigzoMailet {
    private final static Logger logger = LoggerFactory.getLogger(SMIMEEncrypt.class);

    /*
     * The mailet initialization parameters used by this mailet.
     */
    private enum Parameter {
        ALGORITHM("algorithm"), ALGORITHM_ATTRIBUTE("algorithmAttribute"), KEY_SIZE("keySize"), RECIPIENT_MODE(
                "recipientMode"), USE_DEPRECATED_CONTENT_TYPES("useDeprecatedContentTypes"), PROTECTED_HEADER(
                        "protectedHeader"), RETAIN_MESSAGE_ID("retainMessageID");

        private String name;

        private Parameter(String name) {
            this.name = name;
        }
    };

    /*
     * How should the certificate be identified in the encrypted blob
     */
    private SMIMERecipientMode recipientMode = SMIMERecipientMode.ISSUER_SERIAL;

    /*
     * The encryption algorithm
     */
    private SMIMEEncryptionAlgorithm encryptionAlgorithm = SMIMEEncryptionAlgorithm.DES_EDE3_CBC;

    /*
     * The Mail attribute from which the encryption algorithm is read. If null or if there is no
     * attribute with the given name, the encryptionAlgorithm is used. 
     */
    private String encryptionAlgorithmAttribute;

    /*
     * Key size to use. Only applicable when the algorithm supports varaiable key size
     */
    private Integer keySize;

    /*
     * If true the old x-pkcs7-* content types will be used
     */
    private boolean useDeprecatedContentTypes;

    /*
     * If true the original Message-ID will be used for the encrypted message
     */
    private boolean retainMessageID = true;

    /*
     * Determines which headers will be encrypted.
     */
    private String[] protectedHeaders = new String[] {};

    @Override
    protected Logger getLogger() {
        return logger;
    }

    protected SMIMERecipientMode getRecipientMode() {
        return recipientMode;
    }

    protected SMIMEEncryptionAlgorithm getEncryptionAlgorithm(Mail mail) {
        SMIMEEncryptionAlgorithm encryptionAlgorithm = null;

        /*
         * Check if the Mail attribute contains the encryption algorithm
         */
        if (StringUtils.isNotEmpty(encryptionAlgorithmAttribute)) {
            Object attributeValue = mail.getAttribute(encryptionAlgorithmAttribute);

            if (attributeValue != null) {
                if (attributeValue instanceof String) {
                    encryptionAlgorithm = SMIMEEncryptionAlgorithm.fromName((String) attributeValue);

                    if (encryptionAlgorithm == null) {
                        getLogger().warn("The attribute value {} is not a valid algorithm.", attributeValue);
                    }
                } else {
                    getLogger().warn("Attribute {} is not a String but a ", encryptionAlgorithmAttribute,
                            attributeValue.getClass());
                }
            } else {
                getLogger().debug("Attribute with name {} was not found", encryptionAlgorithmAttribute);
            }
        }

        if (encryptionAlgorithm == null) {
            /*
             * Use the default encryption algorithm
             */
            encryptionAlgorithm = this.encryptionAlgorithm;
        }

        getLogger().debug("Encryption algorithm: {}", encryptionAlgorithm);

        return encryptionAlgorithm;
    }

    private void initProtectedHeaders() {
        String param = getInitParameter(Parameter.PROTECTED_HEADER.name);

        if (param != null) {
            protectedHeaders = param.split("\\s*,\\s*");
        }
    }

    @Override
    final public void initMailet() {
        getLogger().info("Initializing mailet: " + getMailetName());

        String param = getInitParameter(Parameter.ALGORITHM.name);

        if (param != null) {
            encryptionAlgorithm = SMIMEEncryptionAlgorithm.fromName(param);

            if (encryptionAlgorithm == null) {
                throw new IllegalArgumentException(param + " is not a valid encryption algorithm.");
            }
        }

        encryptionAlgorithmAttribute = getInitParameter(Parameter.ALGORITHM_ATTRIBUTE.name);

        param = getInitParameter(Parameter.KEY_SIZE.name);

        if (param != null) {
            keySize = Integer.valueOf(param);

            if (keySize <= 0) {
                throw new IllegalArgumentException("KeySize must be > 0");
            }
        }

        param = getInitParameter(Parameter.RECIPIENT_MODE.name);

        if (param != null) {
            recipientMode = SMIMERecipientMode.fromName(param);

            if (recipientMode == null) {
                throw new IllegalArgumentException(recipientMode + " is not a valid SMIMERecipientMode.");
            }
        }

        param = getInitParameter(Parameter.USE_DEPRECATED_CONTENT_TYPES.name);

        if (param != null) {
            useDeprecatedContentTypes = BooleanUtils.toBoolean(param);
        }

        param = getInitParameter(Parameter.RETAIN_MESSAGE_ID.name);

        if (param != null) {
            retainMessageID = BooleanUtils.toBoolean(param);
        }

        initProtectedHeaders();

        StrBuilder sb = new StrBuilder();

        sb.append("Algorithm: ");
        sb.append(encryptionAlgorithm);
        sb.appendSeparator("; ");
        sb.append("Algorithm attribute: ");
        sb.append(encryptionAlgorithmAttribute);
        sb.appendSeparator("; ");
        sb.append("KeySize: ");
        sb.append(keySize);
        sb.appendSeparator("; ");
        sb.append("Recipient Mode: ");
        sb.append(recipientMode);
        sb.appendSeparator("; ");
        sb.append("Use deprecated content-type's: ");
        sb.append(useDeprecatedContentTypes);
        sb.appendSeparator("; ");
        sb.append("Retain Message-ID: ");
        sb.append(retainMessageID);
        sb.appendSeparator("; ");
        sb.append("Protected headers: ");
        sb.append(StringUtils.join(protectedHeaders, ","));

        getLogger().info(sb.toString());
    }

    private String[] getProtectedHeaders(Mail mail) {
        /*
         * Get the protected headers from mail if available, if not available use the protected headers
         */
        DjigzoMailAttributes attributes = new DjigzoMailAttributesImpl(mail);

        String[] protectedHeaders = attributes.getProtectedHeaders();

        if (protectedHeaders == null) {
            protectedHeaders = this.protectedHeaders;
        }

        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Protected headers: {}", StringUtils.join(protectedHeaders, ", "));
        }

        return protectedHeaders;
    }

    @Override
    public void serviceMail(Mail mail) {
        try {
            DjigzoMailAttributes mailAttributes = new DjigzoMailAttributesImpl(mail);

            Certificates certificateStore = mailAttributes.getCertificates();

            if (certificateStore != null) {
                Collection<X509Certificate> certificates = certificateStore.getCertificates();

                if (certificates.size() > 0) {
                    MimeMessage message = mail.getMessage();

                    if (message != null) {
                        SMIMEBuilder sMIMEBuilder = new SMIMEBuilderImpl(message, getProtectedHeaders(mail));

                        sMIMEBuilder.setUseDeprecatedContentTypes(useDeprecatedContentTypes);

                        for (X509Certificate certificate : certificates) {
                            sMIMEBuilder.addRecipient(certificate, getRecipientMode());
                        }

                        SMIMEEncryptionAlgorithm localAlgorithm = getEncryptionAlgorithm(mail);

                        int localKeySize = (keySize != null ? keySize : localAlgorithm.defaultKeySize());

                        getLogger().debug("Encrypting the message. Encryption algorithm: {}, key size: {}",
                                localAlgorithm, localKeySize);

                        sMIMEBuilder.encrypt(localAlgorithm, localKeySize);

                        MimeMessage encrypted = sMIMEBuilder.buildMessage();

                        if (encrypted != null) {
                            encrypted.saveChanges();

                            /*
                             * A new MimeMessage instance will be created. This makes ure that the 
                             * MimeMessage can be written by James (using writeTo()). The message-ID
                             * of the source message will be retained if told to do so
                             */
                            encrypted = retainMessageID ? new MimeMessageWithID(encrypted, message.getMessageID())
                                    : new MimeMessage(encrypted);

                            mail.setMessage(encrypted);
                        }
                    } else {
                        getLogger().warn("Message is null.");
                    }
                } else {
                    getLogger().warn("Certificate collection is empty.");
                }
            } else {
                getLogger().warn("Certificates attribute not found.");
            }
        } catch (SMIMEBuilderException e) {
            getLogger().error("Error encrypting the message.", e);
        } catch (MessagingException e) {
            getLogger().error("Error reading the message.", e);
        } catch (IOException e) {
            getLogger().error("IOException.", e);
        }
    }

    public boolean isUseDeprecatedContentTypes() {
        return useDeprecatedContentTypes;
    }

    public boolean isRetainMessageID() {
        return retainMessageID;
    }

    public String[] getProtectedHeaders() {
        return protectedHeaders;
    }

    public void setRecipientMode(SMIMERecipientMode recipientMode) {
        this.recipientMode = recipientMode;
    }
}