Git Product home page Git Product logo

hakky54 / mutual-tls-ssl Goto Github PK

View Code? Open in Web Editor NEW
547.0 19.0 121.0 1.41 MB

🔐 Tutorial of setting up Security for your API with one way authentication with TLS/SSL and mutual authentication for a java based web server and a client with both Spring Boot. Different clients are provided such as Apache HttpClient, OkHttp, Spring RestTemplate, Spring WebFlux WebClient Jetty and Netty, the old and the new JDK HttpClient, the old and the new Jersey Client, Google HttpClient, Unirest, Retrofit, Feign, Methanol, vertx, Scala client Finagle, Featherbed, Dispatch Reboot, AsyncHttpClient, Sttp, Akka, Requests Scala, Http4s Blaze, Kotlin client Fuel, http4k, Kohttp and ktor. Also other server examples are available such as jersey with grizzly. Also gRPC, WebSocket and ElasticSearch examples are included

License: Apache License 2.0

Java 71.62% Gherkin 0.79% Shell 3.99% Kotlin 15.79% Scala 7.81%
tls truststore certificate keystore encryption openssl certificate-signing-request certificate-authority mutual-tls mutual-authentication

mutual-tls-ssl's Introduction

Actions Status Maintainability Rating Quality Gate Status Coverage JDK compatibility: 8+ Apache2 license GitHub stars chart Join the chat at https://gitter.im/hakky54/mutual-tls-ssl

SonarCloud

Open in Gitpod

Mastering two way TLS 🔐 Tweet

Hey, hello there 👋 welcome to this tutorial! I hope you will like it ❤️

This tutorial will walk you through the process of protecting your application with TLS authentication, only allowing access for certain users based on their certificates. This means that you can choose which users are allowed to call your application.

Table of contents

  1. Introduction
  2. Tutorial
  3. Automated scripts
  4. Tested Http Clients
  5. Demo and walk-through video
  6. Contributing

Introduction

This sample project demonstrates a basic setup of a server and a client. The communication between the server and client happens through HTTP, so there is no encryption at all yet. The goal is to ensure that all communication will be encrypted.

Definition

  • Identity: A KeyStore which holds the key pair also known as private and public key
  • TrustStore: A KeyStore containing one or more certificates also known as public key. This KeyStore contains a list of trusted certificates
  • One way authentication (also known as one way tls, one way ssl): Https connection where the client validates the certificate of the counter party
  • Two way authentication (also known as two way tls, two way ssl, mutual authentication): Https connection where the client as well as the counter party validates the certificate, also known as mutual authentication

Usefull links

Some History

I mostly worked with Apache Http Client and therefore I initially created this project with only a http client from Apache. After some time I discovered that there are a lot more Java clients and there also some clients available based on Kotlin and Scala. Configuring ssl/tls can be hard and every client requires a different setup. I want to share the knowledge with other developers as every developer will probably work with a different http client. I also want to provide a cheatsheet for all the developers and the community, see here for a list of 40+ http client) with example client configuration and example http request. The client module has grown over time and contains a-lot dependencies to test all these http clients for java, scala and kotlin. Therefore client module might look complicated. Beware that for this specific reason it will download a-lot dependencies during the initial build. Also GitHub - SSLContext Kickstart came into life during the lifecycle of this project to easily configure all those clients. Every http client can require a different ssl object to enable ssl and this library ensures it can deliver all the types to configure these clients while having a basic ssl configuration.

Tutorial

Starting the server

Minimum requirements:

  1. Java 11
  2. Maven 3.5.0
  3. Eclipse, Intellij IDEA (or any other text editor like VIM)
  4. A terminal

If you want to start instantly without installing any software, click the button below to open the project in an online development environment:

Open in Gitpod

This project contains a maven wrapper, so you can run this project without installing maven. The documentation for this tutorial contains next to the default mvn command also the commands for the maven wrapper.

If you want to run this project with Java 8, you can get an older version with the git command below. And it is recommended to follow the instruction for that specific version, which is available at this page

git checkout tags/java-8-compatible

The server depends on the other components of the project, so run mvn install in the root directory first.

mvn install

Start the server by running the main method of the App Class in the server project or by running the following command from the terminal in the root directory:

cd server/ && mvn spring-boot:run

Or with the maven wrapper

cd server/ && ./../mvnw spring-boot:run

Saying hello to the server (without encryption)

Currently, the server is running on the default port of 8080 without encryption. You can call the hello endpoint with the following curl command in the terminal:

curl -i -XGET http://localhost:8080/api/hello

It should give you the following response:

HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 5
Date: Sun, 11 Nov 2018 14:21:50 GMT

Hello

You can also call the server with the provided client in the client directory. The client is an integration test based on Cucumber, and you can start it by running the ClientRunnerIT class from your IDE or by running the following command from the terminal in the root directory cd client/ && mvn exec:java or with the maven wrapper cd client/ && ./../mvnw exec:java.

There is a Hello.feature file that describes the steps for the integration test. You can find it in the test resources of the client project. There is another way to run both the server and the client and that is with the following command in the root directory: mvn clean verify or with the maven wrapper ./mvnw clean verify. The client sends by default requests to localhost, because it expects the server on the same machine. If the server is running on a different machine you can still provide a custom url with the following VM argument while running the client: -Durl=http://[HOST]:[PORT]

Enabling HTTPS on the server (one-way TLS)

Now, you will learn how to secure your server by enabling TLS. You can do that by adding the required properties to the application properties file named: application.yml

Add the following property:

server:
  port: 8443
  ssl:
    enabled: true

You will probably ask yourself why the port is set to 8443. The port convention for a tomcat server with https is 8443, and for http, it is 8080. So, we could use port 8080 for https connections, but it is a bad practice. See Wikipedia for more information about port conventions.

Restart the server so that it can apply the changes you made. You will probably get the following exception: IllegalArgumentException: Resource location must not be null.

You are getting this message because the server requires a keystore with the certificate of the server to ensure that there is a secure connection with the outside world. The server can provide you more information if you provide the following VM argument: -Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake

To solve this issue, you are going to create a keystore with a public and private key for the server. The public key will be shared with users so that they can encrypt the communication. The communication between the user and server can be decrypted with the private key of the server. Please never share the private key of the server, because others could intercept the communication and will be able to see the content of the encrypted communication.

To create a keystore with a public and private key, execute the following command in your terminal:

keytool -v -genkeypair -dname "CN=Hakan,OU=Amsterdam,O=Thunderberry,C=NL" -keystore server/src/main/resources/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias server -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -ext SubjectAlternativeName:c=DNS:localhost,DNS:raspberrypi.local,IP:127.0.0.1

Now, you need to tell your server where the location of the keystore is and provide the passwords. Paste the following in your application.yml file:

server:
  port: 8443
  ssl:
    enabled: true
    key-store: classpath:identity.jks
    key-password: secret
    key-store-password: secret

Congratulations! You enabled a TLS-encrypted connection between the server and the client! Now, you can try to call the server with the following curl command: curl -i --insecure -v -XGET https://localhost:8443/api/hello

Let's also run the client in the ClientRunnerIT class.

You will see the following error message: java.net.ConnectException: Connection refused (Connection refused). It looks like the client is trying to say hello to the server but the server is not there. The problem is that the client it trying to say hello to the server on port 8080 while it is active on the port 8443. Apply the following changes to the Constants class:

From:

private static final String DEFAULT_SERVER_URL = "http://localhost:8080";

To:

private static final String DEFAULT_SERVER_URL = "https://localhost:8443";

Let's try to run the client again, and you will see that the following message will appear: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target. This means that the client wants to communicate over HTTPS and during the handshake procedure it received the certificate of the server which it doesn't recognizes yet. Therefor you also need to create a truststore. A truststore is a suitcase containing trusted certificates. The client will compare the certificate, which it will receive during the SSL Handshake process with the content of its truststore. If there is a match, then the SSL Handshake process will continue. Before creating the truststores, you need to have the certificates of the server. You can get it with the following command:

Export certificate of the server

keytool -v -exportcert -file server/src/main/resources/server.cer -alias server -keystore server/src/main/resources/identity.jks -storepass secret -rfc

Now, you can create the truststore for the client and import the certificate of the server with the following command:

keytool -v -importcert -file server/src/main/resources/server.cer -alias server -keystore client/src/test/resources/truststore.jks -storepass secret -noprompt

You created the truststore for the client. Unfortunately, the client is not aware of this. Now, you need to tell that it needs to use the truststore with the correct location and password. You also need to tell the client that authentication is enabled. Provide the following property in the application.yml file of the client:

client:
  ssl:
    one-way-authentication-enabled: true
    two-way-authentication-enabled: false
    trust-store: truststore.jks
    trust-store-password: secret

Require the client to identify itself (two-way TLS)

The next step is to require the authentication of the client. This will force the client to identify itself, and in that way, the server can also validate the identity of the client and whether or not it is a trusted one. You can enable this by telling the server that you also want to validate the client with the property client-auth. Put the following properties in the application.yml of the server:

server:
  port: 8443
  ssl:
    enabled: true
    key-store: classpath:identity.jks
    key-password: secret
    key-store-password: secret
    client-auth: need

If you run the client, it will fail with the following error message: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate. This indicates that the certificate of the client is not valid because there is no certificate at all. So, let's create one with the following command

keytool -v -genkeypair -dname "CN=Suleyman,OU=Altindag,O=Altindag,C=NL" -keystore client/src/test/resources/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias client -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth

You also need to create a truststore for the server. Before creating the truststore, you need to have the certificates of the client. You can get it with the following command:

Export certificate of the client

keytool -v -exportcert -file client/src/test/resources/client.cer -alias client -keystore client/src/test/resources/identity.jks -storepass secret -rfc

Create the server truststore with the certificate of the client

keytool -v -importcert -file client/src/test/resources/client.cer -alias client -keystore server/src/main/resources/truststore.jks -storepass secret -noprompt

You created the extra keystore for the client. Unfortunately, the client is not aware of this. Now, you need to tell that it also needs to use the keystore with the correct location and password. You also need to tell the client that two-way-authentication is enabled. Provide the following property in the application.yml file of the client:

client:
  ssl:
    one-way-authentication-enabled: false
    two-way-authentication-enabled: true
    key-store: identity.jks
    key-password: secret
    key-store-password: secret
    trust-store: truststore.jks
    trust-store-password: secret

The server is also not aware of the newly created truststore. Therefore, replace the current properties with the following properties:

server:
  port: 8443
  ssl:
    enabled: true
    key-store: classpath:identity.jks
    key-password: secret
    key-store-password: secret
    trust-store: classpath:truststore.jks
    trust-store-password: secret
    client-auth: need

If you run the client again, you will see that the test passed and that the client received the hello message from the server in a secured way. Congratulations! You finished installing two-way TLS!

Two way TLS based on trusting the Certificate Authority

There is another way to have mutual authentication and that is based on trusting the Certificate Authority. It has pros and cons.

Pros

  • Clients do not need to add the certificate of the server
  • Server does not need to add all the certificates of the clients
  • Maintenance will be less because only the Certificate Authority's certificate validity can expire

Cons

  • You don't have control anymore for which applications are allowed to call your application. You give permission to any application who has a signed certificate by the Certificate Authority.

These are the following steps:

  1. Creating a Certificate Authority
  2. Creating a Certificate Signing Request
  3. Signing the certificate with the Certificate Signing Request
  4. Replace unsigned certificate with a signed one
  5. Trusting the Certificate Authority only

Creating a Certificate Authority

Normally there is already a Certificate Authority, and you need to provide your certificate to have it signed. Here you will create your own Certificate Authority and sign the Client and Server certificate with it. To create one you can execute the following command:

keytool -v -genkeypair -dname "CN=Root-CA,OU=Certificate Authority,O=Thunderberry,C=NL" -keystore root-ca/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias root-ca -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,keyCertSign -ext BasicConstraints=ca:true,PathLen:3

Or you can use the one which is already provided in the repository, see identity.jks

Creating a Certificate Signing Request

To get your certificate signed you need to provide a Certificate Signing Request (.csr) file. This can be created with the following command:

Certificate Signing Request for the server
keytool -v -certreq -file server/src/main/resources/server.csr -keystore server/src/main/resources/identity.jks -alias server -keypass secret -storepass secret -keyalg rsa
Certificate Signing Request for the client
keytool -v -certreq -file client/src/test/resources/client.csr -keystore client/src/test/resources/identity.jks -alias client -keypass secret -storepass secret -keyalg rsa

The Certificate Authority need these csr files to be able to sign it. The next step will be signing the requests.

Signing the certificate with the Certificate Signing Request

Signing the client certificate
keytool -v -gencert -infile client/src/test/resources/client.csr -outfile client/src/test/resources/client-signed.cer -keystore root-ca/identity.jks -storepass secret -alias root-ca -validity 3650 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -rfc
Signing the server certificate
keytool -v -gencert -infile server/src/main/resources/server.csr -outfile server/src/main/resources/server-signed.cer -keystore root-ca/identity.jks -storepass secret -alias root-ca -validity 3650 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -ext SubjectAlternativeName:c=DNS:localhost,DNS:raspberrypi.local,IP:127.0.0.1 -rfc

Replace the unsigned certificate with a signed one

The identity keystore of the server and client still have the unsigned certificate. Now you can replace it with the signed one. The keytool has a strange limitation/design. It won't allow you to directly import the signed certificate, and it will give you an error if you try it. The certificate of the Certificate Authority must be present within the identity.jks.

Export CA Certificate

keytool -v -exportcert -file root-ca/root-ca.pem -alias root-ca -keystore root-ca/identity.jks -storepass secret -rfc

Client

keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore client/src/test/resources/identity.jks -storepass secret -noprompt
keytool -v -importcert -file client/src/test/resources/client-signed.cer -alias client -keystore client/src/test/resources/identity.jks -storepass secret
keytool -v -delete -alias root-ca -keystore client/src/test/resources/identity.jks -storepass secret

Server

keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore server/src/main/resources/identity.jks -storepass secret -noprompt
keytool -v -importcert -file server/src/main/resources/server-signed.cer -alias server -keystore server/src/main/resources/identity.jks -storepass secret
keytool -v -delete -alias root-ca -keystore server/src/main/resources/identity.jks -storepass secret

Trusting the Certificate Authority only

Now you need to configure your client and server to only trust the Certificate Authority. You can do that by importing the certificate of the Certificate Authority into the truststores of the client and server. You can do that with the following two commands:

Client

keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore client/src/test/resources/truststore.jks -storepass secret -noprompt

Server

keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore server/src/main/resources/truststore.jks -storepass secret -noprompt

The truststores still contains the client and server specific certificates and that needs to be removed. You can do that with the following command:

Client

keytool -v -delete -alias server -keystore client/src/test/resources/truststore.jks -storepass secret

Server

keytool -v -delete -alias client -keystore server/src/main/resources/truststore.jks -storepass secret

If you run the client again, you will see that the test passed and that the client received the hello message from the server while based on a certificate which is signed by the Certificate Authority.

Automated script for enabling authentication with TLS

You can also automate all the previous steps described above with the provided scripts at the script directory of this project. Run one of these commands to run the scripts:

  • One way authentication: ./configure-one-way-authentication
  • Two way authentication: ./configure-two-way-authentication-by-trusting-each-other my-company-name
  • Two way authentication by trusting the Certificate Authority: ./configure-two-way-authentication-by-trusting-root-ca my-company-name

Tested clients

Below is a list of already tested clients, plain Java based Http Client configurations can be found at the ClientConfig class. Kotlin and Scala based http client configurations are included as nested class, see here for the full list: service directory. The service directory contains the individual Http Clients with an example requests. All client examples use the same base ssl configuration created within the SSLConfig class.

Java

Kotlin

Scala

Other

This tutorial does not provide examples for the clients listed below, however it is proven to be working with the server provided in this tutorial. To guide you in the right direction you can convert the created java keystore files into pem files which in return can be used with one of the clients listed below. The following Gist page contains the commands to convert a Java keystore to a pem file: Gist - Curl with Java KeyStore

Demo and walk-through video

Java SSL TLS Demo with a server and a client application

Contributing

There are plenty of ways to contribute to this project:

  • Give it a star
  • Share it with a Tweet
  • Join the Gitter room and leave a feedback or help with answering users questions
  • Submit a PR

mutual-tls-ssl's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar eugeneby avatar gitter-badger avatar hakky54 avatar jamesdbloom avatar polyprogrammist avatar skarzhevskyy avatar snyk-bot avatar storset avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mutual-tls-ssl's Issues

alias does not exist

Describe the bug
Error message :
keytool error: java.lang.Exception: Alias <server> does not exist

To Reproduce
Run following command
keytool -keystore client/src/test/resources/truststore.jks -delete -alias server -storepass secret

Expected behavior
alias deleted correctly

Environmental Data:
Debian GNU/Linux 10 (buster)

An explicit instructions which tell how I can run ClientRunnerIT would be beneficial.

I'm trying to run ClinetRunnerIT with TLS mode, and have modified client/src/main/java/nl/altindag/client/Constants.java and been trying to rebuild&run, but it seems the changes has not been affected.

private static final String DEFAULT_SERVER_URL = "https://localhost:8443";

I have done mvn -pl shared-server-resources clean install after changing Constants.java, and been trying mvn exec:java on the client directory. As I'm not an expert of Maven, if the explicit instructions are RM, that would be beneficial for me. Here comes the error log.

https://gist.github.com/hisashiyamaguchi/d85210177d06587600542a399a87e954

Can't call the server with 2-way TLS based on trusting CA authority - javax.net.ssl.SSLHandshakeException: None of the TrustManagers trust this server certificate chain

After going through the instructions for setting up two way TLS bases on trusting the certificate authority, when I run the tests I get the following exception:

javax.net.ssl.SSLHandshakeException: None of the TrustManagers trust this server certificate chain

	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:320)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:263)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:258)
       <...>
	at nl.altindag.client.service.ApacheHttpClientService.executeRequest(ApacheHttpClientService.java:32)
	at nl.altindag.client.stepdefs.HelloStepDefs.iSayHelloWithClient(HelloStepDefs.java:47)
	at ✽.I say hello with "Apache HttpClient"(file:///Users/eugeneh/Development/mutual-tls-ssl/client/src/test/resources/features/Hello.feature:6)
Caused by: java.security.cert.CertificateException: None of the TrustManagers trust this server certificate chain
	at nl.altindag.sslcontext.trustmanager.CompositeX509TrustManager.checkServerTrusted(CompositeX509TrustManager.java:86)
	at io.netty.handler.ssl.util.X509TrustManagerWrapper.checkServerTrusted(X509TrustManagerWrapper.java:63)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:629)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:464)
      <...>
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

Any ideas what I'm doing wrong?

class path resource [identity.jks] cannot be resolved to URL because it does not exist

Describe the bug
When I add the key-store etc. settings to application.yaml and try to restart the server, I get an error:
"java.io.FileNotFoundException: class path resource [identity.jks] cannot be resolved to URL because it does not exist".
The file exists within shared-server-resources.src.main.resources.

To Reproduce
I followed the readme instructions. No code changes except the ssl settings in application.yaml.

Expected behavior
The server should start properly.

Environmental Data:

  • Java Version 19.0.2
  • Maven Version 3.9.6
  • MacOS 14.4.1

Build with mvn is not working

Describe the bug
A clear and concise description of what the bug is.

I'm trying to build with mvn, but it's not working. You can find the output here.
https://gist.github.com/hisashiyamaguchi/7e8ffc6ae8f0db78cd55456078b63204

To Reproduce

  1. Provide as much of a code sample as possible.
    cd server-with-spring-boot/ && mvn spring-boot:run
  2. provide full stack traces if possible

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Environmental Data:

  • Java Version
    java version "1.7.0_261"
    OpenJDK Runtime Environment (rhel-2.6.22.2.amzn2.0.1-x86_64 u261-b02)
    OpenJDK 64-Bit Server VM (build 24.261-b02, mixed mode)

  • Maven Version
    Maven home: /usr/share/apache-maven
    Java version: 1.7.0_261, vendor: Oracle Corporation
    Java home: /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.261-2.6.22.2.amzn2.0.1.x86_64/jre
    Default locale: ja_JP, platform encoding: UTF-8
    OS name: "linux", version: "4.14.214-160.339.amzn2.x86_64", arch: "amd64", family: "unix"

  • OS (Windows/Linux/MacOS)
    Linux ip-172-31-18-187.us-east-2.compute.internal 4.14.214-160.339.amzn2.x86_64 #1 SMP Sun Jan 10 05:53:05 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Additional context
Add any other context about the problem here.

Access Server using curl command

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Hello Hakan Altındağ

Thank you so much for such wonderful article.

Could you please help me with the usage of server side program with CA Authority using curl command as client. How can we pass the client certificates in curl command so as to do two way SSL handshake.

Thanks,
Ishan Aggarwal

Enable Client Cert validation only for a single end point at Service end (Spring Boot Java)?

Is your feature request related to a problem? Please describe.
I would like to learn on how to enable the client cert validation only on a certain end point, say i would like to enable S2S authentication through a /token end point and this end point alone validates the Client Cert and subsequent requests will carry the token generated by this end point? How to do this at Service end?

Describe the solution you'd like
I would like to learn on how to enable the client cert validation only on a certain end point, say i would like to enable S2S authentication through a /token end point and this end point alone validates the Client Cert and subsequent requests will carry the token generated by this end point? How to do this at Service end?

Describe alternatives you've considered
Also, In your Spring Boot example, I could not see how the Client Cert is validated other than loading the cert and validating common name?

Additional context
NA

找不到客户端启动ssl认证的配置

为了让客户端知晓信任库的存在,您还需要告知其信任库的正确位置、密码、以及身份验证已启用。您可以在客户端的application.yml文件中,提供如下属性:

client:

ssl:

one-way-authentication-enabled: true 

two-way-authentication-enabled: false 

trust-store: truststore.jks 

trust-store-password: secret 

看你的文章不是配这个配置就可以了嘛?好像找不到在哪里?

reference:
https://mp.weixin.qq.com/s/i_hU2GXysSGA9JWAbcxoOA

Specif CLASSPATH setting instructions would be beneficial

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

I'm trying to play the tutorial, and getting an error here. The error tells me that "identity.jks" is not found, and I believe I should set CLASSPATH environmental valuable before executing mvn command. If specific instructions are noted on RM, that would be appreciated.

$ mvn spring-boot:run
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-02-14 23:31:39.633 ERROR 26546 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Could not load key store 'classpath:identity.jks'
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:582) ~[spring-context-5.3.3.jar:5.3.3]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:326) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1311) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.4.2.jar:2.4.2]
	at nl.altindag.server.App.main(App.java:10) ~[classes/:na]
Caused by: org.springframework.boot.web.server.WebServerException: Could not load key store 'classpath:identity.jks'
	at org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer.configureSslKeyStore(SslConnectorCustomizer.java:132) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer.configureSsl(SslConnectorCustomizer.java:92) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer.customize(SslConnectorCustomizer.java:57) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.customizeSsl(TomcatServletWebServerFactory.java:349) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.customizeConnector(TomcatServletWebServerFactory.java:327) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:192) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:181) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:159) ~[spring-boot-2.4.2.jar:2.4.2]
	... 9 common frames omitted
Caused by: java.io.FileNotFoundException: class path resource [identity.jks] cannot be resolved to URL because it does not exist
	at org.springframework.util.ResourceUtils.getURL(ResourceUtils.java:137) ~[spring-core-5.3.3.jar:5.3.3]
	at org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer.configureSslKeyStore(SslConnectorCustomizer.java:129) ~[spring-boot-2.4.2.jar:2.4.2]
	... 16 common frames omitted

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.334 s
[INFO] Finished at: 2021-02-14T23:31:39Z
[INFO] Final Memory: 13M/32M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.4.2:run (default-cli) on project server-with-spring-boot: Application finished with exit code: 1 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

I'm guessing the following .jars should be on my CLASSPATH, but if you gave me which ones should be, that would be great.

$ find . -name '*.jar' -print
./.mvn/wrapper/maven-wrapper.jar
./common-server-utils/target/common-server-utils-1.0-SNAPSHOT.jar
./server-with-jersey-grizzly/target/server-with-jersey-grizzly-1.0-SNAPSHOT.jar
./server-with-jersey-grizzly/target/server.jar
./server-with-spring-boot/target/server-with-spring-boot-1.0-SNAPSHOT.jar
./server-with-spring-boot/target/server.jar
./server-with-sun-httpserver/target/server-with-sun-httpserver-1.0-SNAPSHOT.jar
./server-with-sun-httpserver/target/server.jar
./shared-server-resources/target/shared-server-resources-1.0-SNAPSHOT.jar

Invalid Keystore Format

Followed all the steps mentioned in the README to start the server, But getting Invalid keystore format error while running application. Screenshot and logs attached. Please help me setup mtls server.
image
image
mtlsError.txt

javax.net.ssl.SSLException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

Thank you. Everything worked fine until "Two way TLS based on trusting the Certificate Authority" That did not work and resulted in the error shown below. I tried various JDKs and other suggestions from the internet. Nothing worked.

@demo
Scenario: Saying hello to the Server # src/test/resources/features/Hello.feature:4
Given Server is alive # HelloStepDefs.serverIsAlive()
When I say hello # HelloStepDefs.iSayHello()
javax.net.ssl.SSLException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:129)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)

All client tests fail, potential regression from a dependency

Greetings, dear human. Good project, have my star

Describe the bug
I believe a dependency of the client broke it, causing a regression. Specifically how the client finds its truststore

To Reproduce

  • I start a fresh Ubuntu 18 VM. I install requirements - Java 11 and JAVA_HOME properly, maven, clone the repo. My point is that I have double-checked the issue below occurs on a fresh machine.
  • I run "mvn install" from project root. All green
  • I run server in "server-with-spring-boot" and I run client tests. All green
  • I run script "configure-one-way-authentication" successfully. I check manually that the correct artifacts are created properly. I do tried doing this step manually. My point is that the issue below occurs regardless if I do the changes manually or if I use the script in the repo
  • Side note - for server keystore and server truststore I use folder "server-with-spring-boot" instead of folder "shared-server-resources" . I do this, because if I follow the readme as is, the server does not start because it cannot find its keystore. This is not the reason for this ticket but a side note with a viable workaround.
  • I run the server in "server-with-spring-boot" and it starts successfully.
  • I run the following test with curl and it tells me the server behaves as expected
    "curl -i --insecure -v -XGET https://localhost:8443/api/hello"
  • I run client tests. All fail with "PKIX path building failed" error, the same error mentioned in the readme, suggesting the client still cannot find its truststore. I double check the client trust store is where it is expected to be and the server fingerprint is the server fingerprint. The issue is not in the truststore.
  • A friend used this project 2 weeks ago and confirmed this issue was not present back then. This friend used Ubuntu 18 as well. I checked the code changes since then and I do not see anything that could have caused described behavior. I repeated the process described here with a commit hash from 2 weeks ago and the issue still occurs
  • If I setup 2-way tls, client still does not find its truststore
  • All the above suggests that a dependency broke the client. It does not look like author code changes caused the issue

Missing instructions for running ClientRunnerIT

I'm unfamiliar with Java, Springboot, and Maven, so I couldn't quite figure out how to just run the client without running the rest of it (via mvn clean verify).

Would it be possible to provide a command line example? Or to make it so that mvn spring-boot:run starts the client?

Thank you!

Server with HttpsServer from com.sun.net.httpserver

Is your feature request related to a problem? Please describe.
One of the easiest setups of HttpServer (for demo purposes) is to use com.sun.net.httpserver.HttpServer following Java's documentation.
However, when it comes to HttpsServer, Java's documentation lacks any guidance. Not to mention, it has an obscure way to setup it:

 SSLContext sslContext = SSLContext.getInstance (....);
 HttpsServer server = HttpsServer.create();

 server.setHttpsConfigurator (new HttpsConfigurator(sslContext) {
     public void configure (HttpsParameters params) {

         // get the remote address if needed
         InetSocketAddress remote = params.getClientAddress();

         SSLContext c = getSSLContext();

         // get the default parameters
         SSLParameters sslparams = c.getDefaultSSLParameters();
         if (remote.equals (...) ) {
             // modify the default set for client x
         }

         params.setSSLParameters(sslparams);
     }
 });

This is code snippet from documentation. To be frank, this is mostly all there is to documentation.

However, some have went to great lengths in order to try to setup it. Here's one of the more popular answers in Stackoverflow. I've yet to test it out fully. Even if does work, the solution raises few questions, like why do we need to SSLEngine engine = c.createSSLEngine(); (I'm aware of SSLEngine in JSSE hierarchy).

Describe the solution you'd like
The solution would be to add another module, called something like server-with-sun-httpserver and provide implementation with explanation how and why to setup it.

Describe alternatives you've considered
Alternatives are simple Spring-boot or Jersey applications which are covered. However, for demo purposes, sometimes they are "too heavy", thus there is no alternatives.

Additional context
To build an unsecure HttpServer is easy:

//https://docs.oracle.com/en/java/javase/11/docs/api/jdk.httpserver/com/sun/net/httpserver/package-summary.html

public class UnSecureServer {

    public static void main(String[] args) throws IOException {
        var server = HttpServer.create(new InetSocketAddress(8080), 0);
        server.createContext("/", new MyHandler());
        server.start();
    }

    private static class MyHandler implements HttpHandler {
        public void handle(HttpExchange t) throws IOException {
            try (var is = t.getRequestBody(); var os = t.getResponseBody()) {
                read(is); // .. read the request body
                var response = "This is the response";
                t.sendResponseHeaders(200, response.length());
                os.write(response.getBytes());
            }
        }

        private void read(InputStream is) {
            try (var bufferedReader = new BufferedReader(new InputStreamReader(is))) {
                bufferedReader.lines().forEach(System.out::println);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

And I'm currently working on making SecureServer work, but thought that maybe you can share your insights and would like to add this to the current project.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.