quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.76k stars 2.68k forks source link

SSL/TLS two-way is not working properly on client side #8384

Closed ddewalle closed 8 months ago

ddewalle commented 4 years ago

Describe the bug When running 2 quarkus servers communicating with SSL/TLS two-way between each other, the client key store is not properly managed.

To Reproduce Steps to reproduce the behavior:

Nota: all keys used here follow the chain

  1. Create a server (Bar server)
    mvn io.quarkus:quarkus-maven-plugin:1.3.1.Final:create -DprojectGroupId=org.acme -DprojectArtifactId=bar-server -DclassName="org.acme.bar.BarResource" -Dpath="/bar"

    1.1. In src/main/java/org/acme/bar/BarResource.java, modify hello method to return "hello from bar"

    
    package org.acme.bar;

import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType;

@Path("/bar") public class BarResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
    return "hello from bar";
}

}

1.2. In src/main/resources
- Create bar.cert.pem

-----BEGIN CERTIFICATE----- MIIEjDCCA3SgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwajELMAkGA1UEBhMCRlIx DzANBgNVBAgMBkZyYW5jZTEUMBIGA1UEBwwLRm9vQmFyIENpdHkxDzANBgNVBAoM BkZPT0JBUjEPMA0GA1UECwwGRk9PQkFSMRIwEAYDVQQDDAlGT09CQVJfQ0EwHhcN MjAwNDAzMTk0MjA4WhcNMjEwNDAzMTk0MjA4WjBnMQswCQYDVQQGEwJGUjEPMA0G A1UECAwGRnJhbmNlMRQwEgYDVQQHDAtGb29CYXIgQ2l0eTEPMA0GA1UECgwGRk9P QkFSMQwwCgYDVQQLDANCQVIxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAKyoyHVesx0Urn/8/5a3biRryDLlrotUyrUb FzqtZxb1wLe8SfWUeWi8kodF2xFWpBgukVtd6QVbLZb464a8liLikYYMnA2SswEP +3E8IY0HxVWcqImGCj4AzZ2MZ69E6yTid89xO5FWZUaj6zAwOyBq8ZQkgWW+cq95 w2PkTmjlUHXmRqmz3SkbxTpzmAD0+WDZW7DPDenEUhOazI/KVrvgAq2B3JpWaKSR n5I3qeVZYafrjA1b+XfkzUKWXwbcLmIyo0FpNfA6cn4wjk577h7uVo9ShYvn6jAV ZlizuNcdHw11id9u+mfwQHCpz4eE6czoekWaw5MvaUU77LXqHkECAwEAAaOCAT0w ggE5MAwGA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgeAMCgGCWCGSAGG+EIB DQQbFhlGb29iYXIgQ2xpZW50IENlcnRpZmljYXRlMB0GA1UdDgQWBBQeXLtl2O7N DAJAS/unyo7GdAtV4zCBpwYDVR0jBIGfMIGcgBRWDDet2wPbck92OiwGLwJBS0J+ paFupGwwajELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZyYW5jZTEUMBIGA1UEBwwL Rm9vQmFyIENpdHkxDzANBgNVBAoMBkZPT0JBUjEPMA0GA1UECwwGRk9PQkFSMRIw EAYDVQQDDAlGT09CQVJfQ0GCFFJ59U6dSWnEy+80Kq25C9rRiHZMMA4GA1UdDwEB /wQEAwIF4DATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEA pKK7GbqJEU2tfGyIr343fntUQ4/XfxIMWIFK1a0UoupXsvxA0fbwqXiXIhFjCWyA TcjuW9PKZhc/4sCWb848Tnuo5IidCdDuQlwgBxVW4n2TEN+iA1RVydHc1vwZ6XGY 3DeYi+vs5DNQVXn98ekWNH0DwDLadckYQ9rupi4q9fGnGVBeEsLGs0y6+W5yPIx+ CAGNJ9scizUgp/ZigPm8NhDkFQZryzzmXjdcfUrog6+lINlv2I73CpKRw7CdNYHl gBdIV68zd5rtMonCQZfG/FXpM5LHHksuzY2lrrOM17+drInc0pVRIPrPF946vAtx 3Zl5H3LWF8n744G5/jyhTw== -----END CERTIFICATE-----


- Create bar.key.pem

-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-256-CBC,D0B26B71A06AEA179E7A2ED0EE33ECB7

Fgh0hi/jgv9q0OZH/PPrq6N67JIMve2yWpgviSP8Gqrf4T4aFTOhxiT7fjw10iqs 59/uDveAALfEjyakV0t+F40HoCsF5EresnAftlDPrcxD7ThGM7d3CSDOFPpv2jdG jOhQy6C7KCQXdHtFj+57pr/XlqJS4PjrWExp1cnCKPra/r1SCdMPv+xdRoplL2wn 8U8J6En7WvfZVaB0f/hDw2FVPz4mAtlSqYNBbUQHTBsitykNKwUKo6J0udcOQ3lC xklnfW7/0HNN/0Ax/9U8FwSlAzFtkg8NrNqs3Mkw3G2xwhDeksiy3si1P2cNu580 DRGHp7jd3kJwFVSUtxVzlMBy+ZE9dOPqTbluwUIeO4JZuF56hZ2/u3gOweiH1OHD lqxrgJadLkn8YOp4A5D1QM7mkU5v+e0qxdMv3vimOAwtTBsJv4btoEuJvA3wpLyH aCVe2cWLCgcueQKdmC2GvOA1KDF5iL3DQrEGtbKXI9qLXs24927tcPnAm675WAhx ZBFQ00zqjtFn1jbUSeaZ+A+0Qqlr1ZnVRMFhOoqUZ2e+I4/5jwi1U9ecPFQWa03b BC+g6uGxD3Z3fAUZ6RG+u2NXHD11pSfwVE4Ce+CpC0OZjllLeB+ndMuAEUKVXAqo 3c3OenOtjvlOG07EsTg3PFPVW8CIt8+fVWgMmwml1t+fnr7+Z7T6mZGOuYcKv/8c AzOKow2N5sJLSGjFe5+h5l8NDymCWLaZJ2CKm4x+JpQvISF0kzv6n3pa/oEQeHHD AGDU7gwdzPlGAHnWzF/Vltwc33Rpg6R1K95W4RfrOu2YPuAyjJtGIrmyILT6XHDn ffgaPb9JmxjYfVSY8OGc1aMAsHbzNPNAttaN27YRFIZpvMbNpCLp5pI5rsYk3XZU k8uKE1hP6KPPpRo5qorBj9ZWYBVkeRfCw7sIhwSpU75Dg27ZJDqFZwQdWozBhDgO Gd5H1lzjBMmFhOMowX1MrGVU2uZgk9bTAj8I2PmKXKJEvLz9nvdABKO6c8G+3ojT H51LGEKg4o29TLKwFeQvPIJZTUmTj4d9nOQM0GSWro82gsRsqYj31ha7DBWmabZW EPe5vBhyqHDj86uxyJv7PaSkiJSIqv/tDOpc07DYjmYPXIxXucKt7gHNlel8pM/0 0TgwxrTqqyyxHTmOD1C4yDkrSKC3DKIBxnyUC/07FVSG0ODpHL5B9wp2CaTYxJuA HjrcYyOEYGd9Ru9DNX330Ztjw6pGIQSCRq1M/z7H6VRbbMqvfkuXNCJqcD3/4xWq 06yHxyb6A65rCcXRl7s7yYofUR18jA0jByXoZWWLybv7A1/qvoz0db7j2WJfalH7 eXd9OWtgaE7oLAJIWzCSsx2pIWh2rQ7d2qZioew1UW/hFNTO8NOVoCC4f5qwWeGD xC0QmCyLBmqJLg7o2cjpHHSbeewByn3Np2MZPPF94oBWeOBd8IGwF+umXZJaZ4Lo ZPgnV7YJN7tH8HexLCWz35gE2//BsB5qFlaTOKbHoM96ouKAxX7/o2LLxkas2Y0F Y05iSlBGv6jQN4c2fCL4lxSuy5uHB3yPKjkuVAM0c/5wJ6NCyWEnrCCuRYhUC+Ny -----END RSA PRIVATE KEY-----


- Generate a PKCS12 keystore with openssl

openssl pkcs12 -export -in bar.cert.pem -inkey bar.key.pem -out bar.p12 -name "BAR"

Pass phrase for private key -> barPass
Export Password -> barIdentityPass

- Convert the PKCS12 keystore to JKS keytstore with java keytool

keytool -importkeystore -destkeystore bar.jks -deststorepass barJksKeystorePass -srckeystore bar.p12 -srcstoretype PKCS12 -srcstorepass barIdentityPass


1.3. In src/main/resources/application.properties
```properties
quarkus.http.port = 8081
quarkus.http.ssl-port = 8434
quarkus.http.ssl.protocols = TLSv1.3
quarkus.http.ssl.certificate.key-store-file = bar.jks
quarkus.http.ssl.certificate.key-store-file-type = JKS
quarkus.http.ssl.certificate.key-store-password = barJksKeystorePass
quarkus.http.ssl.certificate.trust-store-file = barTrust.jks
quarkus.http.ssl.certificate.trust-store-file-type = JKS
quarkus.http.ssl.certificate.trust-store-password = barJksTruststorePass
quarkus.http.ssl.client-auth = REQUIRED

At this point, we did not have already defined barTrust.jks

N.B. 'quarkus.http.ssl.protocols = TLSv1.3' is optional: the issue will be the same with TLSv1.2 but the trace will be different during debug

  1. Create a client (Foo server) acting as client for Bar server and as server for simple unsecured http requests for testing
    mvn io.quarkus:quarkus-maven-plugin:1.3.1.Final:create -DprojectGroupId=org.acme -DprojectArtifactId=foo-server -DclassName="org.acme.foo.FooResource" -Dpath="/foo" -Dextensions="rest-client"

    2.1. In src/main/java/org/acme/foo create BarService.java

    
    package org.acme.foo;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType;

@Path("/bar") @RegisterRestClient public interface BarService {

@GET
@Produces(MediaType.TEXT_PLAIN)
String hello();

}

2.2. In src/main/java/org/acme/foo/FooResource.java, modify the class as bellow
```java
package org.acme.foo;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/foo")
public class FooResource {

    @Inject
    @RestClient
    BarService barService;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello from foo -> " + barService.hello();
    }

}

2.3. In src/main/resources

UGem0SWBcxFJLjRDICkugGljflHcOx03xLPaPoblEUTv4dwj2quJ91gbeGgQGTMp Jp716aR5BjdBmHS0Ow0LKc7/MrswmEvf7aFvXu4b898g27/9VLGrVwE2csEeSV9N xSMpqbBt9zNKf6UpYAq7DxW3ZzZ5AWffQYwJ5a+aVEsZmJAWFPrzJYoOX9fkYWdv g+hcguUzgBsPKiUXS/mcC0KsjVVp4MNiWmKqyNsZI6sypIOcOzfZl/TbdFA8SRyP aLYFHowOoIsZu7hoybvmpY6OVaQLJ8ykdY8u+qNmaa44Jx12w6MtvSkEE56tlt6w YvWtbLNBswJUjXQWgxrz0eBG64AyzufQUvwEOw1RLI1wNfeBqK5cPbcigoIT+jHZ 2wLh99ykHFqClutv2btmhVF6R6gl2A+c3D9Ezgbz9CLkiaraC3m3Pm9CAZ3BT5YR o1yzKyewtl3Ftj3r2b2MdO10kcz/AStPxoyFu8iJlfL/Jgr9FecUued0Yl+FBEFa NWuaZf6FFdIPOGMk5pp9TERNQYjvms2ARDUXYJOHyMW4YxfDYSlEyQ4FdXX7zBcW TeYXBlVm7O1ooQ5yc4ljQgCBNIl77P29fTu/8FTTARyuPRRCNCWApigCAJEHuSv/ YL2pIW5mRNXZqMLx9ZdIRNoA9LUCVIDxHfV3tNJYtFVS0v6g4ojBaAeF68BKV9DB QNY4VBRzx2kaP1YT9v43TDLP8/zY6JVG5cNScAkhhCydrGk9JUgWLD7id3dyR83U u6sgwab6xqwog3TJIiludsJs+D6STJ/jhQBmoNdLFgDrjIcbdCpZYwLK5LU5WJoT 2H8DuqQoNQCsd4rg+LzAAWxGv58hmR0I/5ubjREzxLsOdUft5AVDTzM64z3adRiA BVqJX4JBAO5M/Ge+6PhWkIE5fXDvnfnugH2KTruektnado0hpKCSnZYYXtLnnDgC 6pfK4JVJIkiOTf2H3RpwdeN4gDWmZLi88QW6PcOhKqTamqXal2elTHXfIxhm4u28 hvLNY5BwWGD/f6hqZIKo2WIpx1fCbDzTX5KXbs2oHnx0UUnuCnOdGqEL/zFJVmhB TUv7PGngSI3HSMHFVq+PKjfpK7aJbaLwJGPV5vPBcHyl5mV/KcV+2tsiSQA5G16x XWun8PwOeD1QOY0RQQlYMzL4yRiEo3Rz8xJ3GeRAvhTxjUFww1SIjEi9aQgKvaom yWMr/8lfBVGVuMWNFu50MlDbj+FSlUntpQj3KPonH/B54aMU0Q4r9u9zN+W36Ujc h+8EGy3akyUDb6M4ofIkqxfSpHKwq6HnOgtNHIXNs+5dSBYc2l1QEKzDzKwI0DOc yS8SWJQAoPJBBZSDpyM1wFDRKEQUwKhFUu9TIPZiaYPq4jd8e5bhK2iRWi64wkE9 M3w6rT0EwPbyg+PPSXU/lNOzGcFxLiRD582ESKfV4Vhl029YXPqL4oem6gD2Zmqd 4QPWt35+Ime4F9EsnhP/2cS1HkJximUAqpiH1bMWvVHwUHx+qXvsTI2BLxAJMDst 85vyRwzg8U7g7QWVv/HAVJIv8tcdm7GkvXAeUZJPxbvP6/yWWNq0BEjpBjVixtLA -----END RSA PRIVATE KEY-----


- Generate a PKCS12 keystore with openssl

openssl pkcs12 -export -in foo.cert.pem -inkey foo.key.pem -out foo.p12 -name "FOO"

Pass phrase for private key -> fooPass
Export Password -> fooIdentityPass

- Convert the PKCS12 keystore to JKS keytstore with java keytool

keytool -importkeystore -destkeystore foo.jks -deststorepass fooJksKeystorePass -srckeystore foo.p12 -srcstoretype PKCS12 -srcstorepass fooIdentityPass


2.4. In src/main/resources/application.properties
```properties
quarkus.http.port = 8080
quarkus.http.ssl-port = 8433
quarkus.http.ssl.protocols = TLSv1.3
quarkus.http.ssl.certificate.key-store-file = foo.jks
quarkus.http.ssl.certificate.key-store-file-type = JKS
quarkus.http.ssl.certificate.key-store-password = fooJksKeystorePass
quarkus.http.ssl.certificate.trust-store-file = fooTrust.jks
quarkus.http.ssl.certificate.trust-store-file-type = JKS
quarkus.http.ssl.certificate.trust-store-password = fooJksTruststorePass

org.acme.foo.BarService/mp-rest/url=https://localhost:8434

At this point, we did not have already defined fooTrust.jks

N.B. 'quarkus.http.ssl.protocols = TLSv1.3' is optional: the issue will be the same with TLSv1.2 but the trace will be different during debug

  1. Create the truststores
    • In Bar project, we create a truststore that trust Foo: go to src/main/resources keytool -import -noprompt -trustcacerts -alias FOO -file ../../../../foo-server/src/main/resources/foo.cert.pem -keystore barTrust.jks -storepass barJksTruststorePass

4.0. Build the 2 quarkus servers (without the tests)

4.1. Launch the 2 quarkus servers

What is expected

curl http://localhost:8080/foo

should return

hello from foo -> hello from bar

What is the real result

curl http://localhost:8080/foo

Will result to an error on foo-server (as client of bar-server)

javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate

Behind the scene

Do not care here if you have an exception about 'io.vertx.core.VertxException: Thread blocked'

Workaround

The command

curl http://localhost:8080/foo

returns now the expected

hello from foo -> hello from bar

Environment

ddewalle commented 4 years ago

Previous workaround is broken since Quarkus 1.5.1.Final (latest 1.7.1.Final too)

By adding '-Djavax.net.ssl.keyStore' in the previous workaround 'mvnw' command do not fix the problem anymore...

Is this a regression that appeared in version 1.5.1.Final? Or maybe I'm not doing it right? It is quite disconcerting. Is there another approach than the one I used? If so, a description in the QUARKUS - GUIDES would be helpful for SSL/TLS two-way.

However, the root of the problem remains relevant: why have to specify '-Djavax.net.ssl.keyStore' in the 'mvnw' command when 'quarkus.http.ssl.certificate.key-store-file' has been specified in the 'application.properties' file?

Environments

ddewalle commented 4 years ago

The previous regression has been introduced in 'quarkus-rest-client' 1.5.1-Final.

  1. Workaround

The class io.quarkus.restclient.runtime.RestClientBase directed me to this workaround because it uses 'Optional maybeKeyStore = this.getOptionalProperty("%s/mp-rest/keyStore", String.class);' and the eclipse microprofile documentation for rest client explains how to do.

In src/main/resources/application.properties of Foo server, add

org.acme.foo.BarService/mp-rest/keyStore=classpath:/foo.jks
org.acme.foo.BarService/mp-rest/keyStoreType = JKS
org.acme.foo.BarService/mp-rest/keyStorePassword = fooJksKeystorePass

The full file should be now:

quarkus.http.port = 8080
quarkus.http.ssl-port = 8433
quarkus.http.ssl.protocols = TLSv1.3
quarkus.http.ssl.certificate.key-store-file = foo.jks
quarkus.http.ssl.certificate.key-store-file-type = JKS
quarkus.http.ssl.certificate.key-store-password = fooJksKeystorePass
quarkus.http.ssl.certificate.trust-store-file = fooTrust.jks
quarkus.http.ssl.certificate.trust-store-file-type = JKS
quarkus.http.ssl.certificate.trust-store-password = fooJksTruststorePass

org.acme.foo.BarService/mp-rest/url=https://localhost:8434
org.acme.foo.BarService/mp-rest/keyStore=classpath:/foo.jks
org.acme.foo.BarService/mp-rest/keyStoreType = JKS
org.acme.foo.BarService/mp-rest/keyStorePassword = fooJksKeystorePass

Environment for this final test

  1. QUARKUS - GUIDES should be improved

QUARKUS - HTTP REFERENCE and/or QUARKUS - USING THE REST CLIENT should really clearly address the subject which otherwise is a real headache. ;-)

And so, the root of the problem is now: why have to specify '.../mp-rest/keyStore' when 'quarkus.http.ssl.certificate.key-store-file' has been specified in the 'application.properties' file? Ok if it is to override the property but if not?

nanisrikanthvaddi commented 3 years ago

I also face the same issue where , Spring rest client take care everything if you give your JKS with your private key . where as quarkus has lot of issues .

Did a debug of io.vertx.mutiny.ext.web.client.HttpRequest, understand the Client keys are not pick up when making HTTP request

bennetelli commented 2 years ago

I am also facing the same issue.. thanks @ddewalle for opening it :-)

dameng58 commented 2 years ago

@ddewalle I added the following configuration in application.properties image image

and it doesn't work, but the mistake changed: image image

and Related debug logs: {"timestamp":"2022-03-25T08:41:37.574Z","sequence":1475,"loggerClassName":"org.jboss.resteasy.client.jaxrs.i18n.LogMessages_$logger","loggerName":"org.jboss.resteasy.client.jaxrs.i18n","level":"DEBUG","message":"RESTEASY004672: Client send processing failure."}

sberyozkin commented 11 months ago

@cescoffier @geoand Can you please review this issue ?

geoand commented 11 months ago

@BarDweller do you want to have a look at this?

cescoffier commented 10 months ago

Pretty sure mTLS is supported. An up to date reproducer would be more than welcome.

geoand commented 8 months ago

Closing for lack of feedback