2

I'd like to connect to a MySQL/MariaDB RDBMS using Connector/J (or another compatible driver) and provide the keystore and truststore directly to the driver, rather than supplying a filename for an on-disk keystore/truststore.

I'm not storing my keys and certificates on the disk any longer and I'd like to avoid having to drop them into a temporary file just for this purpose.

Christopher Schultz
  • 20,221
  • 9
  • 60
  • 77

4 Answers4

1

This answer seems to be no. As the way to configure KeyStore and TrustStore is by providing an URL, not a class/factory.

Setop
  • 2,262
  • 13
  • 28
1

I think it is possible, if you are willing to go through a log of hoops to make it happen.

Because e.g. clientCertificateKeyStoreUrl will accept a URL, you can supply it one with a custom protocol handler, like mykeysupplier://mykey.

You can write a custom URLConnection class for mekeysupplier and then return whatever bytes you want from the getInputStream() method. You have to register a protocol handler and stuff like that, too.

So, it's kind of horrifically ugly, but I think it can be made to work and will surely try at least a PoC of this idea, because it will likely solve my problem (I posted the original question).

Christopher Schultz
  • 20,221
  • 9
  • 60
  • 77
1

There may be another option.

I re-read the configuration reference for Connector/J and there is a URL property that can be specified for connections: socketFactory. This must be the fully-qualified name of a class which implements Connector/J's interface called SocketFactory (note that this is similar but unrelated to the standard library class javax.net.SocketFactory).

This interface is pretty small:

public interface SocketFaactory {
    Socket afterHandshake() throws SocketException, IOException;
    Socket beforeHandshake() throws SocketException, IOException;
    Socket connect(String host, int portNumber, Properties props) throws SocketException, IOException;
}

Later versions of the driver add a fourth int loginTimeout parameter to the connect method.

At any rate, it looks like this might be the basis for a solution.

Unfortunately, MySQL does not use vanilla TLS connections so it might not be as simple as returning a Socket from a standard customized javax.net.ssl.SSLSocketFactory.

UPDATE

The SSL/TLS magic happens after connection due to the way MySQL manages its protocol (it's not just plain-TLS).

In the 5.1-era drivers, it's done in a class called ExportControlled in a method called getSSLSocketFactoryDefaultOrConfigured. In later versions (I have 8.0-era source in front of me), it's done in the same class but in a different method called performTlsHandshake.

Without significant hacking of the driver source or re-implementation of a ton of code, I suspect that the better solution is to implement the URL-based keystore-loading from this answer.

Christopher Schultz
  • 20,221
  • 9
  • 60
  • 77
-2

Yes, it's possible to programmatically provide a Java KeyStore and TrustStore to Connector/J. You can use the java.security.KeyStore and java.security.TrustStore classes to create the keystore and truststore in memory, and then pass them to the Connector/J Driver and Connection classes via the javax.net.ssl.keyStore and javax.net.ssl.trustStore system properties, respectively. HERE IS A CODE SAMPLE

import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;

// ...

// Load the keystore and truststore data into memory
byte[] keystoreData = ...;
byte[] truststoreData = ...;

// Create the keystore and truststore objects
KeyStore keyStore = KeyStore.getInstance("JKS");
KeyStore trustStore = KeyStore.getInstance("JKS");
keyStore.load(new ByteArrayInputStream(keystoreData), keystorePassword);
trustStore.load(new ByteArrayInputStream(truststoreData), truststorePassword);

// Set the system properties to use the in-memory keystore and truststore
System.setProperty("javax.net.ssl.keyStore", keyStore);
System.setProperty("javax.net.ssl.trustStore", trustStore);

// Connect to the database using Connector/J
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, username, password);
  • I'm sorry, but this code is wrong. It won't even compile because `System.setProperty` only takes String values. Add to that the fact that hijacking the JVM-wide default trust store and key store is a Really Bad Idea. – Christopher Schultz Feb 08 '23 at 15:10