cz.etruhla.mailsigner.Helpers.java Source code

Java tutorial

Introduction

Here is the source code for cz.etruhla.mailsigner.Helpers.java

Source

/**
 * Copyright (C) 2013 Vit Hnilica vit.hnilica@etruhla.cz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cz.etruhla.mailsigner;

import java.io.IOException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.activation.DataHandler;
import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.mail.smime.SMIMEException;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;

public class Helpers {

    private Helpers() {
    }

    public static final int SUBALTNAME_RFC822NAME = 1;
    final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("d.m.yyyy");

    /**
     * vybere z certifikatu vsechny emaily ktere muze podepsat
     * 
     * @param cert
     * @return
     * @throws CertificateParsingException
     * @throws AddressException
     */
    public static Set<InternetAddress> getEmailAddresses(X509Certificate cert)
            throws CertificateParsingException, AddressException {
        HashSet<InternetAddress> addresses = new HashSet<InternetAddress>();
        X500Name x500name = new X500Name(cert.getSubjectDN().getName());
        RDN[] ems;
        ems = x500name.getRDNs(BCStyle.EmailAddress);// Email address (RSA
        // PKCS#9 extension) -
        // IA5String.
        if (ems != null && ems.length > 0) {
            for (RDN em : ems) {
                addresses.add(new InternetAddress(IETFUtils.valueToString(em.getFirst().getValue())));
            }
        }
        ems = x500name.getRDNs(BCStyle.E);// email address in Verisign
        // certificates
        if (ems != null && ems.length > 0) {
            for (RDN em : ems) {
                addresses.add(new InternetAddress(IETFUtils.valueToString(em.getFirst().getValue())));
            }
        }
        // projeti subject alternative name
        if (cert.getSubjectAlternativeNames() != null) {
            for (List<?> l : cert.getSubjectAlternativeNames()) {
                if ((Integer) (l.get(0)) == SUBALTNAME_RFC822NAME) {
                    addresses.add(new InternetAddress((String) (l.get(1))));
                }
            }
        }

        return addresses;
    }

    /**
     * z certifikatu vybere udaje ktere se pouziji pro zobrazeni popisu
     * 
     * @param cert
     * @return
     * @throws CertificateParsingException
     * @throws AddressException
     */
    public static String getCertDescription(X509Certificate cert)
            throws CertificateParsingException, AddressException {
        String desc = "";
        String dn = null;
        if (cert.getSubjectDN() != null)
            dn = cert.getSubjectDN().getName();

        if (dn != null) {
            desc = dn;
        } else {
            //neni nazev, tak se zobrazi alspon emaily
            String emailsStr = null;
            InternetAddress emails[] = Helpers.getEmailAddresses(cert).toArray(new InternetAddress[0]);
            for (int i = 0; i < emails.length; i++) {
                InternetAddress email = emails[i];
                if (i == 0) {
                    emailsStr = email.getAddress();
                } else if (i <= 4) {
                    emailsStr = emailsStr + ", " + email.getAddress();
                } else {
                    //vic jak 4 adresy uz nezobrazim
                    emailsStr = emailsStr + " ...";
                    break;
                }
            }
            if (emailsStr != null) {
                desc = "(" + emailsStr + ")";
            } else {
                desc = "";//bez nazvu
            }
        }
        //prida nakonec datum expirace
        Date expiration = cert.getNotAfter();
        if (expiration != null) {
            desc = desc + " (" + DATE_FORMAT.format(expiration) + ")";
        }

        return desc;
    }

    /**
     * vyzobne ze zpravy obsah pokud je to vic casti, tak je prevede do
     * MimeMultipart a pak vlozi do MimeBodyPart je to kvuli podpisu, ktery by
     * jinak udelat nesel
     * 
     * @param message
     * @return
     * @throws MessagingException
     * @throws IOException
     */
    public static MimeBodyPart makeContentBodyPart(MimeMessage message) throws MessagingException, IOException {
        MimeBodyPart content = new MimeBodyPart();
        message.removeHeader("Message-Id");
        message.removeHeader("Mime-Version");

        if (message.getContent() instanceof Multipart) {
            Multipart mp = (Multipart) message.getContent();
            MimeMultipart dataMultiPart = new MimeMultipart();
            for (int i = 0; i < mp.getCount(); i++) {
                MimeBodyPart dataPart = new MimeBodyPart();
                DataHandler data = new DataHandler(new ByteArrayDataSource(mp.getBodyPart(i).getInputStream(),
                        mp.getBodyPart(i).getContentType()));
                dataPart.setDataHandler(data);

                Enumeration<?> headers = mp.getBodyPart(i).getAllHeaders();
                while (headers.hasMoreElements()) {
                    Header h = (Header) headers.nextElement();
                    dataPart.addHeader(h.getName(), h.getValue());
                }
                dataMultiPart.addBodyPart(dataPart, i);
            }
            content.setContent(dataMultiPart);
            return content;
        }
        content.setContent(message.getContent(), message.getContentType());
        return content;
    }

    private static MimeMultipart signMimeBodyPart(MimeBodyPart content, String signatureAlgorithm, PrivateKey pk,
            X509Certificate cert, Store certsStore)
            throws OperatorCreationException, CertificateEncodingException, SMIMEException {
        ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm).build(pk);
        SMIMESignedGenerator gen = new SMIMESignedGenerator();
        gen.addSignerInfoGenerator(
                new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(signer,
                        cert));
        gen.addCertificates(certsStore);
        return gen.generate(content);
    }

    /**
     * podepise zpravu
     * 
     * @param message
     * @param signatureAlgorithm
     *            algoritmus podpisu, napr. SHA1withRSA, SHA256withDSA ...
     * @param pk
     *            privatni klic kterym zpravu podepisujeme
     * @param cert
     *            certifikat pouzitym v podpisu
     * @param certsStore
     *            certifikaty ktere se priklaji k podpisu
     * @return podepsana zprava
     * @throws MessagingException
     * @throws IOException
     * @throws CertificateEncodingException
     * @throws OperatorCreationException
     * @throws SMIMEException
     */
    public static MimeMessage sign(MimeMessage message, String signatureAlgorithm, PrivateKey pk,
            X509Certificate cert, Store certsStore) throws MessagingException, IOException,
            CertificateEncodingException, OperatorCreationException, SMIMEException {
        MimeBodyPart dataPart = makeContentBodyPart(message);
        MimeMultipart content = signMimeBodyPart(dataPart, signatureAlgorithm, pk, cert, certsStore);
        message.setContent(content);
        message.saveChanges();

        return message;
    }

    /**
     * zprava ktara pred odeslanim nastavuje messageId podle potreby
     * 
     */
    public static class CorrectedMimeMessage extends MimeMessage {
        String messageId;

        public CorrectedMimeMessage(Session session, MimeMessage message, String messageId)
                throws MessagingException {
            super(message);
            this.session = session;
            this.messageId = messageId;
        }

        protected void updateMessageID() throws MessagingException {
            if (messageId != null)
                setHeader("Message-ID", messageId);
        }
    }

}