Reputation: 20862
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.
Upvotes: 2
Views: 794
Reputation: 1
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);
Upvotes: -2
Reputation: 20862
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.
Upvotes: 1
Reputation: 20862
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).
Upvotes: 2