Bug01_ In server environment, mail sending timeout & javax net. ssl. SSLHandshakeException: No appropriate protocol

Posted by IronCannibal on Wed, 19 Jan 2022 11:10:06 +0100

1, Bug1: mail sending timeout

Bug1 Description:

My Java project needs to send mail when users register and activate. In the development environment, localhost can send mail through port 25; When the project is deployed to the server of centos system in the production environment, the mail sending connection timeout occurs.

Bug1 processing:

At first, I thought the following log information was an error message:

DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.163.com", port 465, isSSL false

Later, it was found that this log information is also available if mail sending is successfully implemented in the development environment.
After investigation, Alibaba cloud server has closed port 25 and cannot send data through this port. You need to submit an application to Alibaba cloud for unsealing. Another solution is to change the port where the server sends data to 465. In order to modify the port for the purpose of sending mail, my updated code is as follows:
[1] The relevant profile information is as follows:

spring.mail.protocol=smtp
spring.mail.host=smtp.163.com
spring.mail.port=465
spring.mail.username=salieri97@163.com
spring.mail.password=MSWLTEBIGNKFXQVW
spring.mail.default-encoding=utf-8
spring.mail.properties.mail.debug=true
spring.mail.sendAddressId=http://120.79.133.235:8989/salieri/user/activation

[2] For the purpose of registered account activation, the mail service module is as follows:

package com.salieri.service.serviceImpl;

import com.salieri.service.MailService;
import com.salieri.utils.TrustAllTrustManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.net.ssl.*;
import java.io.UnsupportedEncodingException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Properties;

// Netease mailbox uses port 465 to send mail. The default port 25 is blocked on Alibaba cloud server
@Service
@Transactional
public class MailServiceImpl implements MailService {

    // Sender's email account
    @Value("${spring.mail.username}")
    private String myEmailAccount;

    // Client authorization password
    @Value("${spring.mail.password}")
    private String myEmailPassword;

    // SMTP server address of Netease 163 mailbox
    @Value("${spring.mail.host}")
    private String myEmailSMTPHost;

    private static final String title = "Activate mail";


    // Activate account email and send
    @Override
    public void sendMailForActivationAccount(String activationUrl, String name) throws KeyManagementException, NoSuchAlgorithmException {  // activationUrl is the mail sending content, and name is the recipient mailbox name


        //  Direct host authentication
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                return true;
            }
        };
        //  Configure authentication manager
        javax.net.ssl.TrustManager[] trustAllCerts = {new TrustAllTrustManager()};
        SSLContext sc = SSLContext.getInstance("SSL");
        SSLSessionContext sslsc = sc.getServerSessionContext();
        sslsc.setSessionTimeout(0);
        sc.init(null, trustAllCerts, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());


        String emailMsg = "Click the link:"+activationUrl+" Account activation--EasyBuy";

        // 1. Create a parameter configuration for connecting to the mail server
        // Parameter configuration
        Properties props = new Properties();
        // Protocol used (JavaMail specification requirements)
        props.setProperty("mail.transport.protocol", "smtp");
        // SMTP server address of sender's mailbox
        props.setProperty("mail.smtp.host", myEmailSMTPHost);
        // Require authentication
        props.setProperty("mail.smtp.auth", "true");

        // Port of SSLSocketFactory class
        props.put("mail.smtp.socketFactory.port", "465");
        // SSLSocketFactory class
        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        props.put("mail.smtp.auth", "true");
        // The ssl encryption port provided by Netease, and the QQ mailbox is also this port
        props.put("mail.smtp.port", "465");

        // 2. Create a session object according to the configuration to interact with the mail server
        Session session = Session.getDefaultInstance(props);
        // Set the debug mode to view the detailed sending log
        session.setDebug(true);

        // 1. Create a message
        MimeMessage message = new MimeMessage(session);
        try {
            // 2. From: sender
            message.setFrom(new InternetAddress(myEmailAccount, title, "UTF-8"));
            // 3. To: recipient (multiple recipients, CC and BCC can be added)
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(name, "Personal website", "UTF-8"));
            // 4. Subject: email subject
            message.setSubject(title, "UTF-8");
            // 5. Content: message body (html tag can be used)
            message.setContent(emailMsg, "text/html;charset=UTF-8");
            // 6. Set sending time
            message.setSentDate(new Date());
            // 7. Save settings
            message.saveChanges();

            // 4. Get the mail transfer object according to the Session
            Transport transport = session.getTransport();
            // 5. Use the email account and password to connect to the mail server. The authenticated mailbox here must be consistent with the sender's mailbox in message, otherwise an error will be reported
            transport.connect(myEmailAccount, myEmailPassword);
            // 6. Send mail to all receiving addresses, message Getallrecipients() gets all the recipients, CC and BCC added when creating the mail object
            transport.sendMessage(message, message.getAllRecipients());
            // 7. Close the connection
            transport.close();

        } catch (MessagingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    }
}

[3] TrustAllTrustManager tool class code is as follows:

package com.salieri.utils;

public class TrustAllTrustManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager {

    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
    }

    public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
        return true;
    }

    public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
        return true;
    }

    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
            throws java.security.cert.CertificateException {
        return;
    }

    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
            throws java.security.cert.CertificateException {
        return;
    }

}

2, Bug2: javax net. ssl. SSLHandshakeException: No appropriate protocol

Bug2 Description:

After Bug1 is solved, when the client front-end Vue cli program calls the server-side Java back-end mail interface, the mail sending fails again (it passed the local test). Check the server-side SringBoot output log and find javax net. ssl. Sslhandshakeexception: no approve protocol error.

Bug2 processing:

Through investigation, it is found that it is a certificate problem, so the basis is Solution 1 Modify server-side openjdk1 8 configuration. Because I deployed through Docker on the server side, I modified the jdk configuration file when the container was running. The command line execution process is as follows:

docker ps
docker inspect 588cb39e4b9b   # 588cb39e4b9b is jdk1 8, this command is to view jdk1 8 internal profile location
docker exec -it 588cb39e4b9b bash  # Enter jdk1 8 container
cd /usr/local/openjdk-8/lib/security
apt-get update
apt-get install vim
vim java.security

Modified local reference Solution 1 Then restart the jar package of springboot + JDK 18. The local front end calls the server back end interface to send mail successfully.

Topics: Java