gov.nih.nci.cacis.nav.SendSignedMail.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.cacis.nav.SendSignedMail.java

Source

/**
 * Copyright 5AM Solutions Inc
 * Copyright SemanticBits LLC
 * Copyright AgileX Technologies, Inc
 * Copyright Ekagra Software Technologies Ltd
 *
 * Distributed under the OSI-approved BSD 3-Clause License.
 * See http://ncip.github.com/cacis/LICENSE.txt for details.
 */
package gov.nih.nci.cacis.nav;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

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

import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMEException;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;

/**
 * Example that sends a signed mail message.
 * 
 * Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 * @author vinodh.rc@semanticbits.com
 */
public class SendSignedMail extends AbstractSendMail {

    private static final Logger LOG = Logger.getLogger(SendSignedMail.class);

    private final String keystore;
    private final String storepass;
    private final String keyAlias;

    private Certificate[] chain;
    private PrivateKey privateKey;

    /**
     * Initialize mail signer
     * 
     * @param mailProperties - properties for mail sender
     * @param from - from email address
     * @param host - email server host
     * @param port - email server port
     * @param protocol - email protocol
     * @param keystore - keystore to use for signing mail
     * @param storepass - keystore password
     * @param keyAlias - key alias to use
     * @throws MessagingException - exception thrown, if any
     */
    @SuppressWarnings({ "PMD.ExcessiveParameterList" })
    // CHECKSTYLE:OFF
    public SendSignedMail(Properties mailProperties, String from, String host, int port, String protocol,
            String keystore, String storepass, String keyAlias) throws MessagingException {
        super(mailProperties, from, host, port, protocol);
        this.keystore = keystore;
        this.storepass = storepass;
        this.keyAlias = keyAlias;

        init();
    }

    // CHECKSTYLE:ON
    /**
     * Initialize mail signer. Uses local host and default port
     * 
     * @param mailProperties - properties for mail sender
     * @param from - from email address
     * @param keystore - keystore to use for signing mail
     * @param storepass - keystore password
     * @param keyAlias - key alias to use
     * @throws MessagingException - exception thrown, if any
     */
    public SendSignedMail(Properties mailProperties, String from, String keystore, String storepass,
            String keyAlias) throws MessagingException {
        this(mailProperties, from, "localhost", SMTP_PORT, "SMTP", keystore, storepass, keyAlias);
    }

    private void init() throws MessagingException {
        try {
            /* CommandCap setting */
            setCommandCap();

            /* Add BC */
            Security.addProvider(new BouncyCastleProvider());

            /* Get keystore ref */
            final KeyStore keystoreRef = getKeyStoreRef();
            chain = keystoreRef.getCertificateChain(keyAlias);

            /* Get the private key to sign the message with */
            privateKey = (PrivateKey) keystoreRef.getKey(keyAlias, storepass.toCharArray());
            if (privateKey == null) {
                throw new Exception("cannot find private key for alias: " + keyAlias);
            }
            // CHECKSTYLE:OFF
        } catch (Exception ex) { // NOPMD
            // CHECKSTYLE:ON
            LOG.error("Error initalising signer!", ex);
            throw new MessagingException("Error initalising signer!", ex);
        }
    }

    /**
     * signing of mail
     * 
     * @param message - MimeMessage to be signed
     * @return MimeMessage - signed MimeMessage
     */
    public MimeMessage signMail(MimeMessage message) {
        /* Create the message to sign and encrypt */
        final Session session = createSession(getHost(), String.valueOf(getPort()), getMailProperties());
        return signMail(message, session);
    }

    /**
     * signing of mail
     * 
     * @param message - MimeMessage to be signed
     * @param session - Mail Session
     * @return MimeMessage - signed MimeMessage
     */
    public MimeMessage signMail(MimeMessage message, Session session) {
        try {
            return signMessage(message, session, chain, privateKey);
            // CHECKSTYLE:OFF
        } catch (Exception ex) { // NOPMD
            // CHECKSTYLE:ON
            LOG.error("Error signing mail", ex);
        }
        return null;
    }

    private MimeMessage signMessage(MimeMessage message, Session session, Certificate[] chain,
            PrivateKey privateKey) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
            NoSuchProviderException, CertStoreException, SMIMEException, MessagingException {
        /* Create the SMIMESignedGenerator */
        final SMIMESignedGenerator signer = createSigner(chain, privateKey);

        /* Add the list of certs to the generator */
        final List certList = new ArrayList();
        certList.add(chain[0]);
        final CertStore certs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList),
                "BC");
        signer.addCertificatesAndCRLs(certs);

        /* Sign the message */
        final MimeMultipart mm = signer.generate(message, "BC");
        final MimeMessage signedMessage = new MimeMessage(session);

        /* Set all original MIME headers in the signed message */
        final Enumeration headers = message.getAllHeaderLines();
        while (headers.hasMoreElements()) {
            signedMessage.addHeaderLine((String) headers.nextElement());
        }

        /* Set the content of the signed message */
        signedMessage.setContent(mm);
        signedMessage.saveChanges();

        return signedMessage;
    }

    private KeyStore getKeyStoreRef() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
            CertificateException, IOException {
        /* Open the keystore */
        KeyStore keystoreRef = null;
        InputStream is = null;
        try {
            keystoreRef = KeyStore.getInstance(STORE_TYPE, PROVIDER_TYPE);
            is = new FileInputStream(keystore);
            keystoreRef.load(is, storepass.toCharArray());
            // CHECKSTYLE:OFF
        } catch (Exception e) { // NOPMD
            // CHECKSTYLE:ON
            throw new KeyStoreException("Error loading keystore!", e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    LOG.debug("Error closing keystore reading stream!");
                }
            }
        }
        return keystoreRef;
    }

    private SMIMESignedGenerator createSigner(Certificate[] chain, PrivateKey privateKey) {
        final SMIMECapabilityVector capabilities = new SMIMECapabilityVector();
        capabilities.addCapability(SMIMECapability.dES_EDE3_CBC);
        capabilities.addCapability(SMIMECapability.rC2_CBC, 128);
        capabilities.addCapability(SMIMECapability.dES_CBC);

        final ASN1EncodableVector attributes = new ASN1EncodableVector();
        attributes.add(new SMIMEEncryptionKeyPreferenceAttribute(
                new IssuerAndSerialNumber(new X509Name(((X509Certificate) chain[0]).getIssuerDN().getName()),
                        ((X509Certificate) chain[0]).getSerialNumber())));
        attributes.add(new SMIMECapabilitiesAttribute(capabilities));

        final SMIMESignedGenerator signer = new SMIMESignedGenerator();
        signer.addSigner(privateKey, (X509Certificate) chain[0],
                "DSA".equals(privateKey.getAlgorithm()) ? SMIMESignedGenerator.DIGEST_SHA1
                        : SMIMESignedGenerator.DIGEST_MD5,
                new AttributeTable(attributes), null);

        return signer;
    }
}