mitm.common.security.smime.SMIMEBuilderImplTest.java Source code

Java tutorial

Introduction

Here is the source code for mitm.common.security.smime.SMIMEBuilderImplTest.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.common.security.smime;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

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

import mitm.common.mail.MailSession;
import mitm.common.mail.MailUtils;
import mitm.common.mail.SkipHeadersOutputStream;
import mitm.common.security.SecurityFactory;
import mitm.common.security.SecurityFactoryFactory;
import mitm.common.security.bouncycastle.InitializeBouncycastle;
import mitm.common.security.certificate.CertificateUtils;
import mitm.common.security.certificate.X509CertificateInspector;
import mitm.common.security.cms.SignerIdentifier;
import mitm.common.security.cms.SignerInfo;
import mitm.common.tools.OpenSSLWrapper;
import mitm.common.util.ClassLoaderUtils;
import mitm.common.util.HexUtils;
import mitm.test.TestUtils;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.apache.log4j.PropertyConfigurator;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Test for SMIMEBuilderImpl. This test requires openssl to be installed and in the path because 
 * openssl is used for interoperability testing and testing whether the build s/mime messages 
 * are correct.
 * 
 * @author Martijn Brinkers
 *
 */
public class SMIMEBuilderImplTest {
    private static final File testDir = new File("test/resources/testdata/mail");
    private static final File tempDir = new File("test/tmp");

    private static SecurityFactory securityFactory;
    private static KeyStore keyStore;

    private static X509Certificate rootCertificate;
    private static X509Certificate encryptionCertificate;
    private static KeyStore.PasswordProtection passwd;
    private static PrivateKeyEntry privateKeyEntry;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        // add the root to the system classloader to make sure the CharsetAliasProvider
        // is found. This is only required for testing.
        ClassLoaderUtils.addFile(new File("."));

        PropertyConfigurator.configure("conf/log4j.properties");

        InitializeBouncycastle.initialize();

        securityFactory = SecurityFactoryFactory.getSecurityFactory();

        keyStore = loadKeyStore(new File("test/resources/testdata/keys/testCertificates.p12"), "test");

        rootCertificate = (X509Certificate) keyStore.getCertificate("root");
        encryptionCertificate = (X509Certificate) keyStore.getCertificate("ValidCertificate");

        passwd = new KeyStore.PasswordProtection("test".toCharArray());

        privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry("ValidCertificate", passwd);
    }

    private static KeyStore loadKeyStore(File file, String password) throws KeyStoreException {
        try {
            KeyStore keyStore = securityFactory.createKeyStore("PKCS12");

            // initialize key store
            keyStore.load(new FileInputStream(file), password.toCharArray());

            return keyStore;
        } catch (NoSuchProviderException e) {
            throw new KeyStoreException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new KeyStoreException(e);
        } catch (CertificateException e) {
            throw new KeyStoreException(e);
        } catch (FileNotFoundException e) {
            throw new KeyStoreException(e);
        } catch (IOException e) {
            throw new KeyStoreException(e);
        }
    }

    private static MimeMessage loadMessage(String filename) throws FileNotFoundException, MessagingException {
        File mail = new File(testDir, filename);

        MimeMessage message = MailUtils.loadMessage(mail);

        return message;
    }

    /*
     * verify the message using openssl
     */
    private static void verifyMessage(File messageFile, X509Certificate rootCertificate, File outputFile)
            throws IOException {
        OpenSSLWrapper wrapper = new OpenSSLWrapper();

        wrapper.setCertificateAuthorities(rootCertificate);

        Process p = wrapper.verify(messageFile);

        InputStream result = p.getInputStream();

        FileOutputStream output = new FileOutputStream(outputFile);

        IOUtils.copy(result, output);

        output.close();

        String error = IOUtils.toString(p.getErrorStream(), "US-ASCII");

        assertTrue("Error message: " + error, error.startsWith("Verification successful"));
    }

    /*
     * verify the message using openssl
     */
    private static void decryptMessage(File messageFile, PrivateKey privateKey, File outputFile)
            throws IOException {
        OpenSSLWrapper wrapper = new OpenSSLWrapper();

        wrapper.setPrivateKey(privateKey);

        Process p = wrapper.decrypt(messageFile);

        InputStream result = p.getInputStream();

        FileOutputStream output = new FileOutputStream(outputFile);

        IOUtils.copy(result, output);

        output.close();

        String error = IOUtils.toString(p.getErrorStream(), "US-ASCII");

        assertEquals("", error.trim());
    }

    /*
     * Check for some headers which should exist because they were added to the signed or encrypted blob.
     */
    private static void checkForEmbeddedHeaders(MimeMessage message) throws MessagingException {
        // the message should contain the signed from, to and subject
        assertEquals("<test@example.com>", message.getHeader("from", ","));
        assertEquals("<test@example.com>", message.getHeader("to", ","));
        assertEquals("normal message with attachment", message.getHeader("subject", ","));
    }

    /*
     * Check for some headers which should exist because they also exist on the source message.
     */
    private static void checkForSourceHeaders(MimeMessage message) throws MessagingException {
        // the message should contain the signed from, to and subject
        assertEquals("<test@example.com>", message.getHeader("from", ","));
        assertEquals("<test@example.com>", message.getHeader("to", ","));
        assertEquals("normal message with attachment", message.getHeader("subject", ","));
        assertEquals("1.0", message.getHeader("MIME-Version", ","));
        assertEquals("3", message.getHeader("X-Priority", ","));
        assertEquals("Normal", message.getHeader("X-MSMail-Priority", ","));
        assertEquals("Produced By Microsoft MimeOLE V6.00.2800.1896", message.getHeader("X-MimeOLE", ","));
        assertEquals("test 1,test 2", message.getHeader("X-Test", ","));
        assertEquals("test 3", message.getHeader("X-Test-a", ","));
        assertEquals("test 4", message.getHeader("X-Test-b", ","));
    }

    @Test
    public void testClearSignExtraCRLFPreamble() throws Exception {
        MimeMessage message = loadMessage("extra-cr-lf-preamble.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "extra-cr-lf-preamble-signed.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.DETACHED_SIGNATURE_TYPE,
                SMIMEUtils.dissectSigned((Multipart) newMessage.getContent())[1].getContentType());

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "extra-cr-lf-preamble-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals("Martijn Brinkers <martijn@djigzo.com>", newMessage.getHeader("from", ","));
        assertEquals("Martijn Brinkers <martijn@djigzo.com>", newMessage.getHeader("to", ","));
        assertEquals("test multiple attachments extra CR/LF preamble", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));
    }

    @Test
    public void testEncryptReceivedHeadersOrder() throws Exception {
        MimeMessage message = loadMessage("text-message-with-received-headers.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "text-message-with-received-headers-encrypted.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        String[] returnPaths = newMessage.getHeader("Return-Path");

        assertEquals(2, returnPaths.length);
        assertEquals("<return1@example.com>", returnPaths[0]);
        assertEquals("<return2@example.com>", returnPaths[1]);

        String[] received = newMessage.getHeader("Received");
        assertEquals(7, received.length);
        assertEquals("from secure.example.com (unknown [192.168.0.6])\r\n\tby example.com (Postfix) "
                + "with ESMTP id 0183D43843\r\n\tfor <martijn@example.com>; Sat, 22 Aug 2009 18:30:27 +0200 (CEST)",
                received[0]);
        assertEquals(
                "from test (desktop.box [192.168.178.20])\r\n\tby host.example.com (Postfix) with "
                        + "SMTP id 9883623F5\r\n\tfor <martijn@example.com>; Sat, 22 Aug 2009 12:30:24 -0400 (EDT)",
                received[6]);
    }

    /*
     * Create an S/MIME compressed message. 
     * This is not supported by Openssl, evolution and Outlook. 
     */
    @Test
    public void testCompressDeprecatedHeaders() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.setUseDeprecatedContentTypes(true);

        builder.compress();

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testCompressDeprecatedHeaders.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.DEPRECATED_COMPRESSED_CONTENT_TYPE, newMessage.getContentType());

        assertEquals(SMIMEHeader.Type.COMPRESSED, SMIMEHeader.getSMIMEContentType(newMessage));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));
    }

    @Test
    public void testEncryptDeprecatedHeaders() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.setUseDeprecatedContentTypes(true);

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptDeprecatedHeaders.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.DEPRECATED_ENCRYPTED_CONTENT_TYPE, newMessage.getContentType());

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        File opensslOutputFileSigned = new File(tempDir, "testEncryptDeprecatedHeaders-openssl.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        String content = (String) newMessage.getContent();

        assertEquals("test", content.trim());
    }

    @Test
    public void testOpaqueSignDeprecatedHeaders() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.setUseDeprecatedContentTypes(true);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.OPAQUE);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testOpaqueSignDeprecatedHeaders.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.DEPRECATED_ENCAPSULATED_SIGNED_CONTENT_TYPE, newMessage.getContentType());

        assertEquals(SMIMEHeader.Type.OPAQUE_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "testOpaqueSignDeprecatedHeaders-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        String content = (String) newMessage.getContent();

        assertEquals("test", content.trim());
    }

    @Test
    public void testClearSignDeprecatedHeaders() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.setUseDeprecatedContentTypes(true);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testClearSignDeprecatedHeaders.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        SMIMEUtils.dissectSigned((Multipart) newMessage.getContent());

        assertEquals(SMIMEHeader.DEPRECATED_DETACHED_SIGNATURE_TYPE,
                SMIMEUtils.dissectSigned((Multipart) newMessage.getContent())[1].getContentType());

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "testClearSignDeprecatedHeaders-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        String content = (String) newMessage.getContent();

        assertEquals("test", content.trim());
    }

    @Test
    public void testClearSignSimpleTextMessageLF() throws Exception {
        File mail = new File(testDir, "simple-text-message-LF.eml");

        /*
         * check to make sure the file does not contain CR
         */
        String body = IOUtils.toString(new FileInputStream(mail));

        assertFalse(body.contains("\r"));

        MimeMessage message = MailUtils.loadMessage(mail);

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "test-simple-text-message-signed-lf.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "test-simple-text-message-lf-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        String content = (String) newMessage.getContent();

        assertEquals("test", content.trim());
    }

    @Test
    public void testClearSignKeyUsageNotForSigning() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry("KeyUsageNotForSigning", passwd);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "clear-sign-key-usage-not-for-signing.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));
    }

    @Test
    public void testClearSignMissingSMIMEExtKeyUsage() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry("NoSMIMEExtKeyUsage", passwd);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "clear-sign-missing-smime-ext-key-usage.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));
    }

    @Test
    public void testClearSignUnknownCharset() throws Exception {
        // unknown-charset.eml contains a non-valid charset. We need to test if signing still works if 
        // we use CharsetAliasProvider which will provide support for this character set
        MimeMessage message = loadMessage("unknown-charset.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA, (X509Certificate) privateKeyEntry.getCertificate());

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        MailUtils.validateMessage(newMessage);

        File file = new File(tempDir, "test-signed-unknown-charset.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "test-signed-unknown-charset-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);
    }

    @Test
    public void testClearSignUnknownCharsetFallback() throws Exception {
        // unknown-charset.eml contains a non-valid charset. We need to test if signing still works if 
        // we use CharsetAliasProvider which will provide support for this character set
        MimeMessage message = loadMessage("unknown-charset-xxx.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA, (X509Certificate) privateKeyEntry.getCertificate());

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        MailUtils.validateMessage(newMessage);

        File file = new File(tempDir, "test-signed-unknown-charset.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "test-signed-unknown-charset-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);
    }

    @Test
    public void testClearSignUnknownContentType() throws Exception {
        MimeMessage message = loadMessage("unknown-content-type.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA, (X509Certificate) privateKeyEntry.getCertificate());

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        MailUtils.validateMessage(newMessage);

        File file = new File(tempDir, "test-signed-unknown-content-type.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "test-signed-unknown-content-type-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("application/xxx"));
    }

    @Test
    public void testClearSignUnknownContentTypeMultipart() throws Exception {
        MimeMessage message = loadMessage("unknown-content-type-multipart.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA, (X509Certificate) privateKeyEntry.getCertificate());

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        MailUtils.validateMessage(newMessage);

        File file = new File(tempDir, "test-signed-unknown-content-type-multipart.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "test-signed-unknown-content-type-multipart-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);
    }

    @Test
    public void testClearSignNoCertificates() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA, (X509Certificate) privateKeyEntry.getCertificate());

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "test-signed-no-certificates.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));
    }

    @Test
    public void testClearSignAddEncryptionKeyPreference() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA, (X509Certificate) privateKeyEntry.getCertificate());

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "test-signed-encryptionkeypreference-signed.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "test-signed-encryptionkeypreference-signed-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        String content = (String) newMessage.getContent();

        assertEquals("test", content.trim());
    }

    @Test
    public void testClearSignSimpleTextMessage() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "test-simple-text-message-signed.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.DETACHED_SIGNATURE_TYPE,
                SMIMEUtils.dissectSigned((Multipart) newMessage.getContent())[1].getContentType());

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "test-simple-text-message-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        String content = (String) newMessage.getContent();

        assertEquals("test", content.trim());
    }

    @Test
    public void testOpaqueSignSimpleTextMessage() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.OPAQUE);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "test-simple-text-message-opaque-signed.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.ENCAPSULATED_SIGNED_CONTENT_TYPE, newMessage.getContentType());

        assertEquals(SMIMEHeader.Type.OPAQUE_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "test-simple-text-message-opaque-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        String content = (String) newMessage.getContent();

        assertEquals("test", content.trim());
    }

    /*
     * Check whether quoted printable soft breaks inside an already signed message are
     * not changed by Javamail.
     * 
     * @throws Exception
     */
    @Test
    public void testEncryptSignedQuotedPrintableSoftBreaks() throws Exception {
        MimeMessage message = loadMessage("qp-soft-breaks-signed.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptSignedQuotedPrintableSoftBreaks.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFileSigned = new File(tempDir,
                "testEncryptSignedQuotedPrintableSoftBreaks-openssl-signed.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertTrue(newMessage.isMimeType("multipart/signed"));

        File opensslOutputFile = new File(tempDir, "testEncryptSignedQuotedPrintableSoftBreaks-openssl.eml");

        verifyMessage(opensslOutputFileSigned, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));
    }

    /*
     * Check whether quoted printable soft breaks inside an already signed message are
     * not changed by Javamail. Now using SMIMEEnvelopedGenerator directly and not
     * using our own makeContentBodyPart. 
     */
    @Test
    public void testEncryptSignedQuotedPrintableSoftBreaksDirectBC() throws Exception {
        MimeMessage message = loadMessage("qp-soft-breaks-signed.eml");

        SMIMEEnvelopedGenerator envelopedGenerator = new SMIMEEnvelopedGenerator();

        JceKeyTransRecipientInfoGenerator infoGenerator = new JceKeyTransRecipientInfoGenerator(
                encryptionCertificate);

        envelopedGenerator.addRecipientInfoGenerator(infoGenerator);

        JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(
                new ASN1ObjectIdentifier("1.2.840.113549.3.7"), 0).setProvider("BC");

        MimeBodyPart bodyPart = envelopedGenerator.generate(message, encryptorBuilder.build());

        MimeMessage newMessage = new MimeMessage(MailSession.getDefaultSession());

        newMessage.setContent(bodyPart.getContent(), bodyPart.getContentType());

        newMessage.saveChanges();

        File file = new File(tempDir, "testEncryptSignedQuotedPrintableSoftBreaksDirectBC.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFileSigned = new File(tempDir,
                "testEncryptSignedQuotedPrintableSoftBreaksDirectBC-openssl-signed.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertTrue(newMessage.isMimeType("multipart/signed"));

        File opensslOutputFile = new File(tempDir,
                "testEncryptSignedQuotedPrintableSoftBreaksDirectBC-openssl.eml");

        verifyMessage(opensslOutputFileSigned, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));
    }

    /*
     * Clear sign a multipart mixed message
     */
    @Test
    public void testClearSign() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testClearSign.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFile = new File(tempDir, "testClearSign-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        // the message should contain the signed from, to and subject
        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * Clear sign a multipart mixed message
     */
    @Test
    public void testClearSignNoFromProtectedHeader() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "subject", "to");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testClearSignNoFromProtectedHeader.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFile = new File(tempDir, "testClearSignNoFromProtectedHeader-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        // the message should contain the subject
        assertEquals("normal message with attachment", newMessage.getHeader("subject", ","));
        assertEquals("<test@example.com>", message.getHeader("to", ","));
        assertNull(newMessage.getHeader("from"));
    }

    /*
     * Clear sign a multipart mixed message
     */
    @Test
    public void testClearSignNoProtectedHeader() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testClearSignNoProtectedHeader.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFile = new File(tempDir, "testClearSignNoProtectedHeader-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        assertNull(newMessage.getHeader("subject", ","));
        assertEquals("<test@example.com>", message.getHeader("to", ","));
        assertNull(newMessage.getHeader("from"));
    }

    /*
     * Clear sign a multipart mixed message
     */
    @Test
    public void testClearSignDefaultProtectedHeader() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testClearSignDefaultProtectedHeader.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFile = new File(tempDir, "testClearSignDefaultProtectedHeader-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        assertEquals("normal message with attachment", newMessage.getHeader("subject", ","));
        assertEquals("<test@example.com>", message.getHeader("to", ","));
        assertNull(newMessage.getHeader("from"));
    }

    /*
     * Clear sign a multipart mixed message using multiple signers.
     */
    @Test
    public void testClearSignMultipleSigners() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        PrivateKeyEntry md5Entry = (PrivateKeyEntry) keyStore.getEntry("md5Hash", passwd);

        builder.addSigner(md5Entry.getPrivateKey(), (X509Certificate) md5Entry.getCertificate(),
                SMIMESigningAlgorithm.MD5WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(md5Entry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testClearSignMultipleSigners.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFile = new File(tempDir, "testClearSignMultipleSigners-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        // the message should contain the signed from, to and subject
        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * Opaque sign a multipart/mixed message.
     */
    @Test
    public void testOpaqueSign() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.OPAQUE);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testOpaqueSign.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.OPAQUE_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFile = new File(tempDir, "testOpaqueSign-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        // the message should contain the signed from, to and subject
        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * Opaque sign a multipart/mixed message signed by multiple signers.
     */
    @Test
    public void testOpaqueSignMultipleSigners() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        PrivateKeyEntry md5Entry = (PrivateKeyEntry) keyStore.getEntry("md5Hash", passwd);

        builder.addSigner(md5Entry.getPrivateKey(), (X509Certificate) md5Entry.getCertificate(),
                SMIMESigningAlgorithm.MD5WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(md5Entry.getCertificateChain()));

        builder.sign(SMIMESignMode.OPAQUE);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testOpaqueSignMultipleSigners.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.OPAQUE_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFile = new File(tempDir, "testOpaqueSignMultipleSigners-openssl.eml");

        verifyMessage(file, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        // the message should contain the signed from, to and subject
        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * Create an S/MIME compressed message. 
     * This is not supported by Openssl, evolution and Outlook. 
     */
    @Test
    public void testCompressed() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.compress();

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testCompressed.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.COMPRESSED_CONTENT_TYPE, newMessage.getContentType());

        assertEquals(SMIMEHeader.Type.COMPRESSED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);
    }

    /*
     * Create an S/MIME compressed message. 
     * This is not supported by Openssl, evolution and Outlook. 
     */
    @Test
    public void testSimpleMessageCompress() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.compress();

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "test-simple-text-message-compressed.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.COMPRESSED, SMIMEHeader.getSMIMEContentType(newMessage));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));
    }

    /*
     * 3-DES Encrypt a multipart/mixed message using the issuerAndSerialNumber RecipientIdentifier.
     */
    @Test
    public void testEncryptIssuerSerialRecipientId() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptIssuerSerialRecipientId.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFileSigned = new File(tempDir, "testEncryptIssuerSerialRecipientId-openssl.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForEmbeddedHeaders(newMessage);
    }

    @Test
    public void testSimpleMessageEncrypt() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "simple-text-message-encrypted.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.ENCRYPTED_CONTENT_TYPE, newMessage.getContentType());

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        File opensslOutputFileSigned = new File(tempDir, "simple-text-message-encrypted-openssl.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        assertTrue(newMessage.isMimeType("text/plain"));

        assertEquals("test@example.com", newMessage.getHeader("from", ","));
        assertEquals("test@example.com", newMessage.getHeader("to", ","));
        assertEquals("test simple message", newMessage.getHeader("subject", ","));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        String content = (String) newMessage.getContent();

        assertEquals("test", content.trim());
    }

    /*
     * Encrypt a multipart/mixed message with multiple recipients
     */
    @Test
    public void testEncryptMultipleRecipients() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        Enumeration<String> aliases = keyStore.aliases();

        int i = 15;

        while (aliases.hasMoreElements() && i != 0) {
            String alias = aliases.nextElement();

            if (keyStore.isKeyEntry(alias)) {
                X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias);

                builder.addRecipient(certificate, SMIMERecipientMode.ISSUER_SERIAL);

                i--;
            }
        }

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptMultipleRecipients.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFileSigned = new File(tempDir, "testEncryptMultipleRecipients-openssl.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * AES Encrypt a multipart/mixed message using the issuerAndSerialNumber RecipientIdentifier.
     * 
     * Outlook does not support AES encrypted email.
     */
    @Test
    public void testEncryptIssuerSerialRecipientIdAES() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.AES128_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptIssuerSerialRecipientIdAES.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFileSigned = new File(tempDir, "testEncryptIssuerSerialRecipientIdAES-openssl.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * RC2 Encrypt a multipart/mixed message using the issuerAndSerialNumber RecipientIdentifier.
     */
    @Test
    public void testEncryptIssuerSerialRecipientIdRC2() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.RC2_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptIssuerSerialRecipientIdRC2.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFileSigned = new File(tempDir, "testEncryptIssuerSerialRecipientIdRC2-openssl.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * Creates a message with subject key identifier as the public key identifier.
     * 
     * This is not supported by Openssl and evolution (only issuer/serial number recipient id). 
     * It is supported by Outlook.
     */
    @Test
    public void testEncryptSubjectKeyIdRecipientId() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.SUBJECT_KEY_ID_IF_AVAILABLE);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptSubjectKeyIdRecipientId.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);
    }

    /*
     * Creates a message with subject key identifier and issuer/serial number as the public key identifier.
     * 
     * This is not supported by Openssl and evolution (only issuer/serial number recipient id). 
     * It is supported by Outlook.
     */
    @Test
    public void testEncryptBothRecipientId() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.BOTH);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptBothRecipientId.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);
    }

    /*
     * First sign a multipart/mixed message, then encrypt it.
     */
    @Test
    public void testSignEncrypt() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testSignEncrypt.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFileSigned = new File(tempDir, "testSignEncrypt-openssl-signed.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "testSignEncrypt-openssl.eml");

        verifyMessage(opensslOutputFileSigned, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        // the message should contain the signed from, to and subject
        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * First sign a multipart/mixed message, then encrypt it and again sign it.
     */
    @Test
    public void testSignEncryptSign() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testSignEncryptSign.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFileEncrypted = new File(tempDir, "testSignEncryptSign-openssl-signed-encrypted.eml");

        verifyMessage(file, rootCertificate, opensslOutputFileEncrypted);

        newMessage = MailUtils.loadMessage(opensslOutputFileEncrypted);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFileSigned = new File(tempDir, "testSignEncryptSign-openssl-signed.eml");

        decryptMessage(opensslOutputFileEncrypted, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "testSignEncryptSign-openssl.eml");

        verifyMessage(opensslOutputFileSigned, rootCertificate, opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        // the message should contain the signed from, to and subject
        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * First encrypt a message and then sign it. 
     */
    @Test
    public void testEncryptSign() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptSign.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFileEncrypted = new File(tempDir, "testEncryptSign-openssl-encrypted.eml");

        verifyMessage(file, rootCertificate, opensslOutputFileEncrypted);

        newMessage = MailUtils.loadMessage(opensslOutputFileEncrypted);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "testSignEncryptSign-openssl.eml");

        decryptMessage(opensslOutputFileEncrypted, privateKeyEntry.getPrivateKey(), opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        // the message should contain the signed from, to and subject
        checkForEmbeddedHeaders(newMessage);
    }

    /*
     * Encrypt message, then sign and again encrypt.
     */
    @Test
    public void testEncryptSignEncrypt() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        builder.addRecipient(encryptionCertificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testEncryptSignEncrypt.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);

        File opensslOutputFileSigned = new File(tempDir, "testEncryptSignEncrypt-openssl-signed.eml");

        decryptMessage(file, privateKeyEntry.getPrivateKey(), opensslOutputFileSigned);

        newMessage = MailUtils.loadMessage(opensslOutputFileSigned);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFileEncrypted = new File(tempDir, "testEncryptSignEncrypt-openssl-encrypted.eml");

        verifyMessage(opensslOutputFileSigned, rootCertificate, opensslOutputFileEncrypted);

        newMessage = MailUtils.loadMessage(opensslOutputFileEncrypted);

        assertEquals(SMIMEHeader.Type.ENCRYPTED, SMIMEHeader.getSMIMEContentType(newMessage));

        File opensslOutputFile = new File(tempDir, "testEncryptSignEncrypt-openssl.eml");

        decryptMessage(opensslOutputFileEncrypted, privateKeyEntry.getPrivateKey(), opensslOutputFile);

        newMessage = MailUtils.loadMessage(opensslOutputFile);

        assertEquals(SMIMEHeader.Type.NO_SMIME, SMIMEHeader.getSMIMEContentType(newMessage));

        assertTrue(newMessage.isMimeType("multipart/mixed"));

        // the message should contain the signed from, to and subject
        checkForEmbeddedHeaders(newMessage);
    }

    @Test
    public void testEncryptSignEncryptRepeat() throws Exception {
        for (int i = 0; i < 200; i++) {
            testEncryptSignEncrypt();
        }
    }

    /*
     * Opaque sign a multipart/mixed message.
     */
    @Test
    public void testOpaqueSignNullCertificates() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates((X509Certificate) null);

        builder.sign(SMIMESignMode.OPAQUE);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testOpaqueSignNullCertificates.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.OPAQUE_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);
    }

    /*
     * Opaque sign a multipart/mixed message.
     */
    @Test
    public void testOpaqueSignEmptyCertificateArray() throws Exception {
        MimeMessage message = loadMessage("normal-message-with-attach.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), (X509Certificate) privateKeyEntry.getCertificate(),
                SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(new X509Certificate[] {});
        builder.addCertificates(new LinkedList<X509Certificate>());

        builder.sign(SMIMESignMode.OPAQUE);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testOpaqueSignEmptyCertificateArray.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.OPAQUE_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        checkForSourceHeaders(newMessage);
    }

    @Test
    public void testBuildMultiLayer() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "subject");

        /*
         * ValidCertificate is for test@example.com
         * multipleEmail is for test@example.com, test2@example.com and test3@example.com
         * NoEmail contains no email
         */
        builder.addRecipient((X509Certificate) keyStore.getCertificate("NoEmail"),
                SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.AES128_CBC);

        builder.addRecipient((X509Certificate) keyStore.getCertificate("ValidCertificate"),
                SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        builder.addRecipient((X509Certificate) keyStore.getCertificate("multipleEmail"),
                SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.RC2_CBC);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "testBuildMultiLayer.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);
    }

    /*
     * Test for bug APPLIANCE-2
     */
    @Test
    public void testEncryptBase64EncodeBug() throws Exception {
        MimeMessage message = new MimeMessage(MailSession.getDefaultSession());

        message.setSubject("test");
        message.setContent("test", "text/plain");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message, "to", "subject", "from");

        X509Certificate certificate = TestUtils
                .loadCertificate("test/resources/testdata/certificates/certificate-base64-encode-bug.cer");

        builder.addRecipient(certificate, SMIMERecipientMode.ISSUER_SERIAL);

        builder.encrypt(SMIMEEncryptionAlgorithm.DES_EDE3_CBC);

        MimeMessage newMessage = builder.buildMessage();

        newMessage.saveChanges();

        File file = new File(tempDir, "testEncryptBase64EncodeBug.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        newMessage.writeTo(new SkipHeadersOutputStream(bos));

        String blob = new String(bos.toByteArray(), "us-ascii");

        // check if all lines are not longer than 76 characters
        LineIterator it = IOUtils.lineIterator(new StringReader(blob));

        while (it.hasNext()) {
            String next = it.nextLine();

            if (next.length() > 76) {
                fail("Line length exceeds 76: " + next);
            }
        }
    }

    /*
     * Clear sign a multipart mixed message with subject key id instead of issuer/serial
     */
    @Test
    public void testClearSignSubjectKeyIdentifier() throws Exception {
        MimeMessage message = loadMessage("simple-text-message.eml");

        SMIMEBuilder builder = new SMIMEBuilderImpl(message);

        builder.addSigner(privateKeyEntry.getPrivateKey(), X509CertificateInspector.getSubjectKeyIdentifier(
                (X509Certificate) privateKeyEntry.getCertificate()), SMIMESigningAlgorithm.SHA1WITHRSA);

        builder.addCertificates(CertificateUtils.getX509Certificates(privateKeyEntry.getCertificateChain()));

        builder.sign(SMIMESignMode.CLEAR);

        MimeMessage newMessage = builder.buildMessage();

        File file = new File(tempDir, "sign-subject-key-id.eml");

        FileOutputStream output = new FileOutputStream(file);

        MailUtils.writeMessage(newMessage, output);

        newMessage = MailUtils.loadMessage(file);

        assertEquals(SMIMEHeader.Type.CLEAR_SIGNED, SMIMEHeader.getSMIMEContentType(newMessage));

        /*
         * Note: openssl in Ubuntu 10.04 does not support subject key identifier so we cannot validate it
         */
        SMIMESignedInspector inspector = new SMIMESignedInspectorImpl(newMessage, "BC", "BC");

        List<SignerInfo> signers = inspector.getSigners();

        assertEquals(1, signers.size());

        SignerIdentifier signerId = signers.get(0).getSignerId();

        assertNull(signerId.getIssuer());
        assertNull(signerId.getSerialNumber());
        assertEquals("9C82DE52944798337B05036A187DED41632EFBFF",
                HexUtils.hexEncode(signerId.getSubjectKeyIdentifier()));
    }
}