I'm using Apache CXF (v2.7.3) to call a SOAP service over HTTPS. I can load the truststore from a file but not from the classpath - I get "Invalid keystore format" error.
I have this config in my cfx.xml file:
<http:conduit name="*.http-conduit">
<http:tlsClientParameters>
<sec:trustManagers>
<!-- For some reason, when I use the resource field, I get a "Invalid keystore format" exception -->
<sec:keyStore type="JKS" password="MYPASSWORD"
resource="truststore.jks" />
<!-- THIS WORKS FINE: <sec:keyStore type="JKS" password="MYPASSWORD"
file="/fullPathToMyTrustStore/truststore.jks" /> -->
</sec:trustManagers>
</http:tlsClientParameters>
</http:conduit>
I can load the trust store from file, but not from the classpath. I can tell from the exception that truststore.jks file is being found, but it is invalid. This is the stacktrace of exception thrown.
Caused by: java.io.IOException: Invalid keystore format
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:633)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:38)
at java.security.KeyStore.load(KeyStore.java:1185)
at org.apache.cxf.configuration.jsse.TLSParameterJaxBUtils.getKeyStore(TLSParameterJaxBUtils.java:142)
at org.apache.cxf.configuration.jsse.TLSParameterJaxBUtils.getTrustManagers(TLSParameterJaxBUtils.java:292)
at org.apache.cxf.configuration.jsse.TLSClientParametersConfig.createTLSClientParametersFromType(TLSClientParametersConfig.java:114)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:149)
I had exactly the same problem and blamed CXF at the beginning, but actually certificates were invalid on the classpath. The first thing to check is that the file in the jar is the same as in the project structure (before packing in jar).
Here are possible suspects to blame and possible solutions:
1) If you are using Maven, then filtering process may corrupt binaries (my case)
Solution: exclude certificates from Maven filtering process e.g:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.jks</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.jks</include>
</includes>
</resource>
</resources>
2) If you are using maven-assembly-plugin for building your distribution, it may corrupt binaries:
Solution: http://jira.codehaus.org/browse/MASSEMBLY-412
Related
On a Spring Boot application (2.3.3), I have a dependency to a module developed by my company. From a service I'm calling a method from this dependency which needs a file loaded from resources (src/main/resources/META-INF/spring-main-cfg.xml), so I've copied and pasted this file to my Spring Boot application resources.
Here's the code executed in that dependency:
InputStream in = RSAEncrypter.class.getClassLoader().getResourceAsStream(keyFileName);
// StreamCorruptedException here
ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in));
Stack:
java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:936) ~[na:1.8.0_281]
at java.io.ObjectInputStream.(ObjectInputStream.java:394) ~[na:1.8.0_281]
The thing is, I've created a new dummy Maven project with only this dependency and a main to execute that code and it works.
I don't understand what could be the reasons I've got this exception executed from my Spring Boot application. I've got the same Java version, the file read is the same.
Any ideas?
I've finally found the issue, it was the file it self since I had a global:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
The Maven doc on this say:
Warning: Do not filter files with binary content like images! This will most likely result in corrupt output.
https://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html
So I just added an exception for my file :
<resources>
<!-- Only properties files have to be filtered.
META-INF/spring-main-cfg.xml must not be or StreamCorruptedException occure by reading it -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
I am using sendGrid APIs to send emails using sendGrid maven dependancy in our Java service.
<dependency>
<groupId>com.sendgrid</groupId>
<artifactId>sendgrid-java</artifactId>
<version>4.9.2</version>
</dependency>
We create a fat jar of our service to execute it.
but after adding this dependancy, fat jar is not executing, giving SecurityException: Invalid signature file
Here's stack trace for the error:
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
at java.base/sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:340)
at java.base/sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:282)
at java.base/java.util.jar.JarVerifier.processEntry(JarVerifier.java:327)
at java.base/java.util.jar.JarVerifier.update(JarVerifier.java:239)
at java.base/java.util.jar.JarFile.initializeVerifier(JarFile.java:762)
at java.base/java.util.jar.JarFile.ensureInitialization(JarFile.java:1033)
at java.base/java.util.jar.JavaUtilJarAccessImpl.ensureInitialization(JavaUtilJarAccessImpl.java:72)
at java.base/jdk.internal.loader.URLClassPath$JarLoader$2.getManifest(URLClassPath.java:888)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:848)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:488)
at java.base/java.lang.Class.forName(Class.java:467)
at java.base/sun.launcher.LauncherHelper.loadMainClass(LauncherHelper.java:790)
at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:685)
So, we need to filter signed file while creating a fat jar.
add filter in maven-shade-plugin
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
In my current project I have a submodule which is using the maven exec plugin to run a test service which pulls configuration files from a location outside of the resources/testResources folders.
I need to use filtering to inject an environment variable into a few of the configuration files. This is working for one of the files, a .properties file, but not for another file which is a .json. In the latter case it simply leaves the variable in the json file. The two files are right next to each other in the filtered directory.
Here is the filtering snippet from the submodule:
<build>
<finalName>${artifactId}</finalName>
<testResources>
<testResource>
<filtering>true</filtering>
<directory>../etc</directory>
</testResource>
</testResources>
json file:
{ "customMappings" :
{ "tag:example.com:/vagrant/" : "file:${VAGRANT_CWD}" }
}
Abbreviated project structure:
project
etc
config.properties
config.json
submodule
pom.xml
The submodule is definitely loading both files, but only filtering the .properties file.
Is there something special about it being a json file that would prevent filtering from happening to it? Anything that can be done about this?
For what its worth, I did eventually get this to work. I found that I had to directly list the file for inclusion in order to get it to be processed (its been a long time so hopefully this is the correct solution):
<build>
<finalName>${artifactId}</finalName>
<testResources>
<testResource>
<filtering>true</filtering>
<directory>../etc</directory>
<includes>
<include>config.json</include>
<include>config.properties</include>
</includes>
</testResource>
</testResources>
...
I've been trying for sometime to figure out this problem. I keep getting invalid keystore format error when loading keystore via the Classloader on Tomcat. I've written some unit tests which are successful at loading my keystore via the classpath, but when running in Tomcat I get the Invalid keystore format error.
This is for a RestTemplate Spring REST client.
My Spring Configuration code... pretty much the same in the unit test and real config.
#Value("${env}")
private String env;
#Bean
public RestTemplate getRestTemplate(HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory) {
return new RestTemplate(httpComponentsClientHttpRequestFactory);
}
#Bean
public HttpComponentsClientHttpRequestFactory getHttpComponentsClientHttpRequestFactory(HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
return httpComponentsClientHttpRequestFactory;
}
#Bean
public HttpClientConnectionManager getHttpClientConnectionManager() throws Exception {
String password = environment.getProperty("keystore.password");
InputStream cpStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(env + "/keystore.jks");
//load the keystore
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(cpStream, password.toCharArray());
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(keystore).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
PlainConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create().register("http",plainsf).register("https",sslsf).build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(50);
return cm;
}
#Bean
public HttpClient getHttpClient(HttpClientConnectionManager httpClientConnectionManager) {
return HttpClients.custom().setConnectionManager(httpClientConnectionManager).build();
}
This code works fine to create my RestTemplate with keystore I need for my SSLContext in my unit test. I've also tried following different ways to get the InputStream for my keystore from the class loader via my unit test and these all worked too.
InputStream cpStream = this.getClass().getClassLoader().getResourceAsStream(env + "/csaa.jks");
InputStream cpStream = this.getClass().getResourceAsStream("/" + env + "/csaa.jks");
The env variable is just a way to load a particular environments properties... env=DEV , etc.... Everything works fine with running this with JUnit SpringJUnit4ClassRunner.class but when deploying it to Tomcat I always get the Invalid keystore error. If I use a FileInputStream with a hard coded path to the keystore on the file system it works just fine in Tomcat. I really want to load it with the Classloader.
I tried it in Liberty Profile too with same results.
Relevant part of the stack trace:
Caused by: java.io.IOException: Invalid keystore format
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:650)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:55)
at java.security.KeyStore.load(KeyStore.java:1445)
I found my problem... my Maven filtering was corrupting the keystore. I need to add the following to my pom.xml. Sadly I don't think this the first time I had this problem. :(
pom.xml:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
<excludes>
<exclude>**/*.jks</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.jks</include>
</includes>
<excludes>
<exclude>**/*.xml</exclude>
</excludes>
</resource>
</resources>
This is because Maven transcodes the key file during packaging. It can be solved by adding this Maven plug-in.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<encoding>UTF-8</encoding>
<useDefaultDelimiters>false</useDefaultDelimiters>
<delimiters>
<delimiter>$[*]</delimiter>
</delimiters>
<!-- Filter file suffixes that do not require transcoding .crt/.p8/jks -->
<nonFilteredFileExtensions>
<nonFilteredFileExtension>crt</nonFilteredFileExtension>
<nonFilteredFileExtension>p8</nonFilteredFileExtension>
<nonFilteredFileExtension>jks</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
I'm using Intellij 13, and I've marked a directory as a resources directory (thinking that this would make all sub-files/folders available on the classpath), however when I run the project, an exception is thrown saying that a file called database.properties (inside the resources folder) can't be found at runtime.
Any ideas?
'database' folder is the folder marked as a resources folder.
I do not know intellij-idea, but I know that maven can deal with multiple resources directory provided they are declared in pom.xml. From Maven doc on Mave Resources plugin - Specifying resources directories :
...
<resources>
<resource>
<directory>resource1</directory>
</resource>
<resource>
<directory>resource2</directory>
</resource>
<resource>
<directory>resource3</directory>
</resource>
</resources>
...
If IntelliJ-IDEA has not declared your database directory such way in the pom, maven has no chance to find it.