Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutual TLS Support #1366

Closed
michaelquigley opened this issue May 18, 2018 · 13 comments
Closed

Mutual TLS Support #1366

michaelquigley opened this issue May 18, 2018 · 13 comments

Comments

@michaelquigley
Copy link

I'm trying to implement mutual TLS using quic-go.

I have a client like this:

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"github.com/lucas-clemente/quic-go"
	"io/ioutil"
)

func main() {
	caCert, err := ioutil.ReadFile("ca/intermediate/certs/ca-chain.cert.pem")
	if err != nil {
		panic(err)
	}

	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	cert, err := tls.LoadX509KeyPair("ca/intermediate/certs/client.cert.pem", "ca/intermediate/private/client.key.pem")
	if err != nil {
		panic(err)
	}

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      caCertPool,
	}
	tlsConfig.BuildNameToCertificate()

	session, err := quic.DialAddr("127.0.0.1:6262", tlsConfig, nil)
	if err != nil {
		panic(err)
	}

	stream, err := session.OpenStreamSync()
	if err != nil {
		panic(err)
	}

	stream.Write([]byte("hello"))

	buffer := make([]byte, 256)
	n, err := stream.Read(buffer)
	fmt.Printf("read [%d] bytes, [%s]\n", n, string(buffer[:n]))
	stream.Close()
	session.Close(nil)
}

And a server like this:

package main

import (
	"crypto/tls"
	"crypto/x509"
	"github.com/lucas-clemente/quic-go"
	"io/ioutil"
	"log"
)

func main() {
	caCert, err := ioutil.ReadFile("ca/intermediate/certs/ca-chain.cert.pem")
	if err != nil {
		panic(err)
	}

	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	cert, err := tls.LoadX509KeyPair("ca/intermediate/certs/server.cert.pem", "ca/intermediate/private/server.key.pem")
	if err != nil {
		panic(err)
	}

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientCAs:    caCertPool,
		ClientAuth:   tls.RequireAndVerifyClientCert,
	}

	listener, err := quic.ListenAddr("127.0.0.1:6262", tlsConfig, nil)
	if err != nil {
		panic(err)
	}

	for {
		session, err := listener.Accept()
		if err != nil {
			panic(err)
		}
		stream, err := session.AcceptStream()
		if err != nil {
			panic(err)
		}
		log.Println("accepted")

		stream.Write([]byte("hello"))
		stream.Close()
		session.Close(nil)
	}
}

Even though I'm setting tls.Config{ClientAuth: tls.RequireAndVerifyClientCert, the server is ignoring the client certificate. Is mutual TLS implemented in quic-go? If not, do you have any pointers for doing this implementation?

@michaelquigley michaelquigley changed the title Mututal TLS Support Mutual TLS Support May 18, 2018
@marten-seemann
Copy link
Member

gQUIC doesn't support client authentication. IETF QUIC will.

@michaelquigley
Copy link
Author

michaelquigley commented May 19, 2018

Ok. Thanks. But that doesn't actually help.

I need to this to work on top of quic-go. Do you have any suggestions? Let's say I don't care about standards compatibility, and I only care about my version taking to itself.

@marten-seemann
Copy link
Member

I'm working on IETF QUIC support in quic-go, but it's still highly experimental, and should not be used in production yet.

@michaelquigley
Copy link
Author

For my purposes, I'm not interested in standards compatibility... I just need working client authentication. I'm willing to manage my own custom fork. Any advice as to what needs to be implemented to get client authentication happening?

@marten-seemann
Copy link
Member

You can use protocol.VersionTLS.

@jared2501
Copy link
Contributor

jared2501 commented May 19, 2018

@michaelquigley I've gotten quic-go's IETF quic to work with required client certs and I had to fork and make a few changes. My branch is here: https://github.com/jared2501/quic-go/commits/master

@jared2501
Copy link
Contributor

I also had to make a change to Mint, here's the PR: bifurcation/mint#192

@jared2501
Copy link
Contributor

It will require you to be willing to take the time to read and understand the quic-go and mint codebases.

@marten-seemann
Copy link
Member

@jared2501: I think we merged all your changes into quic-go, except for exposing the VersionTLS (which we don't expose since it's still experimental).

Starting with the next quic-go release, we will start exposing "milestone versions" which will allow selecting IETF QUIC on certain commits.

@michaelquigley
Copy link
Author

michaelquigley commented May 19, 2018

@jared2501 Thank you so much for the info! That will definitely help get me pointed in the right direction.

I'll start digging into this, and into your changes. Much appreciated.

@trvll
Copy link

trvll commented Jan 12, 2022

hey @michaelquigley did you succeed in making mTLS work with quic-go?

@michaelquigley
Copy link
Author

hey @michaelquigley did you succeed in making mTLS work with quic-go?

We did at the time, yes. I remember us having to maintain our quic-go dependency at a specific commit for a long time, in order to keep that functional. The details are pretty hazy at this point, and I'm no longer working on that project.

@jlandowner
Copy link

Finally I found this issue but currently mTLS seems to be supported.

Here is an example (fixed the above codes)

// server.go
package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"
	"log"

	"github.com/lucas-clemente/quic-go"
)

func main() {
	ctx := context.Background()

	caCert, err := ioutil.ReadFile("./client.crt")
	if err != nil {
		panic(err)
	}

	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	cert, err := tls.LoadX509KeyPair("./tls.crt", "./tls.key")
	if err != nil {
		panic(err)
	}

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientCAs:    caCertPool,
		ClientAuth:   tls.RequireAndVerifyClientCert,
		NextProtos:   []string{"app"},
	}

	listener, err := quic.ListenAddr("localhost:6262", tlsConfig, nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(listener.Addr().String())

	for {
		session, err := listener.Accept(ctx)
		if err != nil {
			panic(err)
		}
		log.Println("accepted")

		stream, err := session.AcceptStream(ctx)
		if err != nil {
			panic(err)
		}
		log.Println("accepted stream")

		buffer := make([]byte, 256)
		n, err := stream.Read(buffer)
		fmt.Printf("read [%d] bytes, [%s]\n", n, string(buffer[:n]))
		if err != nil && err != io.EOF {
			panic(err)
		}

		n, err = stream.Write([]byte("hello"))
		fmt.Printf("write [%d] bytes\n", n)
		if err != nil && err != io.EOF {
			panic(err)
		}
		stream.Close()
	}
}
// client.go
package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"

	"github.com/lucas-clemente/quic-go"
)

func main() {
	ctx := context.Background()

	caCert, err := ioutil.ReadFile("./tls.crt")
	if err != nil {
		panic(err)
	}

	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

        cert, err := tls.LoadX509KeyPair("./client.crt", "./client.key")
	if err != nil {
		panic(err)
	}

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      caCertPool,
		NextProtos:   []string{"app"},
	}

	session, err := quic.DialAddrContext(ctx, "localhost:6262", tlsConfig, nil)
	if err != nil {
		panic(err)
	}

	stream, err := session.OpenStreamSync(ctx)
	if err != nil {
		panic(err)
	}

	n, err := stream.Write([]byte("hello"))
	fmt.Printf("write [%d] bytes\n", n)
	if err != nil && err != io.EOF {
		panic(err)
	}

	buffer := make([]byte, 256)
	n, err = stream.Read(buffer)
	fmt.Printf("read [%d] bytes, [%s]\n", n, string(buffer[:n]))
	if err != nil && err != io.EOF {
		panic(err)
	}
	stream.Close()
}

When client cert is invalid, CRYPTO_ERROR has occured at stream.Read(buffer) in client.

$ go run client.go
write [5] bytes
read [0] bytes, []
panic: CRYPTO_ERROR (0x12a): tls: bad certificate

However I just thought it would be failed at session.OpenStreamSync(ctx).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants