All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.notima.generic.pgp.PGPEmailMessageSender Maven / Gradle / Ivy

The newest version!
package org.notima.generic.pgp;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.Transport;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import javax.activation.DataHandler;
import javax.activation.DataSource;

import org.notima.businessobjects.adapter.tools.exception.MessageSenderException;
import org.notima.generic.businessobjects.Message;
import org.notima.generic.businessobjects.exception.KeyNotFoundException;
import org.notima.generic.ifacebusinessobjects.KeyManager;

import me.sniggle.pgp.crypt.PGPMessageEncryptor;
import me.sniggle.pgp.crypt.PGPMessageSigner;

public class PGPEmailMessageSender extends EmailMessageSender {

    /**
     * Send the email to the recipient
     * @param message
     * The message to be sent
     * @param keyManager
     * the keyManager to retrieve public recipient keys from if no keys are provided in the message
     * @param attachSenderPublicKey
     * If set to true, The public sender key will be sent as an attachment.
     * @throws IOException 
     */
    public void send(Message message, KeyManager keyManager, boolean attachSenderPublicKey) throws MessageSenderException, IOException {
    	this.keyManager = keyManager;
    	this.attachPublicKey = attachSenderPublicKey;
    	
        MimeMultipart emailContent = new MimeMultipart();
        MimeBodyPart messageBodyPart = new MimeBodyPart();
        try {
        	initMessageToSend(message);
        	
        	if (!message.isEncrypted() && !message.isSigned()) {
        		messageBodyPart.setContent(message.getBody(), message.getContentType());
                emailContent.addBodyPart(messageBodyPart);
                theMessageToSend.setContent(emailContent);


        	} else if (message.isEncrypted() && !message.isSigned()){
        		messageBodyPart.setContent(encryptMessageBody(message), message.getContentType());
                emailContent.addBodyPart(messageBodyPart);
                theMessageToSend.setContent(emailContent);
                
        	} else if (message.isSigned() && !message.isEncrypted()) {
        		
        		MimeMultipart contentToSign = prepareForSigning(message, theMessageToSend);
        		emailContent = signMessage(theMessageToSend, contentToSign);
        		
        	} else if (message.isSigned() && message.isEncrypted()) {
        		
        		MimeMultipart signedContent = signMessageBody(message);
        		if (attachPublicKey) {
        			attachSenderPublicKey(signedContent);
        		}
        		theMessageToSend.setContent(signedContent);
        		MimeBodyPart encryptedPart = encryptMessage(message, theMessageToSend);
            	MimeBodyPart controlPart = new MimeBodyPart();
            	controlPart.setText("Version: 1");
            	controlPart.setHeader("Content-Type", "application/pgp-encrypted");

            	theMessageToSend = new MimeMessage(this.getMailSession());
            	initMessageToSend(message);
            	
                // Create a Multipart to hold the message parts
                MimeMultipart encryptedMultipart = new MimeMultipart();
                encryptedMultipart.setSubType("encrypted; protocol=\"application/pgp-encrypted\"; micalg=pgp-sha256");
            	encryptedMultipart.addBodyPart(controlPart);
            	encryptedMultipart.addBodyPart(encryptedPart);
            	emailContent = encryptedMultipart;
                theMessageToSend.setContent(emailContent);
        		
        	}

            if(message.getAttachemnts() != null){
                for(File attachment : message.getAttachemnts()){
                    attachFile(emailContent, attachment);
                }
            }

            Transport.send(theMessageToSend); 
        } catch (MessagingException e) {
            throw new MessageSenderException("Failed to send email message", e);
        }
    }


    /**
     * Encrypt a message using provided recipient public key
     * or a key retrieved from the key manager.
     * @param message
     * @return
     * @throws MessageSenderException
     * @throws KeyNotFoundException
     */
    private String encryptMessageBody(Message message) throws MessageSenderException {
        PGPMessageEncryptor encryptor = new PGPMessageEncryptor();

        ByteArrayInputStream bodyIS = new ByteArrayInputStream(message.getBody().getBytes());
        ByteArrayOutputStream bodyOS = new ByteArrayOutputStream();

        encryptor.encrypt(
            getRecipientPublicKeyInputStream(message), 
            message.getSubject(), 
            bodyIS, 
            bodyOS);

        return bodyOS.toString();
    }

    private MimeBodyPart encryptMessage(Message canonicalMessage, MimeMessage mimeMessage) throws IOException, MessagingException, MessageSenderException {
    	
    	ByteArrayOutputStream mimeMessageBytes = new ByteArrayOutputStream();
    	mimeMessage.writeTo(mimeMessageBytes);
    	ByteArrayOutputStream cleansedMessage = removeLinesBeforeContentType(mimeMessageBytes);
    	
    	String encryptedMessage = encryptMessageBody(
    			canonicalMessage.getSubject(), 
    			cleansedMessage.toString(), 
    			canonicalMessage.getRecipientPublicKey());
    	
    	MimeBodyPart encryptedPart = new MimeBodyPart();
    	
        ByteArrayDataSource ds = new ByteArrayDataSource(encryptedMessage.getBytes(), "application/octet-stream");
    	encryptedPart.setDataHandler(new DataHandler(ds));    	
        encryptedPart.setHeader("Content-Type", "application/pgp-encrypted");
        encryptedPart.setHeader("Content-Disposition", "attachment; filename=\"encrypted.asc\"");

    	return encryptedPart;
    	
    }
    
    private MimeMultipart signMessage(MimeMessage mimeMessage, MimeMultipart contentToAddsignatureTo) throws IOException, MessagingException, MessageSenderException {
    	
    	ByteArrayOutputStream mimeMessageBytes = new ByteArrayOutputStream();
    	Enumeration
headers = mimeMessage.getAllHeaders(); List
headerList = new ArrayList
(); for (Header h = headers.nextElement() ; headers.hasMoreElements() ; ) { headerList.add(h); } mimeMessage.writeTo(mimeMessageBytes); ByteArrayOutputStream cleansedMessage = removeLinesBeforeContentType(mimeMessageBytes); // String withProtectedHeaders = addProtectedHeaders(mimeMessage, cleansedMessage.toString()); String signature = signContent(mimeMessageBytes.toString()); MimeBodyPart signedPart = new MimeBodyPart(); DataSource ds = new ByteArrayDataSource(signature.getBytes(), "application/pgp-signature"); signedPart.setDataHandler(new DataHandler(ds)); signedPart.setHeader("Content-Type", "application/pgp-signature; name=\"signature.asc\""); signedPart.setHeader("Content-Disposition", "inline; filename=\"signature.asc\""); contentToAddsignatureTo.addBodyPart(signedPart); return contentToAddsignatureTo; } private String getProtectedHeaders(MimeMessage mimeMessage) throws MessagingException { String boundary = "----protectedHeaders"; // Create a string to insert String result = "Content-Type: multipart/mixed; boundary=\"" + boundary + "\"; protected-headers=\"v1\"\n"; result += getProtectedHeadersAsString(mimeMessage); result += "\n" + boundary + "\n\n"; return result; } private String getProtectedHeadersAsString(MimeMessage mimeMessage) throws MessagingException { String result = "From: " + mimeMessage.getHeader("From", null) + "\n"; result += "To: " + mimeMessage.getHeader("To", null) + "\n"; result += "Subject: " + mimeMessage.getHeader("Subject", null) + "\n\n"; return result; } private String addProtectedHeaders(MimeMessage mimeMessage, String content) throws MessagingException { return insertBeforeSecondContentType(content, getProtectedHeaders(mimeMessage)); } public static String insertBeforeSecondContentType(String input, String stringToInsert) { String searchStr = "Content-Type"; int firstIndex = input.indexOf(searchStr); // Check if the first occurrence exists if (firstIndex == -1) { return input; // No "Content-Type" found } // Find the second occurrence of "Content-Type" int secondIndex = input.indexOf(searchStr, firstIndex + searchStr.length()); // Check if the second occurrence exists if (secondIndex == -1) { return input; // Only one "Content-Type" found, no insertion needed } // Insert the new string before the second "Content-Type" StringBuilder result = new StringBuilder(input); result.insert(secondIndex, stringToInsert); return result.toString(); } /** * Encrypt a message using provided recipient public key * or a key retrieved from the key manager. * @param message * @return * @throws MessageSenderException * @throws FileNotFoundException * @throws KeyNotFoundException */ private String encryptMessageBody(String messageSubject, String encryptString, File publicKeyFile) throws MessageSenderException, FileNotFoundException { PGPMessageEncryptor encryptor = new PGPMessageEncryptor(); ByteArrayInputStream bodyIS = new ByteArrayInputStream(encryptString.getBytes()); ByteArrayOutputStream bodyOS = new ByteArrayOutputStream(); encryptor.encrypt( new FileInputStream(publicKeyFile), messageSubject, bodyIS, bodyOS); return bodyOS.toString(); } private MimeMultipart prepareForSigning(Message message, MimeMessage mimeMessage) throws MessagingException { // Create a Multipart to hold the message parts MimeMultipart multipartForSigning = new MimeMultipart(); multipartForSigning.setSubType("signed; protocol=\"application/pgp-signature\"; micalg=pgp-sha1"); // multipartForSigning.setPreamble(getProtectedHeadersAsString(mimeMessage)); MimeBodyPart theActualMessage = new MimeBodyPart(); theActualMessage.setContent(message.getBody(), message.getContentType()); multipartForSigning.addBodyPart(theActualMessage); mimeMessage.setContent(multipartForSigning); return multipartForSigning; } private String signContent(String contentToSign) throws MessageSenderException { ByteArrayInputStream messageIn = new ByteArrayInputStream(contentToSign.getBytes()); ByteArrayOutputStream signedMessageOut = new ByteArrayOutputStream(); PGPMessageSigner signer = new PGPMessageSigner(); signer.signMessage( getSenderPrivateKeyInputStream(), getEmailUser(), senderPrivateKeyPassword, messageIn, signedMessageOut); return signedMessageOut.toString(); } /** * Sign a message using provided sender private key * or a key retrieved from the key manager. * @param message * @return * @throws MessageSenderException * @throws MessagingException * @throws IOException * @throws KeyNotFoundException */ private MimeMultipart signMessageBody(Message message) throws MessageSenderException, MessagingException, IOException { MimeBodyPart theActualMessage = new MimeBodyPart(); theActualMessage.setContent(message.getBody(), message.getContentType()); MimeMultipart outerMultipart = new MimeMultipart("mixed"); outerMultipart.addBodyPart(theActualMessage); theMessageToSend.setContent(outerMultipart); PGPMessageSigner signer = new PGPMessageSigner(); ByteArrayOutputStream messageToSign = new ByteArrayOutputStream(); outerMultipart.writeTo(messageToSign); ByteArrayInputStream messageIn = new ByteArrayInputStream(messageToSign.toByteArray()); ByteArrayOutputStream signedMessageOut = new ByteArrayOutputStream(); signer.signMessage( getSenderPrivateKeyInputStream(), getEmailUser(), senderPrivateKeyPassword, messageIn, signedMessageOut); MimeBodyPart signedPart = new MimeBodyPart(); DataSource ds = new ByteArrayDataSource(signedMessageOut.toByteArray(), "application/pgp-signature"); signedPart.setDataHandler(new DataHandler(ds)); signedPart.setHeader("Content-Type", "application/pgp-signature; name=\"signature.asc\""); signedPart.setHeader("Content-Disposition", "inline; filename=\"signature.asc\""); // Create a Multipart to hold the message parts MimeMultipart multipart = new MimeMultipart(); multipart.setSubType("signed; protocol=\"application/pgp-signature\"; micalg=pgp-sha256"); multipart.addBodyPart(theActualMessage); multipart.addBodyPart(signedPart); return multipart; } /** * Attach the public key of the sender as a file attachemnt in * order for the recipient to be able to verify signatures and * send encrypted replies. * @throws MessageSenderException * @throws IOException * @throws MessagingException */ private void attachSenderPublicKey(MimeMultipart emailContent) throws MessageSenderException, IOException, MessagingException { MimeBodyPart attachmentBodyPart = new MimeBodyPart(); attachmentBodyPart.attachFile(senderPublicKey); attachmentBodyPart.addHeader("Content-Type", "application/pgp-keys; name=\"" + senderPublicKey.getName() + "\""); emailContent.addBodyPart(attachmentBodyPart); } /** * Add a file attachment body part to the email. * @param attachment file to attach * @throws MessageSenderException * @throws MessagingException * @throws IOException */ private void attachFile(MimeMultipart emailContent, File attachment) throws MessageSenderException { MimeBodyPart attachmentBodyPart = new MimeBodyPart(); if(attachment == null) throw new MessageSenderException("Tried to attach null"); try { attachmentBodyPart.attachFile(attachment); emailContent.addBodyPart(attachmentBodyPart); } catch (Exception e) { throw new MessageSenderException(String.format("Could not attach %s to the email.", attachment.getAbsolutePath())); } } /** * Strip the first headers * * @param input * @return * @throws IOException */ private ByteArrayOutputStream removeLinesBeforeContentType(ByteArrayOutputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); boolean foundContentType = false; // Convert ByteArrayOutputStream to BufferedReader to read line by line BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(input.toByteArray()))); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output)); String line; while ((line = reader.readLine()) != null) { if (!foundContentType) { if (line.toLowerCase().startsWith("content-type")) { foundContentType = true; } else { // Skip lines before the first occurrence of "Content-Type" continue; } } // Write the remainder to the output stream writer.write(line); writer.newLine(); } writer.flush(); // Ensure all data is written to the output return output; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy