Received fatal alert: bad_certificate when connecting to MySQL >= 8.0.19

Viewed 93

I try to connect to MySQL instances running in docker via the MySQL JDBC Driver (version 8.0.27) with TLS. While my setup works for all versions prior to 8.0.19, the connection fails for every newer version with the following Exception:

Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate at 
    java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) at 
    java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)

I created the certificates in accordance to the official documentation:

# Create CA certificate
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3600 \
-key ca-key.pem -out ca.pem

# Create server certificate, remove passphrase, and sign it
# server-cert.pem = public key, server-key.pem = private key
openssl req -newkey rsa:2048 -days 3600 \
-nodes -keyout server-key.pem -out server-req.pem
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3600 \
-CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem

# Create client certificate, remove passphrase, and sign it
# client-cert.pem = public key, client-key.pem = private key
openssl req -newkey rsa:2048 -days 3600 \
-nodes -keyout client-key.pem -out client-req.pem
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3600 \
-CA ca.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem

openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -out keystore.p12 -name test -CAfile ca.pem -caname root

keytool -importkeystore -deststorepass changeme -destkeypass changeme -destkeystore keystore.jks -srckeystore keystore.p12 -srcstoretype PKCS12 -srcstorepass "" -alias test

I created a setup to verify that the problems indeed only happens with MySQL versions >=8.0.19:

public class MySQLTest {

    private @Nullable MySQLContainerSSL mySQLContainerSSL;

    @AfterEach
    void tearDown() {
        if (mySQLContainerSSL != null) {
            mySQLContainerSSL.stop();
        }
    }

    @ParameterizedTest
    @ValueSource(strings = {
            "8.0.0",
            "8.0.18",
            "8.0.19",
            "8.0.27"
    })
    void test(final @NotNull String version) throws Exception {

        Class.forName("com.mysql.jdbc.Driver");

        mySQLContainerSSL = new MySQLContainerSSL(version);
        mySQLContainerSSL.start();

        final Properties properties = new Properties();
        properties.put("sslMode", "REQUIRED");
        properties.put("useSSL", "true");
        properties.put("user", mySQLContainerSSL.getPassword());
        properties.put("password", mySQLContainerSSL.getUsername());
        properties.put("clientCertificateKeyStoreUrl", new File("src/test/resources/mysql/keystore.jks").toURI().toURL().toString());
        properties.put("clientCertificateKeyStorePassword", "changeme");

        final Connection con = DriverManager.getConnection(mySQLContainerSSL.getJdbcUrl(), properties);

        assertNotNull(con);
        assertFalse(con.isClosed());
    }


    public static class MySQLContainerSSL extends MySQLContainer<MySQLContainerSSL> {

        public MySQLContainerSSL(final @NotNull String version) {
            super(getAdHocImageName(version));
        }

        @Override
        public void stop() {
            super.stop();
            final String dockerImageName = getDockerImageName();
            DockerClientFactory.instance().client().removeImageCmd(dockerImageName).withForce(true).exec();
        }

        public static @NotNull DockerImageName getAdHocImageName(final @NotNull String mysqlVersion) {
            final String s = new ImageFromDockerfile().withDockerfile(Path.of("src/test/resources/mysql/Dockerfile"))
                    .withBuildArg("MYSQL_VERSION", mysqlVersion)
                    .get();
            return DockerImageName.parse(s).asCompatibleSubstituteFor("mysql");
        }
    }
}

The Dockerfile:

ARG MYSQL_VERSION
FROM mysql:${MYSQL_VERSION}

COPY server-key.pem /var/ssl/server-key.pem
COPY server-cert.pem /var/ssl/server-cert.pem
COPY ca.pem /var/ssl/ca/ca.pem
COPY my.cnf /etc/mysql/my.cnf
COPY my.cnf /var/lib/mysql-files/my.cnf

RUN chown mysql:mysql /etc/mysql/my.cnf \
                     /var/ssl/ \
                    /var/ssl/server-key.pem \
                    /var/ssl/ca/ca.pem \
                    /var/ssl/server-cert.pem && \
    chmod 644 /var/ssl/server-key.pem \
                /var/ssl/server-cert.pem \
                /var/ssl/ca/ca.pem

my.cnf:

[mysqld]
ssl_key=/var/ssl/server-key.pem
ssl_cert=/var/ssl/server-cert.pem
ssl_ca=/var/ssl/ca/ca.pem

My certificates seem to be correct, as they work for older versions, or is there a mistake I did not spot? Is there a behaviour change from 8.0.18 to 8.0.19 that I am not aware of? Is there something else that I am missing?

0 Answers
Related