{"id":16179197,"url":"https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose","last_synced_at":"2025-03-16T10:31:32.688Z","repository":{"id":40259920,"uuid":"113969913","full_name":"jonashackt/spring-boot-rest-clientcertificates-docker-compose","owner":"jonashackt","description":"Example project showing how to access REST endpoints from multiple servers that are secured by different client certificates, using Spring´s RestTemplate \u0026 Docker Compose","archived":false,"fork":false,"pushed_at":"2024-10-24T16:56:43.000Z","size":410,"stargazers_count":22,"open_issues_count":8,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-26T00:14:40.377Z","etag":null,"topics":["certificate-signing-request","client-certificate","docker","docker-compose","integration-test","java-keystore","jks","rest-template","spring-boot","ssl-certificates","testcontainers","truststore"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonashackt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-12T09:25:43.000Z","updated_at":"2024-10-23T03:27:48.000Z","dependencies_parsed_at":"2023-10-03T08:00:38.401Z","dependency_job_id":"9b400b89-1d09-4a74-b19c-ce5ef1abda14","html_url":"https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fspring-boot-rest-clientcertificates-docker-compose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fspring-boot-rest-clientcertificates-docker-compose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fspring-boot-rest-clientcertificates-docker-compose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fspring-boot-rest-clientcertificates-docker-compose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonashackt","download_url":"https://codeload.github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221662788,"owners_count":16859749,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["certificate-signing-request","client-certificate","docker","docker-compose","integration-test","java-keystore","jks","rest-template","spring-boot","ssl-certificates","testcontainers","truststore"],"created_at":"2024-10-10T05:25:54.919Z","updated_at":"2024-10-27T10:25:44.461Z","avatar_url":"https://github.com/jonashackt.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"Multiple Spring Boot servers that are secured with different client certificates - called by RestTemplate\n=============================\n[![Build Status](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/workflows/github/badge.svg)](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/actions)\n[![renovateenabled](https://img.shields.io/badge/renovate-enabled-yellow)](https://renovatebot.com)\n\nThis repository basically forks all the ground work that was done in https://github.com/jonashackt/spring-boot-rest-clientcertificate. This is a basic example, where the client certificate secured server is a Spring Boot Application and the client is just a Testcase that uses Spring´s RestTemplate which is configured to use the client certificate.\n\nIn contrast the present project focusses on the configuration of more than one client certificates and how to access REST endpoints from multiple servers that are secured by different client certificates with Spring´s RestTemplate.\n\nTherefore we use several Spring Boot based microservices that provide different client certificate secured REST endpoint and a separate microservice that accesses these services:\n\n```\n                  -------------------------------------------    \n                 | Docker Network scope                 __   |  \n                 |                         ============|o¬|  |  \n                 |                         =            ¯¯=  |   \n                 |                         = server-alice =  |\n ============    |   ==============   ssl  =              =  |\n =  docker- =    |   =            = -----\u003e ================  |\n = network- = -----\u003e = client-bob =                     __   |\n =  client  =    |   =   __   __  = -----\u003e ============|o¬|  |\n ============    |   ===|o¬|=|o¬|==   ssl  =            ¯¯=  |\n                 |       ¯¯   ¯¯           =  server-tom  =  |\n                 |                         =              =  |\n                 |                         ================  |\n                  -------------------------------------------\n                 \n```\n\n\nFor a general approach on how to generate private keys and certificates and create Java Keystores, have a look into https://github.com/jonashackt/spring-boot-rest-clientcertificate#generate-the-usual-key-and-crt---and-import-them-into-needed-keystore-jks-files\n\n# HowTo Use\n\nEverything you need to run a full build and __complete__ test (incl. Integrationtest of docker-network-client firing up all three microservices that´ll call each other with client certificate support) is this:\n\n```\nmvn clean install\n```\n\nOnly, if you want to check manually, you can do a `docker-compose up -d` and open your Browser with [http:localhost:8080/swagger-ui.html] and fire up a GET-Request to /secretservers with Swagger :)\n\n\n# Integrationtesting with [testcontainers](https://www.testcontainers.org)\n\nAs client-bob only has access to the DNS aliases `server-alice` and `server-tom`, if it itself is part of the Docker (Compose) network and these aliases are used to access both client certificate secured endpoints, we need another way to run an Integration test inside the Docker network scope.\n\nTherefore we use the [testcontainers](https://www.testcontainers.org) and the __docker-network-client__ that just calls __client-bob__ inside the Docker network.\n\ntestcontainers could be []simply integrated by via Maven](https://www.testcontainers.org/usage.html#maven-dependencies):\n\n```\n\t\t\u003cdependency\u003e\n\t\t\t\u003cgroupId\u003eorg.testcontainers\u003c/groupId\u003e\n\t\t\t\u003cartifactId\u003etestcontainers\u003c/artifactId\u003e\n\t\t\t\u003cversion\u003eNewestTestcontainersVersionOnMavenCentral\u003c/version\u003e\n\t\t\t\u003cscope\u003etest\u003c/scope\u003e\n\t\t\u003c/dependency\u003e\n```\n\nAnd the code you need, to fire up all Docker Compose services / Docker Containers is really simple:\n\n```\npackage de.jonashackt;\n\nimport org.apache.http.HttpStatus;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.testcontainers.containers.DockerComposeContainer;\nimport org.testcontainers.containers.wait.strategy.Wait;\n\nimport java.io.File;\n\nimport static io.restassured.RestAssured.when;\nimport static org.hamcrest.Matchers.containsString;\n\n@RunWith(SpringRunner.class)\n@ContextConfiguration()\npublic class ClientTest {\n\n\t@ClassRule\n\tpublic static DockerComposeContainer services =\n\t\t\tnew DockerComposeContainer(new File(\"../docker-compose.yml\"))\n\t\t\t\t\t.withExposedService(\"server-alice\", 8443,Wait.forListeningPort())\n\t\t\t\t\t.withExposedService(\"server-tom\", 8443, Wait.forListeningPort())\n\t\t\t\t\t.withExposedService(\"client-bob\", 8080, Wait.forHttp(\"/swagger-ui.html\").forStatusCode(200)).withLocalCompose(true);\n\n\n\t@Test\n\tpublic void is_client_bob_able_to_call_all_servers_with_client_certs() {\n\n\t\twhen()\n\t\t\t.get(\"http://localhost:8080/secretservers\")\n\t\t.then()\n\t\t\t.statusCode(HttpStatus.SC_OK)\n\t\t\t.assertThat()\n\t\t\t\t.body(containsString(\"Both Servers called - Alice said 'Alice answering!' \u0026 Tom replied 'Tom answering!'.\"));\n\t}\n}\n\n```\n\n# TlDR: How to create multiple keys \u0026 certificates for multiple servers - and add these into appropriate truststores / keystores\n\n\n## server-alice keys and client certificate, truststore \u0026 keystore (see /server-alice/src/main/resources)\n\n#### 1. Private Key: aliceprivate.key\n\n```\nopenssl genrsa -des3 -out aliceprivate.key 1024\n```\n\n- passphrase `alicepassword`\n\n\n#### 2. Certificate Signing Request (CSR): alice.csr\n\n```\nopenssl req -new -key aliceprivate.key -out alice.csr -config alice-csr.conf\n```\n\n__Common Name__: `server-alice`, which will later be a DNS alias inside the Docker network \n\n\n#### 3. self-signed Certificate: alice.crt\n\n```\nopenssl x509 -req -days 3650 -in alice.csr -signkey aliceprivate.key -out alice.crt -extfile alice-csr.conf -extensions v3_req\n```\n\n\n#### 4. Java Truststore Keystore, that inherits the generated self-signed Certificate: alice-truststore.jks\n\n```\nkeytool -import -file alice.crt -alias alicesCA -keystore alice-truststore.jks\n```\n\n__the same password__ `alicepassword`\n\n\n#### 5. Java Keystore, that inherits Public and Private Keys (keypair): alice-keystore.jks\n\n```\nopenssl pkcs12 -export -in alice.crt -inkey aliceprivate.key -certfile alice.crt -name \"alicecert\" -out alice-keystore.p12\n```\n\n__the same password__ `alicepassword`\n\nTo read in KeyStore Explorer\n\n```\nkeytool -importkeystore -srckeystore alice-keystore.p12 -srcstoretype pkcs12 -destkeystore alice-keystore.jks -deststoretype JKS\n```\n\n\n\n## server-tom keys and client certificate, truststore \u0026 keystore (see /server-tom/src/main/resources)\n\n#### 1. Private Key: tomprivate.key\n\n```\nopenssl genrsa -des3 -out tomprivate.key 1024\n```\n\n- passphrase `tompassword`\n\n\n#### 2. Certificate Signing Request (CSR): tom.csr\n\n```\nopenssl req -new -key tomprivate.key -out tom.csr -config tom-csr.conf\n```\n\n__Common Name__: `server-tom`, which will later be a DNS alias inside the Docker network \n\n\n#### 3. self-signed Certificate: tom.crt\n\n```\nopenssl x509 -req -days 3650 -in tom.csr -signkey tomprivate.key -out tom.crt -extfile tom-csr.conf -extensions v3_req\n```\n\n\n#### 4. Java Truststore Keystore, that inherits the generated self-signed Certificate: tom-truststore.jks\n\n```\nkeytool -import -file tom.crt -alias tomsCA -keystore tom-truststore.jks\n```\n\n__the same password__ `tompassword`\n\n\n#### 5. Java Keystore, that inherits Public and Private Keys (keypair): tom-keystore.p12\n\n```\nopenssl pkcs12 -export -in tom.crt -inkey tomprivate.key -certfile tom.crt -name \"tomcert\" -out tom-keystore.p12\n```\n\n__the same password__ `tompassword`\n\n\n\n## client-bob truststore \u0026 keystore (see /server-alice/src/main/resources)\n\n#### 1. Java Truststore Keystore, that inherits the generated self-signed Certificate: client-truststore.jks\n\n```\nkeytool -import -file alice.crt -alias alicesCA -keystore client-truststore.jks\nkeytool -import -file tom.crt -alias tomsCA -keystore client-truststore.jks\n```\n\n__password__ `bobpassword`\n\nIn KeyStore Explorer this should look like this:\n\n![client-truststore](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/blob/master/client-truststore.png)\n\n\n#### 2. Java Keystores, that inherit Public and Private Keys (keypair): copy alice-keystore.p12 \u0026 tom-keystore.p12\n\nAs Apache HttpClient isn´t able to handle [more than one client certificate for the same SSLContext](http://mail-archives.apache.org/mod_mbox/hc-httpclient-users/201109.mbox/%3C1315998630.3176.17.camel@ubuntu%3E), we need to provide two of them. Therefore we don´t need to add two private keys and certificates to one Keystore - we can just use both Keystores we already assembled before. So we copy `alice-keystore.p12` \u0026 `tom-keystore.p12` to clien-bob/src/main/resources and use them in the [RestClientCertConfiguration](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/blob/master/client-bob/src/main/java/de/jonashackt/configuration/RestClientCertConfiguration.java) like this:\n\n```\nimport org.apache.commons.io.FileUtils;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.impl.client.HttpClients;\nimport org.apache.http.ssl.SSLContextBuilder;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.Resource;\nimport org.springframework.http.client.HttpComponentsClientHttpRequestFactory;\n\nimport javax.net.ssl.SSLContext;\nimport java.io.File;\nimport java.io.IOException;\n\n@Configuration\npublic class RestClientCertConfiguration {\n\n    private char[] bobPassword = \"bobpassword\".toCharArray();\n    private char[] tomPassword = \"tompassword\".toCharArray();\n\n    @Value(\"classpath:alice-keystore.p12\")\n    private Resource aliceKeystoreResource;\n\n    @Value(\"classpath:tom-keystore.p12\")\n    private Resource tomKeystoreResource;\n\n    @Value(\"classpath:client-truststore.jks\")\n    private Resource truststoreResource;\n    private char[] alicePassword = \"alicepassword\".toCharArray();\n\n    @Bean\n    public HttpComponentsClientHttpRequestFactory serverTomClientHttpRequestFactory() throws Exception {\n        SSLContext sslContext = SSLContextBuilder\n                .create()\n                .loadKeyMaterial(inStream2File(tomKeystoreResource), tomPassword, tomPassword)\n                .loadTrustMaterial(inStream2File(truststoreResource), bobPassword)\n                .build();\n\n        HttpClient client = HttpClients.custom()\n                .setSSLContext(sslContext)\n                .build();\n\n        return new HttpComponentsClientHttpRequestFactory(client);\n    }\n\n    @Bean\n    public HttpComponentsClientHttpRequestFactory serverAliceClientHttpRequestFactory() throws Exception {\n        SSLContext sslContext = SSLContextBuilder\n                .create()\n                .loadKeyMaterial(inStream2File(aliceKeystoreResource), alicePassword, alicePassword)\n                .loadTrustMaterial(inStream2File(truststoreResource), bobPassword)\n                .build();\n\n        HttpClient client = HttpClients.custom()\n                .setSSLContext(sslContext)\n                .build();\n\n        return new HttpComponentsClientHttpRequestFactory(client);\n    }\n\n    private File inStream2File(Resource resource) {\n        try {\n            File tempFile = File.createTempFile(\"file\", \".tmp\");\n            FileUtils.copyInputStreamToFile(resource.getInputStream(), tempFile);\n            return tempFile;\n        } catch (IOException e) {\n            throw new RuntimeException(\"Problems loading Keystores\", e);\n        }\n    }\n}\n```\n\nNow we´re able to insert individual SSLContexts into Spring´s RestTemplate. Therefore see [ServerClientImpl](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/blob/master/client-bob/src/main/java/de/jonashackt/client/ServerClientImpl.java):\n\n```\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.client.HttpComponentsClientHttpRequestFactory;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.client.RestTemplate;\n\n@Component\npublic class ServerClientImpl implements ServerClient {\n\n    @Autowired\n    private HttpComponentsClientHttpRequestFactory serverAliceClientHttpRequestFactory;\n\n    @Autowired\n    private HttpComponentsClientHttpRequestFactory serverTomClientHttpRequestFactory;\n\n    private RestTemplate restTemplate = new RestTemplate();\n\n    @Override\n    public String callServerAlice() {\n        restTemplate.setRequestFactory(serverAliceClientHttpRequestFactory);\n\n        return restTemplate.getForObject(\"https://server-alice:8443/hello\", String.class);\n    }\n\n    @Override\n    public String callServerTom() {\n        restTemplate.setRequestFactory(serverTomClientHttpRequestFactory);\n\n        return restTemplate.getForObject(\"https://server-tom:8443/hello\", String.class);\n    }\n}\n```\n\n\n\n# Links\n\nhttps://stackoverflow.com/questions/25869428/classpath-resource-not-found-when-running-as-jar\n\nhttps://www.thomas-krenn.com/de/wiki/Openssl_Multi-Domain_CSR_erstellen\n\nhttps://stackoverflow.com/questions/30977264/subject-alternative-name-not-present-in-certificate\n\nhttps://stackoverflow.com/questions/21488845/how-can-i-generate-a-self-signed-certificate-with-subjectaltname-using-openssl\n\n--\u003e this is not the only solution, see `-extfile` and `-extensions` CLI paramters!\n\nhttps://serverfault.com/questions/779475/openssl-add-subject-alternate-name-san-when-signing-with-ca\n\n#### Multiple certificates handling in Java Keystores: \n\nLook into the documentation of Tomcat in section `keyAlias`: http://tomcat.apache.org/tomcat-6.0-doc/config/http.html#SSL_Support\n\nhttps://stackoverflow.com/questions/5292074/how-to-specify-outbound-certificate-alias-for-https-calls\n\nhttps://stackoverflow.com/questions/6370745/can-we-load-multiple-certificates-keys-in-a-key-store\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonashackt%2Fspring-boot-rest-clientcertificates-docker-compose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonashackt%2Fspring-boot-rest-clientcertificates-docker-compose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonashackt%2Fspring-boot-rest-clientcertificates-docker-compose/lists"}