5

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)
3
  • Could you post the complete exception message & stacktrace? I wonder if it could be a JVM definition issue(different security providers? different trust store?). The stacktrace might help. Commented Jun 23, 2015 at 4:23
  • Tracing the decompiled code of JavaKeyStore. These lines are executing. if (xMagic!=MAGIC || (xVersion!=VERSION_1 && xVersion!=VERSION_2)) { throw new IOException("Invalid keystore format"); } Commented Jun 23, 2015 at 15:33
  • 1. What is env? 2. Is your keystore really of KeyStore.getDefaultType()? Commented May 18, 2022 at 10:12

2 Answers 2

13

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>

Sign up to request clarification or add additional context in comments.

1 Comment

For the records, it did not work for me. Still having the same issue.
0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.