Java tutorial
/** * 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); } } }