Published on

Sending PEC automatically with Java

Authors

I hate PEC. I hate it with all my heart, really. And yet it's the only way to send registered mail with a digital delivery receipt in Italy (and only in Italy, who knows why). For those of you lucky enough not to know it, PEC (Posta Elettronica Certificata) is the Italian "certified email": an email with legal value. It makes no sense, it was designed by people who have never seen a single Hello World in their lives, it's really not secure, and if it were up to me I would never ever use it.

But I'm not here to complain and write a never-ending post explaining all the infinite reasons why it sucks, because I know it wouldn't change anything. Instead, I'm here to tell you how to send PEC programmatically through a "simple" application written in Java.

But first, a couple of important questions.

Does it actually make sense for your business case?

First of all, let me put my hands up: automating PEC sending is not a good idea. Absolutely not. I wouldn't do it. And yet there are cases where it can be useful. I'll leave it to you to decide whether you really need to implement this system or not. Think about it carefully.

But aren't there already tutorials or code examples online?

Since PEC only exists in Italy, there are no even remotely decent tutorials or examples online on how to automate sending it (actually there are, but they're written terribly and just looking at them makes me wonder what the average quality of programmers in Italy actually is). So here I am.

Enough talk, show me the code!

First of all we need a class that properly describes every aspect of the PEC message we want to send. It's pretty simple:

public class MailModel {
        private String html; // Message body
        private String from; // Sender's email
        private String sender; // Sender's full name
        private String replyTo;
        private List<Recipient> recipients;
        private List<Attachment> attachments;

        public static class Recipient {
                private String email;
                private String name; // Full name
                private String subject;
        }

        public static class Attachment {
                private String url;
                private String fileName;
                private String description;
        }
}

I left out all the getters, setters and constructors for brevity (otherwise the class would have been a gazillion lines long, something I deeply hate about Java, a problem Kotlin solves elegantly (learn Kotlin, I beg you!!!). I'm sure you're more than capable of writing them yourselves (otherwise you really shouldn't be here, seriously, at least learn the basics of Java before you keep programming).

The next step is implementing, in a proper MailService, the sendMailPEC method, which takes a single MailModel as a parameter and takes care of sending all the emails. But how exactly does it work?

Simple: through the PEC provider (in our case it will be Aruba, yuck) we'll use the credentials of an already existing account to send the emails as if we were sending them directly from Aruba's official client (yep, this is exactly what happens behind the scenes at Aruba when we send a PEC).

That's right: the account password will be a plain text property of the application. It's important to keep it secret, so my advice is to not check the secret configuration files into Git, or more simply to work on a private repo on Bitbucket.

Needless to say, if your provider isn't Aruba you'll have to change the values of pec.hostname and, if needed, pec.port too.

Since I work with Spring, I'll use its configuration system like this:

pec.sender_mail = EMAIL_PEC
pec.hostname = smtps.pec.aruba.it
pec.port = 465
pec.auth.username = EMAIL_PEC
pec.auth.password = PASSWORD

Instead of EMAIL_PEC write the PEC address you want to use to send the messages, and instead of PASSWORD, obviously, your secret password. Without credentials, you can't connect to the sending system! (Aruba, why the hell didn't you implement a system of revocable API keys? Do I have to tell you everything...)

Now let's get to the meat of the code, which is the implementation of sendMailPEC in MailService. As usual I cut out the extra code we don't need:

import java.util.*;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service("MailService")
@PropertySource(value = "classpath:application.properties")
public class MailServiceImpl implements MailService {
        private Logger logger = LoggerFactory.getLogger(MailServiceImpl.class);
        private static String logPattern = "[MAIL Service]";

        @Value("${pec.sender_mail}")
        @NotNull
        private String pecSenderMail;

        @Value("${pec.hostname}")
        @NotNull
        private String pecHostName;

        @Value("${pec.port}")
        @NotNull
        private int pecPort;

        @Value("${pec.auth.username}")
        @NotNull
        private String pecAuthUsername;

        @Value("${pec.auth.password}")
        @NotNull
        private String pecAuthPassword;

        @Override
        public void sendMailPEC(MailModel mailModel) {
                logger.info("{} - Sending PEC mail to {} recipients", logPattern, mailModel.getRecipients().size());

                // Prepare system properties
                Properties props = System.getProperties();
                props.setProperty("mail.smtp.ssl.enable", "true");
                props.setProperty("mail.smtp.ssl.socketFactory.class", CustomSSLSocketFactory.class.getName());
                props.setProperty("mail.smtp.ssl.socketFactory.fallback", "false");

                mailModel.getRecipients().forEach(recipient -> {
                        try {
                // Prepare email
                HtmlEmail email = new HtmlEmail();

                email.setHostName(pecHostName);
                email.setSmtpPort(pecPort);
                email.setAuthentication(pecAuthUsername, pecAuthPassword);
                email.setSSL(true);

                email.setFrom(pecSenderMail);
                email.setHtmlMsg(mailModel.getHtml());
                email.setSubject(recipient.getSubject());
                email.addTo(recipient.getEmail(), recipient.getName());

                for (MailAttachment attachment : Optional.ofNullable(model.getAttachments()).orElse(Collections.emptyList())) {
                    EmailAttachment emailAttachment = new EmailAttachment();
                    emailAttachment.setDisposition(EmailAttachment.ATTACHMENT);
                    emailAttachment.setURL(new URL(attachment.getURL()));
                    emailAttachment.setName(attachment.getFileName());
                    emailAttachment.setDescription(attachment.getDescription());
                    email.attach(emailAttachment);
                }

                // Finally send email to recipient
                email.send();
            } catch (EmailException | MalformedURLException e) {
                logger.error("{} - Failed to send PEC mail! ({} error)", logPattern, e.getClass().getName());
                logger.error("{} - Error message: {}. Caused by: {}", logPattern, e.getMessage(), e.getCause());
            }
                });

                logger.info("{} - Sent PEC mails to {} recipients", logPattern, mailModel.getRecipients().size());
        }
}

The code is actually very simple. What should catch your attention are these lines:

// Prepare system properties
Properties props = System.getProperties();
props.setProperty("mail.smtp.ssl.enable", "true");
props.setProperty("mail.smtp.ssl.socketFactory.class", CustomSSLSocketFactory.class.getName());
props.setProperty("mail.smtp.ssl.socketFactory.fallback", "false");

What do they mean? You don't really need to know the details, I'd be really boring (and anyway you can read the online documentation of these particular classes), all you need to know is that if you leave these lines out nothing will work, because the SMTPS connection will be refused no matter what.

Essentially (and I'm simplifying a lot) the default factories are set up in a way that's too "restrictive" (secure, that is), so they won't let just any random asshole in. We need to make an important override of these factories, with our own personal implementations, with blackjack and hookers. In fact, forget the factories and the blackjack.

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

public class CustomSSLSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory factory;

    private CustomSSLSocketFactory() {
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[] { new CustomTrustManager() }, null);
            factory = sc.getSocketFactory();
        } catch (Exception ex) {
            // Ignore any exception thrown
        }
    }

    public static SocketFactory getDefault() {
        return new CustomSSLSocketFactory();
    }

    public Socket createSocket() throws IOException {
        return factory.createSocket();
    }

    public Socket createSocket(Socket socket, String s, int i, boolean flag) throws IOException {
        return factory.createSocket(socket, s, i, flag);
    }

    public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException {
        return factory.createSocket(inaddr, i, inaddr1, j);
    }

    public Socket createSocket(InetAddress inaddr, int i) throws IOException {
        return factory.createSocket(inaddr, i);
    }

    public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException {
        return factory.createSocket(s, i, inaddr, j);
    }

    public Socket createSocket(String s, int i) throws IOException {
        return factory.createSocket(s, i);
    }

    public String[] getDefaultCipherSuites() {
        return factory.getDefaultCipherSuites();
    }

    public String[] getSupportedCipherSuites() {
        return factory.getSupportedCipherSuites();
    }

}

And finally, let's implement CustomTrustManager

import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;

public class CustomTrustManager implements X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) {
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }

}

Essentially these new classes will allow pretty much any connection, also making MITM attacks possible, since the whole certificate system has officially gone to hell. So please, if you suffer one (I really hope not), don't blame me. You know what risks you're taking with this system.

Again, if only that fucking Aruba had a decent API system (and it doesn't) none of this would be necessary. You would just call their secure APIs with a token and off you go! Classic fucking Italian bodge jobs... and here I am, going along with it!

That's it?

Yes, that's all. This code will let you send PEC emails programmatically. Amazing, right? No, it isn't. This code opens up a serious vulnerability in your application, and if you're not willing to take the risk that someone unfortunately finds it, you have no other way to send PEC (yes, I'm dead serious!). Unless you want to spend 2000 euros a year chasing after MailUp... the choice is yours...

See you next time!