Skip to content

Instantly share code, notes, and snippets.

@geoah
Last active November 9, 2023 08:43
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save geoah/31340b8155318a3661b1555c191470b5 to your computer and use it in GitHub Desktop.
Save geoah/31340b8155318a3661b1555c191470b5 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)
}
@eurozulu
Copy link

// TODO no idea what the appropriate values for the following would be

The BasicConstraintsValid is for setting 'pathLenConstraint' on the certificate. When true it will be set, otherwise its left unset.
For a self signed, it doesn't make much difference. For a root CA or Inter CA it governs if that CA can issue other CAs.
When isCA is true, MaxPathLen and MaxPathLenZero all control that constraint.
if MaxPathLen = 0, default, The CA can only issue non CA's (server/client certs etc)
When > 0, it can issue intermediate CA's, which can issue their own certs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment