KeyStore configuration ignored

661 views
Skip to first unread message

Maciej Gawinecki

unread,
Feb 8, 2016, 9:11:46 AM2/8/16
to REST assured
Hi Johan,

I'm using REST-Assured 2.8.0. I'm having a problem to authenticate to a certain service with RestAssured using both keystore and truststore. Keystore is used to identity myself in front of the service and truststore is used to trust the service.

        KeyStore trustStore;
        try {
            trustStore = KeyStore.getInstance("JKS");
            trustStore.load(DontKnowYetWhatIAmTesting.class.getResourceAsStream("/keys/trusted.jks"), "changeit".toCharArray());
        } catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
            throw Throwables.propagate(e);
        }

        RestAssured.given()
                .trustStore(trustStore)
                .keystore("/keys/internal.jks", "changeit")
                .when()
                .get("https://protected.resource.com")
        .then().log().all().statusCode(200);


I started to debug with -Djavax.net.debug=all and found that only certificates from my trustustore are listed. I started to analyze the problem and found that in com.jayway.restassured.internal.KeystoreSpecImpl you are creating SSLFactory only with truststore

    ssl = new SSLSocketFactory(truststore);

You never use the SSLSocketFactory constructor that enabled defining both truststore and keystore, e.g.:

    public SSLSocketFactory(final KeyStore keystore, final String keystorePassword, final KeyStore truststore)


Or I am using something wrong?

In general, I found the code of KeystoreSpecImpl a bit confusing. In KeystoreSpecImpl.createTrustStore() method you create truststore by reading a keystore file.

Regards,
Maciej

Johan Haleby

unread,
Feb 11, 2016, 11:46:27 PM2/11/16
to rest-a...@googlegroups.com
I believe this is broken. Since I've never had to use this myself I tried to implement it to the best of my knowledge (and last time I looked into to it I interpreted truststore and keystore to essentially be the same thing but a truststore was essentially a keystore applied with a password) but with my current understanding this was a big and embarrassing mistake. Correct me if I'm wrong (and please do so fast :)) but truststore is used by the client to validate the server and keystore is used by the server to validate the client? They can both be specified for the same request. Is this correct?

--
You received this message because you are subscribed to the Google Groups "REST assured" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rest-assured...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Maciej Gawinecki

unread,
Feb 12, 2016, 5:16:20 PM2/12/16
to rest-a...@googlegroups.com
John,

You're right.

Truststore and keystore are structurally the same. This is why in Java
KeyStore class can be used to represent both.

However, they are used for different things. Truststore contains
certificates of servers that the client trust. When SSL handshake
starts, the server sends its certificate and client checks if it
matches one of the certificates it has in its truststore. More
precisely, server signs response with its private key and certificates
in truststore are public keys to verify this signature. On the other
side, keystore contains a private keys identifying a client.
Precisely, client signs request with its private key and then server
is able to identity a client and checks if it trust it.

This is my understanding so far. I found a more precise and
technically sound explanation here:
http://javarevisited.blogspot.com/2012/09/difference-between-truststore-vs-keyStore-Java-SSL.html.

I wish I could provide pull request with a fix, but I still need to
understand how to use both with SslSockerFactory + how to unit test
that. So far, I haven't been able to get this working.

Regards,
Maciej
> You received this message because you are subscribed to a topic in the
> Google Groups "REST assured" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/rest-assured/kHKf04XH6yA/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to

OR

unread,
Feb 13, 2016, 6:36:50 AM2/13/16
to REST assured, mgawi...@gmail.com
Hi Maciej,

I am working on something similar. I have implemented this whole flow in Java using Apache http client library and it is working fine. I got a valid response successfully. But I am not able to implement that using rest-assured.

Let me know if you find a way.

Thanks,
OR

Maciej Gawinecki

unread,
Feb 13, 2016, 11:03:40 AM2/13/16
to OR, REST assured
OR,

REST-Assured is using apache-http-client underneath with its
SSLSockerFactory. Maybe, if you share your solution with
apache-http-client, I would be able to provide a fix for REST-Assured.
Could you?

Regards,
Maciej

OR

unread,
Feb 13, 2016, 11:55:54 AM2/13/16
to REST assured, objectre...@gmail.com, mgawi...@gmail.com
Hey Maciej,

Here you go:


The rest api i was trying to access uses keystore, trust store and user name password. I haven't been able to figur eout how to specify user name password in rest-assured.
package testPkg;

import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

public class TestClient {

public static void main(String[] args) throws Exception {
testAuth();
}
public static String testAuth() {

String KEY_STORE_PATH = give keystore file path here
String KEY_STORE_PASSWORD = give key store password here
String TRUST_STORE_PATH = give truststore path here
String TRUST_STORE_PASSWORD = give trust store password here

String SERVICE_USER = givelogin id here
String SERVICE_ACCT_PASSWORD = give password here

CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(SERVICE_USER, SERVICE_ACCT_PASSWORD);
provider.setCredentials(AuthScope.ANY, (Credentials) credentials);

try {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreInput = new FileInputStream(KEY_STORE_PATH);
keystore.load(keystoreInput, KEY_STORE_PASSWORD.toCharArray());
System.out.println("Keystore has " + keystore.size() + " keys");
KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream truststoreInput = new FileInputStream(TRUST_STORE_PATH);
truststore.load(truststoreInput, TRUST_STORE_PASSWORD.toCharArray());
System.out.println("Truststore has " + truststore.size() + " keys");

SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keystore, KEY_STORE_PASSWORD, truststore);
HttpClient httpClient = HttpClientBuilder.create()
.setSSLSocketFactory(sslSocketFactory)
.setDefaultCredentialsProvider(provider).build();

HttpGet httpGet = new HttpGet(GIVE REST API URL HERE);
HttpResponse httpResponse = httpClient.execute(httpGet);

String response = EntityUtils.toString(httpResponse.getEntity());
System.out.println("response::" + response);
System.out.println("responseCode " + httpResponse.getStatusLine().getStatusCode());
return response;
} catch (Exception e) {
System.out.println(e.getCause());
e.printStackTrace();
}
return null;
}
}

I found this method named certificate in AuthConfig class in package com.jayway.restassured.internal.http. It looks like it might do the trick, haven't tried it though. I am not sure what would be passed as port number and x509HostnameVerifier.

Do let me know if you are able to get the code working in rest-assured.

Thanks,
OR

Object Repository

unread,
Feb 15, 2016, 9:32:44 AM2/15/16
to REST assured, objectre...@gmail.com, mgawi...@gmail.com
Hey Maciej,

Did you happen to find a fix for this? I tried fixing it the whole day but no success. :(

Thanks,
OR

Johan Haleby

unread,
Feb 24, 2016, 2:33:33 AM2/24/16
to rest-a...@googlegroups.com, objectre...@gmail.com, mgawi...@gmail.com
@marciej Could you please help out and test the new snapshot (2.8.1-SNAPSHOT) and see if it works? You need the following repo:

<repositories>
        <repository>
            <id>sonatype</id>
            <snapshots />
        </repository>
</repositories>


Huiai Chan

unread,
Mar 11, 2016, 11:47:21 PM3/11/16
to REST assured, objectre...@gmail.com, mgawi...@gmail.com
I had a similar problem and was able to adapt @marciej solution successfully. I have now upgraded to 2.9.0 but the fix is not working. 

Here is my code that was working without using the keystore/truststore form Rest-Assured. I'm passing in the keystore/truststore details as system parameters. 
//Keystore
FileInputStream keyStoreStream = new FileInputStream(System.getProperty("keystore"));
KeyStore keyStore = KeyStore.getInstance("jks");
String certPassword = System.getProperty("keystorePassword");
keyStore
.load(keyStoreStream, certPassword.toCharArray());

//TrustStore
FileInputStream trustStoreStream = new FileInputStream(System.getProperty("truststore"));
KeyStore trustStore = KeyStore.getInstance("jks");
trustStore
.load(trustStoreStream, System.getProperty("truststorePassword").toCharArray());

//SSLSocketFactory
org.apache.http.conn.ssl.SSLSocketFactory lSchemeSocketFactory = new org.apache.http.conn.ssl.SSLSocketFactory(keyStore, certPassword, trustStore);

//RestAssured
RestAssured.config = RestAssured.config().sslConfig(sslConfig().with().sslSocketFactory(lSchemeSocketFactory));

//proceed with the call...
given
()....

Now with 2.9.0, I tried replacing the above with 
RestAssured.keystore(System.getProperty("keystore"), System.getProperty("keystorePassword"));
RestAssured.trustStore(System.getProperty("truststore"), System.getProperty("truststorePassword"));

given
()...

I'm getting this error:

java.io.IOException: Keystore was tampered with, or password was incorrect

at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:780)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:56)
at sun.security.provider.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:225)
at sun.security.provider.JavaKeyStore$DualFormatJKS.engineLoad(JavaKeyStore.java:70)
at java.security.KeyStore.load(KeyStore.java:1445)
at java_security_KeyStore$load$1.call(Unknown Source)
at com.jayway.restassured.internal.TrustAndKeystoreSpecImpl$_createStore_closure1.doCall(TrustAndKeystoreSpecImpl.groovy:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
at groovy.lang.Closure.call(Closure.java:426)
at groovy.lang.Closure.call(Closure.java:442)
at org.codehaus.groovy.runtime.IOGroovyMethods.withStream(IOGroovyMethods.java:1207)
at org.codehaus.groovy.runtime.ResourceGroovyMethods.withInputStream(ResourceGroovyMethods.java:1674)
at org.codehaus.groovy.runtime.dgm$1019.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at com.jayway.restassured.internal.TrustAndKeystoreSpecImpl.createStore(TrustAndKeystoreSpecImpl.groovy:92)
at com.jayway.restassured.internal.TrustAndKeystoreSpecImpl$createStore.callCurrent(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:182)
at com.jayway.restassured.internal.TrustAndKeystoreSpecImpl.apply(TrustAndKeystoreSpecImpl.groovy:49)
at com.jayway.restassured.internal.http.AuthConfig.certificate(AuthConfig.java:125)
at com.jayway.restassured.internal.http.AuthConfig$certificate.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at com.jayway.restassured.authentication.CertAuthScheme.authenticate(CertAuthScheme.groovy:39)
at com.jayway.restassured.authentication.AuthenticationScheme$authenticate.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at com.jayway.restassured.internal.RequestSpecificationImpl.sendRequest(RequestSpecificationImpl.groovy:1144)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1210)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:810)
at com.jayway.restassured.internal.RequestSpecificationImpl.invokeMethod(RequestSpecificationImpl.groovy)
at org.codehaus.groovy.runtime.callsite.PogoInterceptableSite.call(PogoInterceptableSite.java:48)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:149)
at com.jayway.restassured.internal.filter.SendRequestFilter.filter(SendRequestFilter.groovy:30)
at com.jayway.restassured.filter.Filter$filter$0.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
at com.jayway.restassured.internal.filter.FilterContextImpl.next(FilterContextImpl.groovy:73)
at com.jayway.restassured.filter.time.TimingFilter.filter(TimingFilter.java:56)
at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
at com.jayway.restassured.internal.filter.FilterContextImpl.next(FilterContextImpl.groovy:73)
at com.jayway.restassured.filter.log.StatusCodeBasedLoggingFilter.filter(StatusCodeBasedLoggingFilter.java:94)
at com.jayway.restassured.filter.log.ResponseLoggingFilter.filter(ResponseLoggingFilter.java:31)
at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
at com.jayway.restassured.internal.filter.FilterContextImpl.next(FilterContextImpl.groovy:73)
at com.jayway.restassured.filter.log.RequestLoggingFilter.filter(RequestLoggingFilter.java:101)
at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:141)
at com.jayway.restassured.internal.filter.FilterContextImpl.next(FilterContextImpl.groovy:73)
at com.jayway.restassured.filter.FilterContext$next.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133)
at com.jayway.restassured.internal.RequestSpecificationImpl.applyPathParamsAndSendRequest(RequestSpecificationImpl.groovy:1596)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1210)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:810)
at com.jayway.restassured.internal.RequestSpecificationImpl.invokeMethod(RequestSpecificationImpl.groovy)
at org.codehaus.groovy.runtime.callsite.PogoInterceptableSite.call(PogoInterceptableSite.java:48)
at org.codehaus.groovy.runtime.callsite.PogoInterceptableSite.callCurrent(PogoInterceptableSite.java:58)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:182)
at com.jayway.restassured.internal.RequestSpecificationImpl.get(RequestSpecificationImpl.groovy:160)
at com.jayway.restassured.internal.RequestSpecificationImpl.get(RequestSpecificationImpl.groovy)...

Am I not using the keystore/truststore correctly? Please advice. 


Johan Haleby

unread,
Mar 12, 2016, 12:31:57 AM3/12/16
to rest-a...@googlegroups.com, Object Repository, Maciej Gawinecki
Have you replaced "keystore" with "truststore" as indicated by the release notes?

Huiai Chan

unread,
Mar 12, 2016, 2:22:18 PM3/12/16
to REST assured, objectre...@gmail.com, mgawi...@gmail.com
Hi Johan, 

I needed both a keystore and truststore. How should I load the keystore if I replace it with truststore? I've also tried loading the keystore manually first (see code below), but got a "Password must not be null" error back:

FileInputStream keyStoreStream = new FileInputStream(System.getProperty("keystore"));
KeyStore keyStore = KeyStore.getInstance("jks");
String certPassword = System.getProperty("keystorePassword");
keyStore
.load(keyStoreStream, certPassword.toCharArray());

RestAssured.trustStore(keyStore);

What am I missing? 

Johan Haleby

unread,
Mar 13, 2016, 1:58:33 AM3/13/16
to rest-a...@googlegroups.com
I mean that I had unintentionally (because of lack of understanding) mixed up keystore and truststore in previous versions (prior to 2.9.0). So everywhere you had a keystore earlier (i.e. given().keystore(x)) should be replace with truststore in 2.9.0 (given().truststore(x)). Since I'm not using this myself is extremely hard for me to validate if things work and I'm dependent on the community to help me out testing new version before they are released. So I hope this will work, otherwise we'll have to improve it in the next release.

Regards,
/Johan
Reply all
Reply to author
Forward
0 new messages