Search code examples
javasslglassfishjakarta-mailantivirus

Glassfish, Java email and certificate


I've search far and long and can't find the answer.

I'm using Glassfish and want to send an email via our smtp server.

I've obtained the server's certificate:

openssl s_client -connect mail.example.com:587 -starttls smtp > our.cer

I've cleaned up the cer file to only contain the certificate data.
I've import it everywhere:

keytool -import -alias mail.example.com -file our.cer -keystore c:\Progra~1\Java\jre7\lib\security\cacerts
keytool -import -alias mail.example.com -file our.cer -keystore c:\Progra~1\Java\JDK17~1.0_4\jre\lib\security\cacerts
keytool -import -alias mail.example.com -file our.cer -keystore c:\PROGRA~1\GLASSF~1.0\GLASSF~1\domains\domain1\config\cacerts.jks

But I get the following error:

javax.mail.MessagingException: Could not convert socket to TLS;
    nested exception is:
        javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    ...
    Caused by...
    ...
    Caused by...
    ...
    Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

My code snippet:

...
Properties properties = System.getProperties();
properties.put("mail.smtp.starttls.enable", "true");
properties.put("mail.smtp.host", "mail.example.com");
properties.put("mail.smtp.user", "[email protected]");
properties.put("mail.smtp.password", "some.password");
properties.put("mail.smtp.port", "587"));
properties.put("mail.smtp.auth", "true"));
properties.put("mail.smtp.from", "[email protected]"));
properties.put("mail.transport", "smtp"));

...

Authenticator mailAuthenticator = new Authenticator()
{
    @Override
    protected PasswordAuthentication getPasswordAuthentication()
    {
        return new PasswordAuthentication(properties.getProperty("mail.smtp.user"),
                        properties.getProperty("mail.smtp.password"));
    }
};
try
{
    // Get the default Session object.
    Session session = Session.getDefaultInstance(properties, mailAuthenticator);
    MimeMessage mimeMessage = new MimeMessage(session);
    mimeMessage.setSubject("Hallo world!");
    mimeMessage.setFrom(new InternetAddress(properties.getProperty("mail.smtp.from")));
    mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]"));
    mimeMessage.setText("Some text message...");

    Transport transport = session.getTransport(properties.getProperty("mail.transport"));//"smtp"

    int port = Integer.parseInt(properties.getProperty("mail.smtp.port"));//587
    transport.connect(properties.getProperty("mail.smtp.host"),//"mail.example.com"
                    port,
                    properties.getProperty("mail.smtp.user"),//"[email protected]"
                    properties.getProperty("mail.smtp.password"));//Clear text password
    transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
    LOG.info("...done sending email");
}
catch (AddressException e)
{
    LOG.log(Level.SEVERE, "Error while sending email", e);
}
catch (MessagingException e)
{
    LOG.log(Level.SEVERE, "Error while sending email", e);
}
catch (Exception e)
{
    LOG.log(Level.SEVERE, "Error while sending email", e);
}

I've also tried Mkyong's suggestion to run InstallCert (source) against the mail server but I get:

javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?

UPDATE 1

I've loaded the certificate in the following additional places:

keytool -import -alias mail.example.com -file our.cer -keystore c:\PROGRA~1\GLASSF~1.0\GLASSF~1\domains\domain1\config\keystore.jks
keytool -import -alias mail.example.com -file our.cer -keystore c:\GLASSFISH\config\keystore.jks
keytool -import -alias mail.example.com -file our.cer -keystore c:\GLASSFISH\config\cacerts.jks

I've also set these properties:

System.setProperty("javax.net.ssl.keyStore", "c:\\GLASSFISH\\config\\keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
System.setProperty("javax.net.ssl.trustStore", "c:\\GLASSFISH\\config\\cacerts.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

I've also switched on debugging for ssl (Glassfish console, server, properties) javax.net.debug=ssl.

It works in a standalone test application (Java SE) but not in Glassfish.

UPDATE 2

Using the javax.net.debug=ssl and examining the log out put I can see that even-though I've loaded the certificate "everywhere" or if I point Glassfish to the java keystore, Glassfish is missing this certificate. The stand alone test app has it:

adding as trusted cert:
    Subject: CN=avast! Web/Mail Shield Root, O=avast! Web/Mail Shield, OU=generated by avast! antivirus for SSL/TLS scanning
    Issuer:  CN=avast! Web/Mail Shield Root, O=avast! Web/Mail Shield, OU=generated by avast! antivirus for SSL/TLS scanning
    Algorithm: RSA; Serial number: 0x14128fa09c50b64ba6d5c99875872673
    Valid from Wed Feb 04 08:56:17 CAT 2015 until Sat Feb 01 08:56:17 CAT 2025

(Note the Serial number...)

It seems to be the certificate missing from Glassfish; because the stand alone test app finds it:

Found trusted certificate:
[
[
    Version: V3
    Subject: CN=avast! Web/Mail Shield Root, O=avast! Web/Mail Shield, OU=generated by avast! antivirus for SSL/TLS scanning
    Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

    Key:  Sun RSA public key, 2048 bits
    modulus: ---8<---
    public exponent: 65537
    Validity: [From: Wed Feb 04 08:56:17 CAT 2015, : Sat Feb 01 08:56:17 CAT 2025]
    Issuer: CN=avast! Web/Mail Shield Root, O=avast! Web/Mail Shield, OU=generated by avast! antivirus for SSL/TLS scanning
    SerialNumber: [    14128fa0 9c50b64b a6d5c998 75872673]

    ...

(Look at the SerialNumber...)

Please help


Solution

  • Okey dokey! I've figured it out. Here is how my 5 day journey ended today, in brief...

    First I dumped all the certificates from the Java keystore, as it was working using that keystore:

    keytool -list -v -keystore c:\Progra~1\Java\jre7\lib\security\cacerts > allJavaCerts.txt
    

    Then I dumped all the certificates from the Glassfish keystore, to see if the certificate was correctly imported:

    keytool -list -v -keystore c:\WORKSP~1\GLASS\config\cacerts.jks > allGFCerts.txt
    

    In the Java keystore list I notice that the mail server's certificate was issued by Avast... weird, right?

    Issuer: CN=avast! Web/Mail Shield Root, O=avast! Web/Mail Shield, OU=generated by avast! antivirus for SSL/TLS scanning
    

    And in the Java keystore list there is a certificate for Alias name: avastsslscannerroot but not in the Glassfish keystore list - Thank you Beyond Compare v3

    So I exported the avastsslscannerroot certificate from the Java keystore and import it into the Glassfish keystore:

    keytool -export -keystore c:\Progra~1\Java\jre7\lib\security\cacerts -alias avastsslscannerroot -file avastsslscannerroot.cer
    keytool -import -alias avastsslscannerroot -file avastsslscannerroot.cer -keystore c:\WORKSP~1\GLASS\config\keystore.jks
    keytool -import -alias avastsslscannerroot -file avastsslscannerroot.cer -keystore c:\WORKSP~1\GLASS\config\cacerts.jks
    

    And now it works...

    And then it didn't on the server... After some search I came across this:

    mail.smtp.ssl.trust="*"
    

    Property and it works for us as we are connection from our web server to our mail server...