mtls

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2024 License: MIT Imports: 24 Imported by: 0

README

Go Reference

Mutual TLS

Install

go get aead.dev/mtls@latest

The documentation contains some examples for TLS clients and servers to get started.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	// PrivateKey is the private key used to authenticate
	// to the mTLS server by sending the corresponding
	// public key during the TLS handshake. If not set,
	// then the client does not authenticate itself.
	//
	// The server has to know the corresponding public key
	// identity to verify the client.
	PrivateKey PrivateKey

	// PeerIdentities contains a static mapping from network
	// addresses to identities. When establishing a connection
	// to one of the addresses, the client performs a mTLS handshake
	// expecting a public key that matches the identity assigned
	// to the address.
	//
	// For mapping identities to network address dynamicially set
	// GetPeerIdentity.
	PeerIdentities map[string]Identity

	// GetPeerIdentity is called whenever the client establishes a
	// network connection. The addr is the server's network address
	// as passed to [crypto/tls.Dial] - usually host:port. If it returns
	// true, the client performs a mTLS handshake expecting a public key
	// that matches the returned identity.
	GetPeerIdentity func(addr string) (Identity, bool)

	// Config is the TLS config used when connecting to other TLS servers
	// not within PeerIdentities or for which GetPeerIdentity returns false.
	Config *tls.Config

	// MinVersion contains the minimum TLS version that is acceptable for
	// mTLS connections. For specifying the minimum TLS versions for regular
	// TLS connections use Config.MinVersion.
	//
	// By default, TLS 1.2 is currently used as the minimum. TLS 1.0 is the
	// minimum supported by this package.
	MinVersion uint16

	// MaxVersion contains the maximum TLS version that is acceptable for
	// mTLS connections. For specifying the maximum TLS versions for regular
	// TLS connections use Config.MaxVersion.
	MaxVersion uint16

	// NextProtos is a list of supported application level protocols for mTLS
	// connections, in order of preference. If both peers support ALPN, the
	// selected protocol will be one from this list, and the connection will
	// fail if there is no mutually supported protocol. If NextProtos is empty
	// or the peer doesn't support ALPN, the connection will succeed and
	// ConnectionState.NegotiatedProtocol will be empty.
	//
	// For specifying the supported application level protocols for regular
	// TLS connections use Config.NexProtos.
	NextProtos []string

	// CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites for mTLS
	// connections. The order of the list is ignored. Note that TLS 1.3
	// ciphersuites are not configurable.
	//
	// If CipherSuites is nil, a safe default list is used. The default cipher
	// suites might change over time.
	//
	// For specifying the enabled TLS 1.0-1.2 cipher suites for regular
	// TLS connections use Config.CipherSuites.
	CipherSuites []uint16

	// Dialer contains options for connecting to a network address.
	Dialer net.Dialer
	// contains filtered or unexported fields
}

A Client structure is used to configure a mTLS client.

It establishes a mTLS connection for any server within PeerIdentities or for which GetPeerIdentity returns true. Without a private key, a client does not authenticate itself to the mTLS server(s).

Once a client has been passed to a TLS function, it must no longer be modified.

Example

ExampleClient shows how to configure a simple HTTP client verifying a pinned public key for the server running at "10.1.2.3:443". For all other servers it uses regular TLS certificate verification. The client does not authenticate itself to the server.

Hence, the server running at "10.1.2.3:443" does not have to have a certificate issued by a CA trusted by the client.

package main

import (
	"crypto/tls"
	"log"
	"net"
	"net/http"
	"time"

	"aead.dev/mtls"
)

func main() {
	// The server's identity - required to verify mTLS handshakes with the server.
	const Identity = "h1:dKNb3WhlZ1dxE6VSI1mH7FAd2EPTijEU37RHvkhuT7Y"

	srvIdentity, err := mtls.ParseIdentity(Identity)
	if err != nil {
		log.Fatalf("failed to parse identity: %v", err)
	}

	clientConf := mtls.Client{
		// Map a set of server addresses to identites. The client expects
		// that the server with address X has a public identity Y.
		PeerIdentities: map[string]mtls.Identity{
			"10.1.2.3:443": srvIdentity,
		},
		GetPeerIdentity: nil, // If the mapping isn't static, consider this callback instead

		// Optionally, configure mTLS connections, like minimal supported
		// version or HTTP/2 support.
		MinVersion: tls.VersionTLS13,
		NextProtos: []string{"h2", "http/1.1"},

		// Optionally, provide a custom net.Dialer to customize network timeouts, keepalives, etc.
		Dialer: net.Dialer{
			Timeout: 10 * time.Second,
		},

		// Optionally, if the client should also verify certificates from other TLS servers,
		// e.g. some external systems that serve regular certificates issued by trusted CAs,
		// provide the regular TLS client config here.
		Config: &tls.Config{
			RootCAs: nil, // The set of root CA certificates the client trusts.
		},
	}
	client := http.Client{
		Transport: &http.Transport{
			Proxy:                 http.ProxyFromEnvironment,
			DialTLSContext:        clientConf.DialTLSContext, // Use the mTLS config
			ForceAttemptHTTP2:     true,
			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}
	_ = client

}
Output:

func (*Client) DialTLSContext

func (c *Client) DialTLSContext(ctx context.Context, network, addr string) (net.Conn, error)

DialContext connects to the given network address and initiates a TLS handshake, returning the resulting TLS connection.

The provided Context must be non-nil. If the context expires before the connection is complete, an error is returned. Once successfully connected, any expiration of the context will not affect the connection.

The returned Conn, if any, will always be of type *crypto/tls.Conn.

type ECDSAPrivateKey

type ECDSAPrivateKey struct {
	// contains filtered or unexported fields
}

ECDSAPrivateKey is a PrivateKey for the elliptic curve digital signature algorithm as specified in FIPS 186-4 and SEC 1, Version 2.0.

func GenerateKeyECDSA

func GenerateKeyECDSA(curve elliptic.Curve, random io.Reader) (*ECDSAPrivateKey, error)

GenerateKeyECDSA generates a new ECDSAPrivateKey for the given elliptic curve using entropy from random. If rand is nil, crypto/rand.Reader will be used.

Currently, only the NIST curves P-256, P-384 and P-521 are supported.

func (*ECDSAPrivateKey) Identity

func (pk *ECDSAPrivateKey) Identity() Identity

Identity returns the identity of the ECDSA public key.

func (*ECDSAPrivateKey) MarshalText

func (pk *ECDSAPrivateKey) MarshalText() ([]byte, error)

MarshalText returns a textual representation of the private key.

It returns output equivalent to ECDSAPrivateKey.String

func (*ECDSAPrivateKey) Private

func (pk *ECDSAPrivateKey) Private() crypto.PrivateKey

Private returns the ECDSA private key.

func (*ECDSAPrivateKey) Public

func (pk *ECDSAPrivateKey) Public() crypto.PublicKey

Private returns the ECDSA public key.

func (*ECDSAPrivateKey) String

func (pk *ECDSAPrivateKey) String() string

String returns a string representation of the private key.

Its output is equivalent to ECDSAPrivateKey.MarshalText

func (*ECDSAPrivateKey) UnmarshalText

func (pk *ECDSAPrivateKey) UnmarshalText(text []byte) error

UnmarshalText parses an private key textual representation.

type EdDSAPrivateKey

type EdDSAPrivateKey struct {
	// contains filtered or unexported fields
}

EdDSAPrivateKey is a PrivateKey for the EdDSA signature algorithm as specified in RFC 8032.

func GenerateKeyEdDSA

func GenerateKeyEdDSA(random io.Reader) (*EdDSAPrivateKey, error)

GenerateKeyEdDSA generates a new EdDSAPrivateKey using entropy from random. If random is nil, crypto/rand.Reader will be used.

func (*EdDSAPrivateKey) Identity

func (pk *EdDSAPrivateKey) Identity() Identity

Identity returns the identity of the EdDSA public key.

func (*EdDSAPrivateKey) MarshalText

func (pk *EdDSAPrivateKey) MarshalText() ([]byte, error)

MarshalText returns a textual representation of the private key.

It returns output equivalent to EdDSAPrivateKey.String

func (*EdDSAPrivateKey) Private

func (pk *EdDSAPrivateKey) Private() crypto.PrivateKey

Private returns the EdDSA private key.

func (*EdDSAPrivateKey) Public

func (pk *EdDSAPrivateKey) Public() crypto.PublicKey

Public returns the EdDSA public key.

func (*EdDSAPrivateKey) String

func (pk *EdDSAPrivateKey) String() string

String returns a string representation of the private key.

Its output is equivalent to EdDSAPrivateKey.MarshalText

func (*EdDSAPrivateKey) UnmarshalText

func (pk *EdDSAPrivateKey) UnmarshalText(text []byte) error

UnmarshalText parses a private key textual representation.

type Identity

type Identity struct {
	// contains filtered or unexported fields
}

An Identity is a cryptographic checksum over some data, usually a public key. Two identities, A and B, are equal when A == B is true.

Its zero value is a valid identity but won't match any public key.

func CertificateIdentity added in v0.2.0

func CertificateIdentity(cert *x509.Certificate) Identity

CertificateIdentity returns the identity of the certificate's public key.

func ParseIdentity

func ParseIdentity(s string) (Identity, error)

ParseIdentity parses s and returns it as Identity.

If s is the empty string, it returns the Identity zero value - for which IsZero returns true - and no error.

func PeerIdentity

func PeerIdentity(state *tls.ConnectionState) (Identity, error)

PeerIdentity returns the Identity of the peer's public key or an error if it did not provide any certificate during the TLS handshake.

A TLS client should always receive a certificate containing the server's public key.

A TLS server has to request a certificate and the client might not have one or choose to not send it.

func (Identity) IsZero added in v0.2.0

func (i Identity) IsZero() bool

IsZero returns true if i is the Identity zero value.

func (Identity) MarshalBinary

func (i Identity) MarshalBinary() ([]byte, error)

MarshalBinary returns a binary representation of the identity.

func (Identity) MarshalText

func (i Identity) MarshalText() ([]byte, error)

MarshalText returns a textual representation of the identity.

func (Identity) String

func (i Identity) String() string

String returns a string representation of the identity.

In contrast to Identity.MarshalText, it returns the empty string if i is the zero value.

func (*Identity) UnmarshalBinary

func (i *Identity) UnmarshalBinary(b []byte) error

UnmarshalBinary parses the binary representation of an identity.

func (*Identity) UnmarshalText

func (i *Identity) UnmarshalText(text []byte) error

UnmarshalText parses the textual representation of an identity.

type IdentityError

type IdentityError struct {
	PeerIdentity Identity // Identity received from the connection peer
	Identity     Identity // Expected peer identity
}

IdentityError is an error that occurs when a peer does not provide a certificate during the TLS handshake or sends a public key that doesn't match an expected identity value.

func (IdentityError) Error

func (e IdentityError) Error() string

Error returns the IdentityError's error message.

type PrivateKey

type PrivateKey interface {
	// Private returns the PrivateKey's cryptographic private key.
	Private() crypto.PrivateKey

	// Public returns the PrivateKey's cryptographic public key.
	Public() crypto.PublicKey

	// Identity returns the PrivateKey's [Identity]. It identifies
	// the cryptographic public key.
	Identity() Identity

	// String returns a string representation of the PrivateKey.
	String() string
}

PrivateKey is private key for TLS and mutual TLS connections.

func ParsePrivateKey

func ParsePrivateKey(s string) (PrivateKey, error)

ParsePrivateKey parses s and returns it as PrivateKey.

Currently, ParsePrivateKey either returns a *EdDSAPrivateKey, a *ECDSAPrivateKey or an error.

type Server

type Server struct {
	// PrivateKey is the server's private key used to authenticate
	// to mTLS clients by sending the corresponding public key during
	// the TLS handshake. If not set, the server will use Config for
	// all incoming TLS connections.
	//
	// Clients that try to establish a mTLS connection should send
	// the PrivateKey's public key identity as server name (SNI).
	//
	// Clients have to know the corresponding public key identity to
	// verify the client.
	PrivateKey PrivateKey

	// PeerIdentities contains a static list of accepted peers. If set,
	// the server only accepts an incoming TLS connection from peers
	// that present one of the listed public keys during the TLS handshake.
	//
	// The server requests a certificate from the clinet only if PeerIdentities
	// is not nil, or VerifyPeerIdentity is set.
	PeerIdentities []Identity

	// VerifyPeerIdentity verifies the connection peer's identity during
	// the TLS handshake. If it returns an error, the handshake is aborted.
	//
	// The server requests a certificate from the clinet only if PeerIdentities
	// is not nil, or VerifyPeerIdentity is set.
	VerifyPeerIdentity func(Identity) error

	// Config is the TLS config used for regular TLS clients that don't send
	// the PrivateKey's public key identity as SNI.
	Config *tls.Config

	// MinVersion contains the minimum TLS version that is acceptable for
	// mTLS connections. For specifying the minimum TLS versions for regular
	// TLS connections use Config.MinVersion.
	//
	// By default, TLS 1.2 is currently used as the minimum. TLS 1.0 is the
	// minimum supported by this package.
	MinVersion uint16

	// MaxVersion contains the maximum TLS version that is acceptable for
	// mTLS connections. For specifying the maximum TLS versions for regular
	// TLS connections use Config.MaxVersion.
	MaxVersion uint16

	// NextProtos is a list of supported application level protocols for mTLS
	// connections, in order of preference. If both peers support ALPN, the
	// selected protocol will be one from this list, and the connection will
	// fail if there is no mutually supported protocol. If NextProtos is empty
	// or the peer doesn't support ALPN, the connection will succeed and
	// ConnectionState.NegotiatedProtocol will be empty.
	//
	// For specifying the supported application level protocols for regular
	// TLS connections use Config.NexProtos.
	NextProtos []string

	// CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites for mTLS
	// connections. The order of the list is ignored. Note that TLS 1.3
	// ciphersuites are not configurable.
	//
	// If CipherSuites is nil, a safe default list is used. The default cipher
	// suites might change over time.
	//
	// For specifying the enabled TLS 1.0-1.2 cipher suites for regular
	// TLS connections use Config.CipherSuites.
	CipherSuites []uint16
	// contains filtered or unexported fields
}

A Server structure is used to configure a mTLS server.

Example

ExampleServer shows how to configure a simple HTTP server that serves a certificate with a pinned public key. All clients that know the server's identity are able to verify the server.

The server does not authenticate clients.

package main

import (
	"crypto/tls"
	"log"
	"net/http"

	"aead.dev/mtls"
)

func main() {
	const PrivateKey = "k1:VUP2ZuJttWBTJpv3T8aeJR4jaemYYBgjqAri1uK78No"

	priv, err := mtls.ParsePrivateKey(PrivateKey)
	if err != nil {
		log.Fatalf("failed to parse private key: %v", err)
	}

	srvConf := mtls.Server{
		PrivateKey: priv, // Use the private key for mTLS connections

		// Optionally, configure mTLS connections, like minimal supported
		// version or HTTP/2 support.
		MinVersion: tls.VersionTLS13,
		NextProtos: []string{"h2", "http/1.1"},

		// Optionally, require and verify client public keys. Provide either
		// a static list of client identities or a callback to verify the
		// peer identity of an incoming mTLS connection.
		PeerIdentities:     []mtls.Identity{},
		VerifyPeerIdentity: func(peer mtls.Identity) error { return nil },

		// Optionally, set a public-facing TLS config for regular (e.g. non-mTLS) clients.
		Config: &tls.Config{
			Certificates:   nil, // Provide your public-facing certificates here
			GetCertificate: nil, // or here, if any
		},
	}
	_ = http.Server{
		Addr: ":443",
		TLSConfig: &tls.Config{
			GetConfigForClient: srvConf.GetConfigForClient,
		},
	}
}
Output:

func (*Server) GetConfigForClient

func (s *Server) GetConfigForClient(hello *tls.ClientHelloInfo) (*tls.Config, error)

GetConfigForClient returns a TLS config for a TLS client hello message.

It returns a configuration for mutual TLS when a private key is set and the client sends the corresponding public key identity via SNI. Otherwise, it returns Server.Config.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL