sahaya / rest-assured

Automatically exported from code.google.com/p/rest-assured
0 stars 0 forks source link

Certificate authentication with own truststore doesn't work #205

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Do an request over https to a service with a certificate sign by a CA that's 
not in default cacerts
2.
3.

What is the expected output? What do you see instead?

What version of the product are you using? On what operating system?
1.7, Windows 7

Please provide any additional information below.
Running this test class generates:
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:352)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:692)
    at org.apache.http.conn.scheme.SchemeSocketFactoryAdaptor.connectSocket(SchemeSocketFactoryAdaptor.java:65)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
    at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294)
    at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
    at org.apache.http.client.HttpClient$execute.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
    at com.jayway.restassured.internal.RequestSpecificationImpl$RestAssuredHttpBuilder.doRequest(RequestSpecificationImpl.groovy:1341)
    at com.jayway.restassured.internal.http.HTTPBuilder.doRequest(HTTPBuilder.java:489)
    at com.jayway.restassured.internal.http.HTTPBuilder.request(HTTPBuilder.java:438)
    at com.jayway.restassured.internal.http.HTTPBuilder$request.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
    at com.jayway.restassured.internal.RequestSpecificationImpl.sendHttpRequest(RequestSpecificationImpl.groovy:917)
    at com.jayway.restassured.internal.RequestSpecificationImpl.this$2$sendHttpRequest(RequestSpecificationImpl.groovy)
    at com.jayway.restassured.internal.RequestSpecificationImpl$this$2$sendHttpRequest.callCurrent(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at com.jayway.restassured.internal.RequestSpecificationImpl.sendRequest(RequestSpecificationImpl.groovy:784)
    at com.jayway.restassured.internal.RequestSpecificationImpl.this$2$sendRequest(RequestSpecificationImpl.groovy)
    at com.jayway.restassured.internal.RequestSpecificationImpl$this$2$sendRequest.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
    at com.jayway.restassured.internal.filter.RootFilter.filter(RootFilter.groovy:28)
    at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
    at com.jayway.restassured.internal.filter.FilterContextImpl.next(FilterContextImpl.groovy:47)
    at com.jayway.restassured.filter.FilterContext$next.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
    at com.jayway.restassured.internal.RequestSpecificationImpl.invokeFilterChain(RequestSpecificationImpl.groovy:717)
    at com.jayway.restassured.internal.RequestSpecificationImpl$invokeFilterChain.callCurrent(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:149)
    at com.jayway.restassured.internal.RequestSpecificationImpl.applyPathParamsAndSendRequest(RequestSpecificationImpl.groovy:1106)
    at com.jayway.restassured.internal.RequestSpecificationImpl.this$2$applyPathParamsAndSendRequest(RequestSpecificationImpl.groovy)
    at com.jayway.restassured.internal.RequestSpecificationImpl$this$2$applyPathParamsAndSendRequest.callCurrent(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:149)
    at com.jayway.restassured.internal.RequestSpecificationImpl.get(RequestSpecificationImpl.groovy:127)
    at com.jayway.restassured.specification.RequestSender$get.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
    at com.jayway.restassured.internal.ResponseSpecificationImpl.get(ResponseSpecificationImpl.groovy:218)
    at se.pulsencombine.integration.externalservices.RestTestCase.testGetRoot(RestTestCase.java:59)
    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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:69)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:48)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:292)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

public class RestTestCase {

    @BeforeClass
    public static void beforeClass() {
        RestAssured.keystore(new File(RestTestCase.class.getClassLoader().getResource("trust.jks").getPath()), "changeit");
        RestAssured.authentication = RestAssured.certificate(RestTestCase.class.getClassLoader().getResource("client.jks").toString(), "changeit");
        RestAssured.baseURI = "https://localhost/";
        RestAssured.port = 443;
    }

    @Test
    public void testGetRoot() {
        expect().body(containsString(FinancialFilesResource.DISPLAYNAME)).get("/");
    }

}

Running the same class with the RestAssured.keystore line removed and
with -Djavax.net.ssl.trustStore=target/test-classes/trust.jks and the test goes 
ok:
public class RestTestCase {

    @BeforeClass
    public static void beforeClass() {
        //RestAssured.keystore(new File(RestTestCase.class.getClassLoader().getResource("trust.jks").getPath()), "changeit");
        RestAssured.authentication = RestAssured.certificate(RestTestCase.class.getClassLoader().getResource("client.jks").toString(), "changeit");
        RestAssured.baseURI = "https://localhost/";
        RestAssured.port = 443;
    }

    @Test
    public void testGetRoot() {
        expect().body(containsString(FinancialFilesResource.DISPLAYNAME)).get("/");
    }

}

Original issue reported on code.google.com by henrik.k...@gmail.com on 12 Nov 2012 at 10:49

GoogleCodeExporter commented 9 years ago
Thanks for reporting. I'm not quite sure how to fix this. If you have any ideas 
please let us know.

Original comment by johan.ha...@gmail.com on 12 Nov 2012 at 8:18

GoogleCodeExporter commented 9 years ago
I've managed to get this to work. You have to create a SSLSocketFactory with 
both a keystore and a truststore. I added this method to AuthConfig:

    public void certificate( String certURL, String password, String trustURL, String trustPassword ) 
            throws GeneralSecurityException, IOException {

        KeyStore keyStore = KeyStore.getInstance( KeyStore.getDefaultType() );
        InputStream jksStream = new URL(certURL).openStream();
        try {
            keyStore.load( jksStream, password.toCharArray() );
        } finally { jksStream.close(); }

        KeyStore trustStore = KeyStore.getInstance( KeyStore.getDefaultType() );
        jksStream = new URL(trustURL).openStream();
        try {
            trustStore.load( jksStream, trustPassword.toCharArray() );
        } finally { jksStream.close(); }

        SSLSocketFactory ssl = new SSLSocketFactory(keyStore, password, trustStore);
        ssl.setHostnameVerifier( SSLSocketFactory.STRICT_HOSTNAME_VERIFIER );

        builder.getClient().getConnectionManager().getSchemeRegistry()
            .register( new Scheme("https", ssl, 443) );
    }

I now changed the beforeClass() method like this:
    @BeforeClass
    public static void beforeClass() throws Exception {
        final CertAuthWithTrustStoreScheme scheme = new CertAuthWithTrustStoreScheme();
        scheme.setCertURL(AbstractExternalServicesFunctionalTestCase.class.getClassLoader().getResource("client.jks").toString());
        scheme.setPassword("xdr537");
        scheme.setTrustURL(AbstractExternalServicesFunctionalTestCase.class.getClassLoader().getResource("trust.jks").toString());
        scheme.setTrustPassword("changeit");
        RestAssured.authentication = scheme;
        RestAssured.baseURI = "https://localhost/";
        RestAssured.port = 443;
    }

The best would be if you could use the keystore set by RestAssured.keystore(), 
but I didn't found a way to do that...

Original comment by henrik.k...@gmail.com on 21 Nov 2012 at 5:33

Attachments:

GoogleCodeExporter commented 9 years ago
Thanks a lot for your help! I'll look into your code as soon as I find some 
time.

Original comment by johan.ha...@gmail.com on 22 Nov 2012 at 8:57

GoogleCodeExporter commented 9 years ago
I've look into this briefly. I think there may be some overlap with the new 
AuthConfig method and features that's already available in Rest Assured. Have a 
look at Rest Assured.setKeystore and you'll find the similarities if you dig 
further into the code (see KeystoreSpecImpl). What may be missing is the 
possibility to set a trust url. Perhaps that's what missing and in that case we 
wouldn't need a new auth config method?

Original comment by johan.ha...@gmail.com on 28 Nov 2012 at 7:29

GoogleCodeExporter commented 9 years ago
You already have an overlap then using the current AuthConfig.certificate() 
method because the SSLSocketFactory created by KeystoreSpecImpl isn't used if 
RestAssured.authentication is set to CertAuthScheme. I you extend 
KeystoreSpecImpl to set both cert and trust store, I think you should remove 
AuthConfig.certificate() and CertAuthScheme so this only can be configured in 
one way.

But I think the best solution would be to set the trust store as today with 
RestAssured.setKeyStore() (maybe renamed to setTruststore()) and make 
AuthConfig look if a keystore is set by setKeyStore() and use it the creating 
the SSLSocketFactory.

I also found that the port is hardcoded in AuthConfig.certificate() to 443. 
Shouldn't this be read from RestAssured.port?

Original comment by henrik.k...@gmail.com on 23 Jan 2013 at 1:42

GoogleCodeExporter commented 9 years ago
I'm really really busy right now and it would be really helpful if you would 
like help out by creating a pull request at github for this. 

It sounds very strange that the port should not be hard-coded, that's probably 
a bug as well.

Original comment by johan.ha...@gmail.com on 23 Jan 2013 at 2:06

GoogleCodeExporter commented 9 years ago
Some links:

http://stackoverflow.com/questions/7256955/java-sslexception-hostname-in-certifi
cate-didnt-match
http://javaskeleton.blogspot.com/2010/07/avoiding-peer-not-authenticated-with.ht
ml
http://tech.chitgoks.com/2011/04/24/how-to-avoid-javax-net-ssl-sslpeerunverified
exception-peer-not-authenticated-problem-using-apache-httpclient/

Original comment by johan.ha...@gmail.com on 19 Mar 2013 at 7:17

GoogleCodeExporter commented 9 years ago
Thanks to Ian Forsey this should now be fixed:

RequestSpecBuilder request = ...
    request.setKeystore("truststore.jks", "password");

    CertAuthScheme scheme = new CertAuthScheme();
    scheme.setCertURL("clientcert.p12");
    scheme.setCertType("pkcs12");
    scheme.setPassword("password");
    scheme.setPort(8443);
    request.setAuthentication(scheme);

Original comment by johan.ha...@gmail.com on 23 Apr 2013 at 5:39

GoogleCodeExporter commented 9 years ago
You can also do:

given().auth().certificate("clientcert.p12",  "password", "pkcs12", 8443, 
scheme). .. 

Original comment by johan.ha...@gmail.com on 23 Apr 2013 at 6:06

GoogleCodeExporter commented 9 years ago
That should be given().auth().certificate("clientcert.p12",  "password", 
"pkcs12", 8443, trustStore)

Original comment by johan.ha...@gmail.com on 23 Apr 2013 at 6:09

GoogleCodeExporter commented 9 years ago
Great! When will this be available in a released version?
I also saw that the default port in CertAuthScheme was 433 instead of 443...

Original comment by henrik.k...@gmail.com on 8 May 2013 at 8:46

GoogleCodeExporter commented 9 years ago
Thanks for pointing out, I've changed it to 443. 

I don't know when the release will be available in Maven central but I've 
published a new snapshot version to Sonatype if you want to try it out. Depend 
on version 1.8.1-SNAPSHOT after having added the following maven repo:

<repositories>
        <repository>
            <id>sonatype</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
            <snapshots />
        </repository>
</repositories>

Original comment by johan.ha...@gmail.com on 8 May 2013 at 1:10

GoogleCodeExporter commented 9 years ago

Original comment by johan.ha...@gmail.com on 8 May 2013 at 1:11