Giter Club home page Giter Club logo

go_mtls_scratchpad's Introduction

go mTLS with multiple certificate issuers and OCSP verification

Sample client/server application which does the following:

  1. Suppose there are two CAs that issue client certificates (ca1, ca2).

  2. These two CAs also issue server a certificate each with different SNIs (tee.collaborator1.com, tee.collaborator2.com)

  3. The server will load the two server certificates from these two CAs.

  4. The server will enforce mTLS where the client certificates are issued by the corresponding CAs.

  5. The client will load the client certificates and server CA certificate for one of the two issuers and connect to the server while specifying an appropriate SNI value

  6. During TLS negotiation, the server will return the server cert that maps to SNI sent by the client

  7. During TLS negotiation, the server will return the set of client certificate CA's it accepts.

  8. Client will verify the server's cert CA issuer from the loaded CA in step 5.

  9. Client will negotiate mTLS using the client certs in step 5.

  10. Server will verify the client certificate using the server certs from step 3.

  11. Server will optionally invoke an external OCSP request to verify live that the client certificate presented has not been revoked


also see


Setup

Run Server

go run server/server.go 

Try to make a connection to the server without specifying any certificates:

The response will show a 'default' server certificate tee.operator.com and the list of CA's it accepts.

The default certificate was shown because we did not specify an SNI value (eg -servername tee.collaborator1.com)

$ openssl s_client --connect localhost:8081

---
Certificate chain
 0 s:C = US, O = Google, OU = Enterprise, CN = tee.operator.com             <<<<<<<<
   i:C = US, O = Operator, OU = Enterprise, CN = Enterprise Root CA

---
Server certificate

subject=C = US, O = Google, OU = Enterprise, CN = tee.operator.com
issuer=C = US, O = Operator, OU = Enterprise, CN = Enterprise Root CA
---
Acceptable client certificate CA names
C = US, O = Collaborator 1, OU = Enterprise, CN = Collaborator 1 Root CA
C = US, O = Collaborator 2, OU = Enterprise, CN = Collaborator 2 Root CA

Run Client

Now run the clients:

## collaborator 1
go run client/client.go --serverName tee.collaborator1.com --serverCertRootCA ca1/root-ca.crt --clientCert ca1/client.crt --clientKey ca1/client.key
## collaborator 2
go run client/client.go --serverName tee.collaborator2.com --serverCertRootCA ca2/root-ca.crt --clientCert ca2/client.crt --clientKey ca2/client.key

What you should see on the server config is a pair of connections from the clients:

$ go run server/server.go 
Subject CN=client.collaborator1.com,OU=Enterprise,O=Google,L=US
Client Certificate hash KK8-CThe_W9i0FWTX7L-DMbvsuCvv5K10m6fWJO--MA

Subject CN=client.collaborator2.com,OU=Enterprise,O=Google,C=US
Client Certificate hash Pkapc_CwEq12asjxrJ5mdn5_1MV2NNDosQ44hxbcwG0

Emit client certificate hash

The sever also generates and prints the client certificate hash for use with Token Binding

$ echo `openssl x509 -in ca1/client.crt -outform DER | openssl dgst -sha256 | cut -d" " -f2` | xxd -r -p - | openssl enc -a | tr -d '=' | tr '/+' '_-'
KK8-CThe_W9i0FWTX7L-DMbvsuCvv5K10m6fWJO--MA

also see

Exported Key Material

Both the client and server will print the common Exported Key Material rfc5705 from the established ConnectionState.ExportKeyingMaterial. This can be used for binding.

The net result is you should get the same per-conn data on both ends of the connection

$ go run server/server.go 
Starting Server..
EKM my_nonce: 1efe1f9d73fcaefb59d10a898592759c6a1bae792f8fbfce80ca7db72202e06b   <<<<<<<<<<<<<<<

$ go run client/client.go --serverName tee.collaborator1.com --serverCertRootCA ca1/root-ca.crt --clientCert ca1/client.crt --clientKey ca1/client.key
EKM my_nonce: 1efe1f9d73fcaefb59d10a898592759c6a1bae792f8fbfce80ca7db72202e06b   <<<<<<<<<<<<<<<

SSL KeyLog

If you want to see the decrypted tls traces using wireshark, enable the SSLKEYLOGFILE writer within the TLSConfig

sudo tcpdump -s0 -iany -w trace.cap port 8081
export SSLKEYLOGFILE=/tmp/keylog.log
go run server/server.go

run client

cd tcpdump/
wireshark trace.cap --log-level=debug -otls.keylog_file:keylog.log
  • Client->Server SNI

images/sni.png

  • Server->Client Client CA issuers

images/ca_requested.png

  • Server->Client HTTP Decrypted

images/decrypted.png

also see

OCSP Check

The server can also make an OCSP API call to verify the client' certificates revocation status at runtime.

To use this, you need to run an ocsp server which in the following is trough openssl

cd ca1/ca_scratchpad

## start OCSP Server
openssl ocsp -index ca/root-ca/db/root-ca.db -port 9999 -rsigner ca/root-ca.crt -rkey ca/root-ca/private/root-ca.key -CA ca/root-ca.crt -text -ndays 10

## in new windows, test ocsp server with revoked cert 
openssl ocsp -CA ca/root-ca.crt -CAfile ca/root-ca.crt -issuer ca/root-ca.crt  -cert certs/revoked.crt -url http://localhost:9999 -resp_text
### note Cert Status: revoked

### test ocsp server with valid cert
openssl ocsp -CA ca/root-ca.crt -CAfile ca/root-ca.crt -issuer ca/root-ca.crt  -cert certs/tee.crt -url http://localhost:9999 -resp_text
### note Cert Status: good

now run the server with ocsp enabled

## run server
$ go run server/server.go --check_ocsp

## run client
$ go run client/client.go --serverName tee.collaborator1.com --serverCertRootCA ca1/root-ca.crt --clientCert ca1/client.crt --clientKey ca1/client.key

The ocsp server will show the request, validation and response

$ openssl ocsp -index ca/root-ca/db/root-ca.db -port 9999 -rsigner ca/root-ca.crt -rkey ca/root-ca/private/root-ca.key -CA ca/root-ca.crt -text -ndays 10

ACCEPT 0.0.0.0:9999 PID=187589
ocsp: waiting for OCSP client connections...
ocsp: Received request, 1st line: POST / HTTP/1.1
OCSP Request Data:
    Version: 1 (0x0)
    Requestor List:
        Certificate ID:
          Hash Algorithm: sha1
          Issuer Name Hash: 10CA8300F670BDF813C03C0CD3DACE5EA8AAB355
          Issuer Key Hash: 750D12CCDB33ED58068CADED0E9E2F00E96FC165
          Serial Number: 01
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: C = US, O = Collaborator 1, OU = Enterprise, CN = Collaborator 1 Root CA
    Produced At: Apr 17 12:20:54 2023 GMT
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: 10CA8300F670BDF813C03C0CD3DACE5EA8AAB355
      Issuer Key Hash: 750D12CCDB33ED58068CADED0E9E2F00E96FC165
      Serial Number: 01
    Cert Status: good                             <<<<<<<<<<<<<<<<<<
    This Update: Apr 17 12:20:54 2023 GMT
    Next Update: Apr 27 12:20:54 2023 GMT


TLS Config without SNI specific client certificates

Notice that the initial TLS negotiation sends back BOTH the client cert CA's it accepts:

Acceptable client certificate CA names
C = US, O = Collaborator 1, OU = Enterprise, CN = Collaborator 1 Root CA
C = US, O = Collaborator 2, OU = Enterprise, CN = Collaborator 2 Root CA

if you would rather only send back the Client Cert CA that is appropriate for the SNI requested, then some more modifications are required for the TLSConfig:

	tlsConfig := &tls.Config{
		GetConfigForClient: func(ci *tls.ClientHelloInfo) (*tls.Config, error) {
			if ci.ServerName == "tee.collaborator1.com" {
				return &tls.Config{
					MinVersion:   tls.VersionTLS13,
					ClientAuth:   tls.RequireAndVerifyClientCert,
					// set client1 root pool
					ClientCAs:    client1_root_pool,
					GetCertificate: func(ci *tls.ClientHelloInfo) (*tls.Certificate, error) {
					// set server1_ cert
						return &server1_cert, nil
					},
				}, nil
			}

			if ci.ServerName == "tee.collaborator2.com" {
				return &tls.Config{
					MinVersion:   tls.VersionTLS13,
					ClientAuth:   tls.RequireAndVerifyClientCert,
					// set client1 root pool
					ClientCAs:    client2_root_pool,
					GetCertificate: func(ci *tls.ClientHelloInfo) (*tls.Certificate, error) {
					// set server2 cert
						return &server2_cert, nil
					},
				}, nil
			}
			return nil, fmt.Errorf("SNI not recognized %s", ci.ServerName)
		},
	}

go_mtls_scratchpad's People

Contributors

salrashid123 avatar

Watchers

 avatar  avatar  avatar

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.