Setting up Gmail

All of the advise that most site provide is that you should allow less secure access to your Gmail account during the testing and then turn this setting off again afterwards.

Gmail 2-Step Verification

What I have found to work better is to enable 2-Step verification on your Gmail account and then to generate an App Password for the application instead of using the email account password.

App Password

Browse to the Sign-in and security section of the Gmail account and under the 2-Step Verification you’ll find the App Password section.

2017-05-03 Gmail Security Settings

After confirming your password, all the App Password are displayed and a link to generate a new App Password. The App Password will be used later instead of the email account password.

2017-05-03 Gmail App Password

Importing the Gmail certificate into the Java Keystore

I could only find two methods to retrieve the Google Gmail certificate, one being OpenSSL and the other being Python. Now my development machine is Windows and I did not want to go through the pain of setting up Cygwin only to execute one command so I instead opted for the Python approach. As a side note, I wanted to find a pure Java solution but to date have found none.

Python

import smtplib
import ssl

connection = smtplib.SMTP()
connection.connect('smtp.gmail.com')
connection.starttls()

f1=open('./gmail.pem', 'w+')
print >> f1, ssl.DER_cert_to_PEM_cert(connection.sock.getpeercert(binary_form=True))

The preceding code connects to smtp.gmail.com and saves the certificate in the PEM Base64 encoding.

OpenSSL

To be honest there are enough sources on the net on how to use OpenSSL to retrieve a certificate, so I will not polute the net with yet another.

Creating the certificate keystore

keytool -keystore cacerts.jks -importcert -alias gmail -file gmail.pem
keytool -list -keystore cacerts.jks

I guess the commands are pretty self explanatory, since the first performs the actual creation of the cacerts.jks file with the certificate gmail.pem being aliased by the name gmail. The second command lists the certificates within the cacerts.jks file.

Providing the SSL Context Parameters

Configuration

In the application.properties define the properties for the location and the password for the keystore. A neat feature of Spring Boot is that the password can be overwritten by providing a environment variable THREESIXTY_KEYSTORE_PASSWORD which will keep the actual password away from prying eyes or opt for HashCorp Vault to keep this.

# Keystore
threesixty.keystore.location=C:\\tmp\\cacerts.jks
threesixty.keystore.password=password

Bean

Next provide the creation of the SSLContextParameters object from the keystore location and password for Apache Camel.

@Bean
   public SSLContextParameters sslContextParameters(
          @Value("threesixty.keystore.location") final String location,
      @Value("threesixty.keystore.password") final String password) {

       KeyStoreParameters store = new KeyStoreParameters();
       store.setResource(location);
       store.setPassword(password);

       TrustManagersParameters trust = new TrustManagersParameters();
       trust.setKeyStore(store);

    SSLContextParameters parameters = new SSLContextParameters();
    parameters.setTrustManagers(trust);

    return parameters;
   }

Configuring a route to deliver a file to email

The route polls the directory for files and then enriches the exchange with a header field that will contain the attachment (i.e. the file itself) and then routes the file to the email smtps endpoint which will use the file as the body and as an attachment.

from("file:C://tmp/camel")
                 .routeId("DELIVER001")
                 .routeDescription("Deliver file to Gmail")
                 .bean(new AttachmentProcessor())
                 .to("smtps:smtp.gmail.com:465?username=<Gmail Email Address>&password=<App Password>&to=somebody@gmail.com&subject=Test&debugMode=true&mail.smtp.auth=true&mail.smtp.starttls.enable=true");

 

public class AttachmentProcessor implements Processor {
    @Handler
    public void process(Exchange exchange) {

        try {
            byte[] file = exchange.getIn().getBody(byte[].class);
            String fileName = exchange.getIn().getHeader("CamelFileName", String.class);
            String path = exchange.getIn().getHeader("CamelFileAbsolutePath", String.class);
            String mimeType = Files.probeContentType(Paths.get(path));

            exchange.getIn().setHeader("mimeType", mimeType);
            exchange.getIn().addAttachment(fileName, new DataHandler(new ByteArrayDataSource(file, mimeType)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Advertisements