Is it possible to programmatically provide a Java KeyStore and TrustStore to Connector/J? - jdbc

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.

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

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).

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.

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);

Related

Apache Camel set privateKey SFTP

I am trying to connect to a sftp server in order to store some files. I want to connect via the SSH.
My question is almost identical with:
Camel SFTP component - SSH private key URI works with privateKeyFile, doesn't work with privateKey
and the following answer: Apache Camel - How to set a private key in a dinamic sftp endpoint
But I still I didn't manage to connect.
My working route:
from(mytopic)
.to(sftp://username#my.host:22/folder/?privateKeyFile=path/to/privateRsa)
My NON working route:
from(mytopic)
.to(sftp://username#my.host:22/folder/?privateKey=byteArrayRsa)
I debugged the com.jcraft.jsch.KeyPair class -> following method:
public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchException
And seems that instead of having the correct byte[] prvkey, I got its address.
How can I send the correct byte array? I tried to create a bean as it is suggested in one of the links, but it didn't worked. (I am using camel 3.1)
This doesn't work, you cannot simply use a ByteArray reference in a route definition. It works when you use a Registry like org.apache.camel.impl.SimpleRegistry to store the key reference.
simpleRegistry.put("MySshKey", byteArrayRsa)
camelCtx.setRegistry(simpleRegistry)
Then you can use a reference in the route:
privateKey=#MySshKey
Maybe you could also use Spring Injection.
I have successfully connected SFTP server using apache camel's SFTP component using the ssh private key. My requirement was to download files from SFTP server. Similarly, It can be applied to use SFTP to upload file, Sample code is as follows :
public class MySFTPFileTransferRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("sftp://my_host/?username=user_name&password=&preferredAuthentications=publickey&useUserKnownHostsFile=false&privateKeyFile=/Users/XXXX/.ssh/id_rsa")
.to("file:/Users/XXXX/Downloads/source/").
log("Upload file ${file:name} is complete.");
}
}

Replace password in Websphere datasource during runtime

After I look up the datasource in Websphere 8 with application code can I set its password to something other than what is in the config file? (Where I get that other password is not of concern here, I just have it.)
This should all happen during runtime and I don't want to write the password to a config file, just do this replacement in memory.
Yes, you can supply the user name and password as arguments to the DataSource.getConnection(user, password) method.
If you obtain your data source with a resource reference, you will need to ensure that its authentication type is APPLICATION, not CONTAINER. (CONTAINER is the default for a resource reference that does not specify). Here is an example,
#Resource(lookup = "jdbc/myDataSource", authenticationType = AuthenticationType.APPLICATION)
DataSource myDataSource;
...
Connection con = myDataSource.getConnection(user, password);

How to use keystore in Websphere Liberty and in Java?

maybe it dumb question but I couldn't find explanation by googling.
There is settings in server.xml file of Websphere Liberty 8.5
<keyStore id="sampleJKSKeyStore"
location="MyKeyStoreFile.jks"
type="JKS" password="myPassword" />
What can I do with this keystore? Can I for example programatically save to and load from PrivateKey / PublicKey which I create by using the following code:
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
final KeyPair key = keyGen.generateKeyPair();
final PrivateKey privateKey1=key.getPrivate();
final PublicKey publickey1=key.getPublic();
Thank you in advance!
The keystore configuration essentially just tells the Liberty profile runtime where the keystore is and how to get certificates from it. It can then be associated with the httpEndpoint to store the certificates for https. There are other ways it is used by the Liberty runtime, but the usage is by the runtime, not for applications. If you would like to get application access to it you can raise a request for the product to be enhanced here.

tyrus websocket server programmatic endpoint

I am trying to create a websocket server using programmatic endpoints with tyrus 1.8.2. I have found that the constructor:
public Server(String hostName,
int port,
String contextPath,
Map<String,Object> properties,
Class<?>... configuration)
does not accept a class implementing ServerEndpointConfig. When I try that it throws a DeploymentException "Class XXX is not ServerApplicationConfig descendant nor has #ServerEndpoint annotation."
Since I am using programmatic endpoints (not annotated), this would seem to imply that I must implement ServerApplicationConfig. That is contrary to the websocket API documentation.
So when I implement ServerApplicationConfig, I no longer get this exception, and the server appears to start without problems, but it returns 404 to what I believe are valid connection attempts (correct host, port, and context path.)
What am I missing?
Additional information: I extended TyrusServerEndpointConfigurator and provided an override for the modifyHandshake() method. The server is returning 404's without ever calling this method.
The problem turned out to be confusion about the way Tyrus constructs the context path. There is a path passed to the Server constructor, and a path returned by the ServerEndpointConfig getPath() method. These are concatenated to form the full context path.
So if you specify "/server" in the Server constructor and "/endpoint" in ServerEndpointConfig.getPath(), the server will accept connection requests on "/server/endpoint".

How can I specify my .keystore file with Spring Boot and Tomcat?

I'm trying to set up Spring Security to work with Spring Boot's embedded Tomcat instance. There are quite a few basic samples that do this but I'm stuck where they leave off -- they do basic authentication over HTTP (not HTTPS).
I could probably make it work if I had access to the Tomcat configuration files (server.xml) but since Spring Boot uses an embedded Tomcat instance (which is otherwise a huge convenience), I dont have access to the Tomcat configuration files (at least, not to my knowledge).
There may be an application.properties setting for this but I haven't been able to track it down. I've seen references to a server.contextPath field in application.properties that I suspect may have something to do with replacement Tomcat config files. Even if it is related, I wouldn't know where to begin anyway -- all of the Tomcat SSL instructions I've seen start with editing an existing server.xml file, not building one from scratch.
Can this be done with Spring Boot (either by somehow specifying a snippet of server.xml or through other means)? If not, what would be the simplest way to do this? I understand that I may need to exclude the Tomcat component of Spring Boot but I'd prefer to avoid that if possible.
Starting with Spring Boot 1.2, you can configure SSL using application.properties or application.yml. Here's an example for application.properties:
server.port = 8443
server.ssl.key-store = classpath:keystore.jks
server.ssl.key-store-password = secret
server.ssl.key-password = another-secret
Same thing with application.yml:
server:
port: 8443
ssl:
key-store: classpath:keystore.jks
key-store-password: secret
key-password: another-secret
Here's a link to the current reference documentation.
For external keystores, prefix with "file:"
server.ssl.key-store=file:config/keystore
It turns out that there is a way to do this, although I'm not sure I've found the 'proper' way since this required hours of reading source code from multiple projects. In other words, this might be a lot of dumb work (but it works).
First, there is no way to get at the server.xml in the embedded Tomcat, either to augment it or replace it. This must be done programmatically.
Second, the 'require_https' setting doesn't help since you can't set cert info that way. It does set up forwarding from http to https, but it doesn't give you a way to make https work so the forwarding isnt helpful. However, use it with the stuff below, which does make https work.
To begin, you need to provide an EmbeddedServletContainerFactory as explained in the Embedded Servlet Container Support docs. The docs are for Java but the Groovy would look pretty much the same. Note that I haven't been able to get it to recognize the #Value annotation used in their example but its not needed. For groovy, simply put this in a new .groovy file and include that file on the command line when you launch spring boot.
Now, the instructions say that you can customize the TomcatEmbeddedServletContainerFactory class that you created in that code so that you can alter web.xml behavior, and this is true, but for our purposes its important to know that you can also use it to tailor server.xml behavior. Indeed, reading the source for the class and comparing it with the Embedded Tomcat docs, you see that this is the only place to do that. The interesting function is TomcatEmbeddedServletContainerFactory.addConnectorCustomizers(), which may not look like much from the Javadocs but actually gives you the Embedded Tomcat object to customize yourself. Simply pass your own implementation of TomcatConnectorCustomizer and set the things you want on the given Connector in the void customize(Connector con) function. Now, there are about a billion things you can do with the Connector and I couldn't find useful docs for it but the createConnector() function in this this guys personal Spring-embedded-Tomcat project is a very practical guide. My implementation ended up looking like this:
package com.deepdownstudios.server
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import org.springframework.boot.*
import org.springframework.stereotype.*
#Configuration
class MyConfiguration {
#Bean
public EmbeddedServletContainerFactory servletContainer() {
final int port = 8443;
final String keystoreFile = "/path/to/keystore"
final String keystorePass = "keystore-password"
final String keystoreType = "pkcs12"
final String keystoreProvider = "SunJSSE"
final String keystoreAlias = "tomcat"
TomcatEmbeddedServletContainerFactory factory =
new TomcatEmbeddedServletContainerFactory(this.port);
factory.addConnectorCustomizers( new TomcatConnectorCustomizer() {
void customize(Connector con) {
Http11NioProtocol proto = (Http11NioProtocol) con.getProtocolHandler();
proto.setSSLEnabled(true);
con.setScheme("https");
con.setSecure(true);
proto.setKeystoreFile(keystoreFile);
proto.setKeystorePass(keystorePass);
proto.setKeystoreType(keystoreType);
proto.setProperty("keystoreProvider", keystoreProvider);
proto.setKeyAlias(keystoreAlias);
}
});
return factory;
}
}
The Autowiring will pick up this implementation an run with it. Once I fixed my busted keystore file (make sure you call keytool with -storetype pkcs12, not -storepass pkcs12 as reported elsewhere), this worked. Also, it would be far better to provide the parameters (port, password, etc) as configuration settings for testing and such... I'm sure its possible if you can get the #Value annotation to work with Groovy.
If you don't want to implement your connector customizer, you can build and import the library (https://github.com/ycavatars/spring-boot-https-kit) which provides predefined connector customizer. According to the README, you only have to create your keystore, configure connector.https.*, import the library and add #ComponentScan("org.ycavatars.sboot.kit"). Then you'll have HTTPS connection.
And here's an example of the customizer implemented in Groovy:
https://github.com/UniconLabs/orville/blob/master/web/src/main/groovy/org/apereo/openregistry/config/TomcatSslConfiguration.groovy

Resources