Skip to content

Instantly share code, notes, and snippets.

@igorzakhar
Forked from geoah/go-mTLS.go
Created April 24, 2021 15:06
Show Gist options
  • Save igorzakhar/33b91317ad5a32e7b244299cfa6021fa to your computer and use it in GitHub Desktop.
Save igorzakhar/33b91317ad5a32e7b244299cfa6021fa to your computer and use it in GitHub Desktop.
package main
import (
"crypto/ed25519"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"net"
"time"
)
func generateEd25519PrivateKey() (ed25519.PrivateKey, error) {
_, k, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
return k, nil
}
func generateTLSCertificate(privateKey ed25519.PrivateKey) (*tls.Certificate, error) {
now := time.Now()
template := &x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
Subject: pkix.Name{
CommonName: "foo",
},
NotBefore: now,
NotAfter: now.AddDate(1, 0, 0), // one year
// TODO no idea what the appropriate values for the following would be
BasicConstraintsValid: true,
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
},
KeyUsage: x509.KeyUsageKeyEncipherment |
x509.KeyUsageDigitalSignature |
x509.KeyUsageCertSign,
}
cert, err := x509.CreateCertificate(
rand.Reader,
template,
template,
privateKey.Public(),
privateKey,
)
if err != nil {
return nil, err
}
outCert := &tls.Certificate{
Certificate: [][]byte{
cert,
},
PrivateKey: privateKey,
}
return outCert, nil
}
func serve() {
// generate a new keypair
privateKey, err := generateEd25519PrivateKey()
if err != nil {
fmt.Println("server: could not generate private key, error:", err)
return
}
// generate a certificate from said key
tlsCertificate, err := generateTLSCertificate(privateKey)
if err != nil {
fmt.Println("server: could not generate certificate, error:", err)
return
}
// construct new TLS config with our certificate
config := tls.Config{
Certificates: []tls.Certificate{
*tlsCertificate,
},
// ClientAuth defines the server's policy in regards to what the client
// is expected to provide.
// Other options for ClientAuth:
// * RequestClientCert
// * RequireAnyClientCert
// * VerifyClientCertIfGiven
// * RequireAndVerifyClientCert
ClientAuth: tls.RequestClientCert,
// To allow self-signed certificates
InsecureSkipVerify: true,
}
// start a tcp server
listener, err := tls.Listen("tcp", "0.0.0.0:1234", &config)
if err != nil {
fmt.Println("server: could not start listening, error:", err)
return
}
fmt.Println("server: ready")
for {
// wait for a new incoming connection
conn, err := listener.Accept()
if err != nil {
fmt.Println("server: could not accept incoming connection, error:", err)
continue
}
// we got a connection
fmt.Println("server: accepted connection from", conn.RemoteAddr())
// get the underlying tls connection
tlsConn, ok := conn.(*tls.Conn)
if !ok {
fmt.Println("server: erm, this is not a tls conn")
return
}
// perform handshake
if err := tlsConn.Handshake(); err != nil {
fmt.Println("client: error during handshake, error:", err)
return
}
// get connection state and print some stuff
state := tlsConn.ConnectionState()
for _, v := range state.PeerCertificates {
fmt.Printf(
"server: remote public key: %x\n",
v.PublicKey,
)
}
// close connection
conn.Close()
}
}
func dial() {
// generate a new keypair
privateKey, err := generateEd25519PrivateKey()
if err != nil {
fmt.Println("server: Could not generate private key, error:", err)
return
}
// generate a certificate from said key
tlsCertificate, err := generateTLSCertificate(privateKey)
if err != nil {
fmt.Println("server: Could not generate certificate, error:", err)
return
}
// construct new TLS config with our certificate
config := tls.Config{
Certificates: []tls.Certificate{
*tlsCertificate,
},
// To allow self-signed certificates
InsecureSkipVerify: true,
}
// constrct a new dialer
dialer := net.Dialer{
Timeout: time.Second,
}
// dial using our new dialer
tlsConn, err := tls.DialWithDialer(&dialer, "tcp", "0.0.0.0:1234", &config)
if err != nil {
fmt.Println("client: could not dial, error:", err)
return
}
// we got a connection
fmt.Println("client: connected to", tlsConn.RemoteAddr())
// perform handshake
if err := tlsConn.Handshake(); err != nil {
fmt.Println("client: error during handshake, error:", err)
return
}
// get connection state and print some stuff
state := tlsConn.ConnectionState()
for _, v := range state.PeerCertificates {
fmt.Printf(
"client: remote public key: %x\n",
v.PublicKey,
)
}
// close connection
tlsConn.Close()
}
func main() {
go serve()
dial()
time.Sleep(time.Second)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment