configure embedded Tomcat7 to use keystorefile embedded in executable jar - java

I've managed to configure my embedded tomcat to use a keystorefile and it works when i execute the project from eclipse.
The code is simple:
...
String keystore = new File(MyServer.class.getResource("/keystore").toURI()).toPath().toString();
httpsConnector.setAttribute("keystoreFile",keystore);
...
The file keystore is located in a source directory added to the buildpath.
After exporting the project to an executable jar, i can verify the existence of the keyfile in the root of the jar.
But on executing the jar, i get this error:
Exception in thread "main" java.lang.IllegalArgumentException: URI is not hierarchical
So i asume, that i can not configure the keyfile with httpsConnector.setAttribute("keystoreFile",...). is there another way to configure that? i really dont want to copy the keyfile in a temp dir and reference it from there.

i really dont want to copy the keyfile in a temp dir and reference it from there.
I sympathize, but it looks like you will have to just that. A Java keystore can be loaded from any type of input stream (see Keystore.load) so you would think it would be possible to load your keystore from a resource in the jar file. However if you search the Tomcat 7 source code for the string "ks.load" you will see that it always interprets the keystoreFile attribute as the name of a File and creates a FileInputStream which it then passes the ks.load.
Therefore it will be necessary to create the temp file containing the keystore and pass the location of this file as your keystoreFile attribute.
BTW - if you are not intending to distribute this jar and are willing to keep it private, then your approach of embedding the keystore is probably OK.
However if you are planning on distributing this jar to multiple sites, then the security of every site is based on the same private key. Furthermore, anyone with access to the jar file can probably extract the private key and can then eavesdrop or perhaps man-in-the-middle attack all of your sites.
In general it is better to have an installation process that requires a new private key and certificate to be generated by the administrator of the site (even if it's just a self-signed certificate). That way every key is different and the only way to compromise the site would be to get access to that servers key store and password.

This might be an old post, but I encountered the same problem and didn't found a solution online. So I want to share what I did.
In my case, I needs two ports both with self-signed certificates. Besides the one configured in application.yml, the other port is configured in the main entry.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServletWebServerFactory servletContainer() throws IOException
{
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addAdditionalTomcatConnectors(additionalConnector());
return tomcat;
}
private Connector additionalConnector() throws IOException
{
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("https");
connector.setPort(8088);
...
URL keystore = new ClassPathResource("keystore.jks").getURL();
connector.setAttribute("keystoreFile", keystore.toExternalForm());
return connector;
}
}
Using URL, both mvn spring-boot:run and run the jar work and can load the keystore without problem.
I was using spring-boot 2.0.1.RELEASE and tomcat 8.5.29

Related

bundling resource files into a java app jar file that is used on an ecs cluster

We have an application that uses a truststore for sasl/ssl from a java application.
The application is hosted on an ECS cluster on AWS. When running locally the truststore is stored in the resources folder of the java app then use this method when want to reference it, this works perfectly from local
public String getTruststoreFilepath() {
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("ts.truststore.jks").getFile());
return file.getPath();
}
When this is deployed on AWS it bombs out with the exception
Caused by: java.io.FileNotFoundException: file:/app.jar!/BOOT-INF/classes!/kafka.client.truststore.jks (No such file or directory)
Is there a way I can include the trust store as part of the jar file when deploying? Or would I have to create it on the ec2 that is running the jar file?
Thanks
This is the limitation of Kafka client, you can check the GitHub issue.
The Kafka client requires the keystore to be on the file system (it doesn't understand classpath resources, and it can't read files in the jar).
KeyStore load() {
try (FileInputStream in = new FileInputStream(path)) {
So One possible workaround is to extract the keystore to a temporary
file:
#SpringBootApplication
public class Kgh710Application {
public static void main(String[] args) throws Exception {
FileCopyUtils.copy(new ClassPathResource("client.ks").getInputStream(),
new FileOutputStream("/tmp/client.ks"));
SpringApplication.run(Kgh710Application.class, args);
}
}
and
spring.kafka.ssl.keystore-location=file:/tmp/client.ks

How to properly package keystore in .jar file

I'm at the final step of developing (and especially deploying) a client server application where I have a secured connection using TLS. I got the keystore and truststore for the client and the server and currently I load them as SystemProperties before creating the SSLSocket / SSLServerSocket like this:
Properties systemProperties = System.getProperties();
systemProperties.put("javax.net.ssl.keyStore", "./auth/labkey.jks");
systemProperties.put("javax.net.ssl.keyStorePassword", "<somepassword>");
systemProperties.put("javax.net.ssl.trustStore", "./auth/labtrust.jks");
systemProperties.put("javax.net.ssl.trustStorePassword", "<somepassword>");
System.setProperties(systemProperties);
This works fine but - as I already thought - doesn't when packaging everything into a .jar file. Currently my project folder consists of the normal src folder in which I keep all the packages with the sourcecode files. The keystore and truststore are kept in a folder called "auth" which is on the same level as the src folder.
I know from previous projects that the relative path changes when packaging additional files into the .jar however the first thing is I don't know how I should properly handle these file to have them packed into the .jar (currently they are only included when I create a package for them in the source folder or declare the auth folder as an additional source folder).
I tried both approaches and after testing a lot of different relative paths (which is the second problem: finding the right relative path) I couldn't figure out how to properly address them.
Any advice or hint would be really appreciated.
EDIT:
After trying out several things in context with suggested ideas I can narrow the problem down to the path to the keystore and truststore file being my actual problem. I'm quite sure if I were able to access it relatively there would be an option to use, however for accessing the files I HAVE to provide a path in any kind, no matter which approach I'm going to use. The fact that I'm not able to get the proper relative path to the file is therefore the main problem which I need to get solved.
So accordingly these questions come to my mind:
How do I correctly add these files to the .jar?
Will I need to make the auth folder a source folder or do I need to put it in a package or is there any need or possibility to configure the files correctly by configuring the build path?
What will then be the relative path to the files?
Quick edit: this has been answered before in previous threads - I'm sure my explanation is sub-par here, but this came up recently for me as well and this was my solution
You may want to use getClass().getClassLoader.getResourceAsStream("filename.properties")
Properties properties = new Properties();
String propertiesFileName = "config.properties";
inputStream = getClass().getClassLoader().getResourceAsStream(propertiesFileName);
if (inputStream != null) {
properties.load(inputStream);
} else {
throw new FileNotFoundException("property file '" + propertiesFileName + "' not found.");
}

Spark-java how to import keystore file with path inside jar

I have an Spark-java application that has a couple of endpoints. Now i created a keystore file with a SSL certificate for my domain.
This is how i added it in spark:
secure("src/deploy/letsencrypt.jks", "mysecretpassword", null, null);
If i run my application from inside intteliJ it is able to run, but once i package my application in a jar file it can't find the keystore file. I have searched a lot on the web and the answer to the question is: use an inputstream. Now spark only lets you give an path, not an inputstream.
Is there a way to still access a keystore file from within a jar, just by specifying an path? Or is this not possible atm with spark?
When you package an application in a jar file, the src path could not be found because it doesn't exist anymore.
In Intelij IDEA / Project Structure windows / Modules section you can find Resource directory. Every file/folder you put in Resource directory, would be copied in Jar file.
But hard coding keyStorePath, keyStoreKey in your code is not secure. I suggest set these variable as parameters in run-time. something like this code:
String keyStorePath = System.getProperty("app.httpskey.path");
String keyStoreKey = System.getProperty("app.httpskey.key");
if (keyStoreKey != null && keyStorePath != null) {
try {
secure(keyStorePath, keyStoreKey, null, null);
Logger.log("Web server will started in HTTPS mode.");
} catch (Exception e) {
Logger.log(e);
}
}

Netbeans - GlassFish - Java EE Configuration File

Since it was really difficult for me to find an answer to this question I'm gonna post both the question and the answer I found to this problem.
Problem: How to use a configuration file in java while working with Netbeans and deploying into a GlassFish Server?
Main problem is to actually access the file (a lot of trouble with the path in which things as getResource, creating a new File and getting it's absolute path, and many other tricks didn't work).
In this particular case I wanted the file to be in my ejb Project.
Create a configuration File (e.g. "config.properties") in
ProjectName-ejb\src\conf
You will be able to see the file from Netbeans in your project configuration Files:
Insert all the properties you want:
Create an attribute in the class from which you will access the file like this: private final String BAD_WORDS_FILE_NAME = "\META-INF\config.properties";
Once your code is deployed to GlassFIsh, all conf files seem to be deployed to this META-INF folder:
Access Properties using sth like:
private String[] getBadWordsFromFile() throws IOException {
InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream(BAD_WORDS_FILE_NAME);
Properties properties = new Properties();
properties.load(resourceAsStream);
String badWordsAsString = properties.getProperty(BAD_WORDS_PROPERTY_NAME);
return badWordsAsString.split(BAD_WORDS_SEPARATOR);
}
This was the Solution I found, which worked but was only tested on a local machine... this might get some trouble on Release.

Where to put file that a jar needs to load

I have a jar that will be connecting to a website for communication using websockets. The website is an SSL site and for that reason I have a .ts the program needs to load file. The following is the java code used to load that .ts file
System.setProperty("javax.net.ssl.trustStore",
"foo.ts");
System.setProperty("javax.net.ssl.trustStorePassword", "password");
and the java program is in a package called foo.web so thus when packed into the jar is in a folder called foo which then has a subfolder called web.
My question is, do I put the foo.ts in the foo/web folder where my classes reside or should I put the foo.ts file at the root of the jar to be loaded with my current code in the program for loading the foo.ts file?
The work around System.setProperty... needing a 'regular' file path, is using mechanism to resolve class resources:
place ts file in your code folder under some package (normal web code not
a library jar code)
in a class from the same package, call:
a
//this will find the file in the package folder
URL cert = this.getClass().getResource("ts");
String path = cert.toString(); //this translate the url to file system location
if (path.startsWith("file:"))
path = path.substring("file:".length()+1,path.length()); //getting rid of file prefix as not needed
System.setProperty("javax.net.ssl.trustStore", path);
If you want to include ts in jar, similar trick, but instead of path (which will not work, read the content and save it to temporary location
in = this.getClass().getResourceAsStream(wsdl);
... save it to tmp location
System.setProperty("javax.net.ssl.trustStore", your tmp location);
I assume that you have unsigned, self generated certificate that you want to use for your ssl connection.
The part that servers the content over ssl, (your tomcat on 443 or apache) needs to know the certificate and its key (configured as in tomcat or apache document).
Your java code that is deployed on that server, does not need to knwo about the certificate or even that is behind ssl.
But the code that wants to connect to such server, lets say web service client will throw exception (and very obscured btw) as it will not recognize the certificate and refuse connecting (unlike the web browser which ofer the dialog that lets you add an exception).
So the client code needs the certificate added to its TrustedStore before oppening connection (the trick with System.property does the job). Thanks to it the client can trust the connection as your remote cert matches the one he already has.
If your client code happens to be running on the same tomcat, it still needs the cert added to the store, as the configuration options for connector at 443 only expose the cert to anyone who can read it, but do not add it to the tomcat's pool of know certificates. To do so you need the -Djava.net.ssl.trustStore=YOUR_TS -Djavax.net.trustStorePassword=PASS options for tomcat starts, or the System.setProperty inside your application code.
The value of the javax.net.ssl.trustStore property is a file path that points to the location of the trust store on disk. If you don't specify a folder, it will assume the current working directory for the program which is most likely where the JVM was invoked from. If you are unsure what your current working directory is, you can get it from the user.dir system property.
System.getProperty("user.dir"));

Categories

Resources