Spring Boot connection to AWS RDS MySQL - SSLHandshakeException: Received fatal alert: unknown_ca

user2512806 :

I have a Spring Boot application from which I am trying to connect to a MySQL on AWS RDS. However I am having issues with the following error:

localhost-startStop-1, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca

After enabling debug for the ssl handshake, I see that for the CertificateRequest step the Cert Authorities: are empty. Based on my understanding this is the part where the client (spring boot app) needs to present a cert to the server (mysql db).

  • Certificate Request step - The server will issue a certificate request from the client.
  • The next step would be *** Certificate chain, which is the certificate the client is sending to the server. In this case for me, it is sending the content of keyStore_cert.jks.

What I think the issue so far: The server (mySQL db) does not know about this certificate (my keyStore_cert.jks) that the client (my app) is sending. But, I was under the impression that the client certs are not required unless you set REQUIRE X509 for the user.

The questions:

  • Is there something else that I can look in order to find the exact problem?
  • If the above is the issues, how can I fix it? How can I disable client verification?
  • Or how can I keep client verification but have it work?
  • Do I need add anything to the keyStore_cert.jks/trustStore_cert.jks?


These are what my settings and what I have tried.

  • RDS Engine version: 5.7.22
  • mysql-connector-java v8.0.13
  • connection URL: jdbc:mysql://<host>:<port>/<db_name>?useLegacyDatetimeCode=false&verifyServerCertificate=true&useSSL=true&requireSSL=true
  • for the user that I am using I executed the following on the DB:
ALTER USER '<my_db_user>'@'%' require SSL;
GRANT USAGE ON <db_name>.* TO '<my_db_user>'@'%' REQUIRE SSL;
  • Based on the AWS documentation: I imported those certs (root and intermediate) into the trustStore. Doing something: keytool -import -keystore trustStore_cert.jks -storepass <trustStore_password> -alias "awsrds-us-east1" -file rds-ca-2015-us-east-1.pem
  • I use our own keyStore. Nothing was done to the keyStore at this time.
  • application trustStore and keyStore are passed as JVM param like the following:
-Djavax.net.ssl.keyStore=path/keyStore_cert.jks
-Djavax.net.ssl.keyStorePassword=<keyStore_password>
-Djavax.net.ssl.trustStore=path/trustStore_cert.jks
-Djavax.net.ssl.trustStorePassword=<trustStore_password>
  • For the specific user, when I use MySQL Workbench and in the connection setting for Use SSL I specify: If available or Require. It connects with this message:
Host: <host>
Port: <port>
User: <user>
SSL: enabled with DHE-RSA-AES128-SHA
  • However if I specify, Require and Verify CA or Require and Verify Identity it gives this message: SSL connection error: CA certificate is required if ssl-mode is VERIFY_CA or VERIFY_IDENTITY. Which makes sense since i am not specifying anything for the CA file.


Steps for the handshake (will omit some logs.):

*** ClientHello, TLSv1.1 (seems okay)
***
*** ServerHello, TLSv1.1 (seems okay)
***
*** Certificate chain (has the root key)
chain [0] = [
[
  Version: V3
  Subject: C=US, ST=Washington, L=Seattle, O=Amazon.com, OU=RDS, CN=**<my_rds_name>abcd.us-east-1.rds.amazonaws.com**
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
...
...
chain [1] = [ (has the us-east-1 key)
[
  Version: V3
  Subject: CN=Amazon RDS us-east-1 CA, OU=Amazon RDS, O="Amazon Web Services, Inc.", L=Seattle, ST=Washington, C=US
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
...
...
***
Found trusted certificate:
[
[
  Version: V3
  Subject: CN=Amazon RDS us-east-1 CA, OU=Amazon RDS, O="Amazon Web Services, Inc.", L=Seattle, ST=Washington, C=US
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
...
...
*** CertificateRequest (the Authorities are empty)
Cert Types: RSA, DSS, ECDSA
Cert Authorities:
<Empty>
*** ServerHelloDone
matching alias: <my keyStoreAlias>
*** Certificate chain (seems entries from my own key Store)
chain [0] = [
[
  Version: V3
  Subject: CN=<the CN on my keyStore>, OU=Web Servers, O=Company , C=US
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

***
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1.1 (seems okay)

*** CertificateVerify (seem okay)
*** Finished
verify_data:  { 50, 89, 33, 202, 193, 158, 226, 114, 128, 50, 198, 250 }
***
user2512806 :

After working with AWS RDS Support I got an explanation why the issue and how to fix it.

Just to clarify, this issue is mainly presented only if you pass your own keystore as part of JVM arguments. Something like this:

 -Djavax.net.ssl.keyStore=path/keyStore_cert.jks
 -Djavax.net.ssl.keyStorePassword=<keyStore_password>

RDS is a managed service and they do not have a way (currently) to load specific client certificates into the server. Meaning they can’t pass a specific certificates in DB configuration level. Usually this would be possible if you were to standup your own MY SQL server. In the config file for that server you can specify the client/server certs. So, RDS can’t verify the certificate provided by client.

If you were to pass a keystore, which has a key-pair entry (as JVM argument or otherwise), during SSL handshake, the client authentications step would fail. This is expected behavior in database. RDS can’t restrict itself to verify (or not verify) the file loaded in client field for the client during the initial connection. So, if a keystore is passed, server will try to match the certificate keys with existing CA file and if doesn’t match it will not allow the connections.

The solution is to either not pass a keystore at all or pass a blank keystore (one that does not have a key-pair with just Trusted Certificate Entry or one that is just blank).

  1. if you choose to not pass a keystore, then do not specify these properties -Djavax.net.ssl.keyStore= & -Djavax.net.ssl.keyStorePassword=. And construct the DB connection URL like this:
    -Ddb_jdbc_url=jdbc:mysql://<host>:<port>/<db_name>?useLegacyDatetimeCode=false&verifyServerCertificate=true&useSSL=true&requireSSL=true. You still need to provide the truststore. See at the bottom for more.

  2. Pass a black keystore or (or pass the trust store in the keystore field) You can pass what ever you want for the keystore and trustore JVM params. And for the URL you construct it like this:

    -Ddb_jdbc_url=jdbc:mysql://<host>:<port>/<db_name>?
    useLegacyDatetimeCode=false&verifyServerCertificate=true&useSSL=true&requireSSL=true
    &clientCertificateKeyStoreUrl=file:/user/documents/projects/trust-store-rds.jks
    &clientCertificateKeyStorePassword=<password>
    &clientCertificateKeyStoreType=JKS
    &trustCertificateKeyStoreUrl=file:/user/documents/projects/trust-store-rds.jks
    &trustCertificateKeyStorePassword=<password>
    &trustCertificateKeyStoreType=JKS

Note that for both trustCertificateKeyStoreUrl and clientCertificateKeyStoreUrl I am passing the same file.

Note, you need to also configure all the previous steps:

  1. enable SSL for a user on the DB.
CREATE USER 'my_user'@'%' IDENTIFIED BY 'my_password';
ALTER USER 'my_user'@'%' REQUIRE SSL;
GRANT USAGE ON *.* TO 'my_user'@'%' REQUIRE SSL ;

  1. You need to import the AWS Root and Regional certs into your truststore; Something like this:
     keytool -import -keystore trust-store-rds.jks -storepass changeit -noprompt alias "aws-rds-root" -file rds-ca-2015-root.pem

     keytool -import -keystore trust-store-rds.jks -storepass changeit -noprompt -alias "aws-rds-us-east-1" -file rds-ca-2015-us-east-1.pem
  1. Build the URL as above.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=145255&siteId=1