Generate certificates on demand
This commit is contained in:
parent
b7340aef27
commit
ae4b458964
32
cert.go
32
cert.go
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -14,27 +15,25 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CertificateStore maps hostnames to certificates.
|
// CertificateStore maps hostnames to certificates.
|
||||||
type CertificateStore struct {
|
type CertificateStore map[string]*tls.Certificate
|
||||||
store map[string]*x509.Certificate // map of hostnames to certificates
|
|
||||||
|
// NewCertificateStore creates and returns a new certificate store.
|
||||||
|
func NewCertificateStore() CertificateStore {
|
||||||
|
return map[string]*tls.Certificate{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCertificateStore() *CertificateStore {
|
// NewCertificate creates and returns a new parsed certificate.
|
||||||
return &CertificateStore{
|
func NewCertificate(host string, duration time.Duration) (tls.Certificate, error) {
|
||||||
store: map[string]*x509.Certificate{},
|
crt, key, err := NewRawCertificate(host, duration)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
}
|
}
|
||||||
|
return tls.X509KeyPair(crt, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CertificateStore) Put(hostname string, cert *x509.Certificate) {
|
// NewRawCertificate creates and returns a raw certificate for the given host.
|
||||||
c.store[hostname] = cert
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CertificateStore) Get(hostname string) *x509.Certificate {
|
|
||||||
return c.store[hostname]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCertificate creates and returns a raw certificate for the given host.
|
|
||||||
// It generates a self-signed TLS certificate and a ED25519 private key.
|
// It generates a self-signed TLS certificate and a ED25519 private key.
|
||||||
func NewCertificate(host string) (crt, key []byte, err error) {
|
func NewRawCertificate(host string, duration time.Duration) (crt, key []byte, err error) {
|
||||||
// Generate a ED25519 private key
|
// Generate a ED25519 private key
|
||||||
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -47,8 +46,7 @@ func NewCertificate(host string) (crt, key []byte, err error) {
|
|||||||
keyUsage := x509.KeyUsageDigitalSignature
|
keyUsage := x509.KeyUsageDigitalSignature
|
||||||
|
|
||||||
notBefore := time.Now()
|
notBefore := time.Now()
|
||||||
validFor := 365 * 24 * time.Hour
|
notAfter := notBefore.Add(duration)
|
||||||
notAfter := notBefore.Add(validFor)
|
|
||||||
|
|
||||||
// Generate the serial number
|
// Generate the serial number
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
12
client.go
12
client.go
@ -185,11 +185,11 @@ type Client struct {
|
|||||||
KnownHosts KnownHosts
|
KnownHosts KnownHosts
|
||||||
|
|
||||||
// CertificateStore contains all the certificates that the client has stored.
|
// CertificateStore contains all the certificates that the client has stored.
|
||||||
CertificateStore *CertificateStore
|
CertificateStore CertificateStore
|
||||||
|
|
||||||
// GetCertificate, if not nil, will be called to determine which certificate
|
// GetCertificate, if not nil, will be called to determine which certificate
|
||||||
// (if any) should be used for a request.
|
// (if any) should be used for a request.
|
||||||
GetCertificate func(req *Request, store *CertificateStore) *tls.Certificate
|
GetCertificate func(hostname string, store CertificateStore) *tls.Certificate
|
||||||
|
|
||||||
// TrustCertificate, if not nil, will be called to determine whether the
|
// TrustCertificate, if not nil, will be called to determine whether the
|
||||||
// client should trust the given certificate.
|
// client should trust the given certificate.
|
||||||
@ -205,14 +205,14 @@ func (c *Client) Send(req *Request) (*Response, error) {
|
|||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||||
if c.GetCertificate != nil {
|
if c.GetCertificate != nil {
|
||||||
if cert := c.GetCertificate(req, c.CertificateStore); cert != nil {
|
if cert := c.GetCertificate(req.Hostname(), c.CertificateStore); cert != nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.Certificate == nil {
|
if req.Certificate != nil {
|
||||||
return &tls.Certificate{}, nil
|
return req.Certificate, nil
|
||||||
}
|
}
|
||||||
return req.Certificate, nil
|
return &tls.Certificate{}, nil
|
||||||
},
|
},
|
||||||
VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
||||||
// Parse the certificate
|
// Parse the certificate
|
||||||
|
@ -4,19 +4,21 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~adnano/go-gemini"
|
gmi "git.sr.ht/~adnano/go-gemini"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
host := "localhost"
|
host := "localhost"
|
||||||
|
|
||||||
crt, key, err := gemini.NewCertificate(host)
|
duration := 365 * 24 * time.Hour
|
||||||
|
crt, key, err := gmi.NewRawCertificate(host, duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gemini.WriteCertificate(host, crt, key); err != nil {
|
if err := gmi.WriteCertificate(host, crt, key); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,22 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~adnano/go-gemini"
|
gmi "git.sr.ht/~adnano/go-gemini"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
scanner = bufio.NewScanner(os.Stdin)
|
scanner = bufio.NewScanner(os.Stdin)
|
||||||
client *gmi.Client
|
client = &gmi.Client{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Initialize the client
|
// Initialize the client
|
||||||
client = &gmi.Client{}
|
|
||||||
client.KnownHosts.Load() // Load known hosts
|
client.KnownHosts.Load() // Load known hosts
|
||||||
client.TrustCertificate = func(hostname string, cert *x509.Certificate, knownHosts *gmi.KnownHosts) error {
|
client.TrustCertificate = func(hostname string, cert *x509.Certificate, knownHosts *gmi.KnownHosts) error {
|
||||||
err := knownHosts.Lookup(hostname, cert)
|
err := knownHosts.Lookup(hostname, cert)
|
||||||
@ -45,6 +46,21 @@ func init() {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.CertificateStore = gmi.NewCertificateStore()
|
||||||
|
client.GetCertificate = func(hostname string, store gmi.CertificateStore) *tls.Certificate {
|
||||||
|
if cert, ok := store[hostname]; ok {
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
// Generate a certificate
|
||||||
|
duration := time.Hour
|
||||||
|
cert, err := gmi.NewCertificate(hostname, duration)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
store[hostname] = &cert
|
||||||
|
return &cert
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendRequest sends a request to the given URL.
|
// sendRequest sends a request to the given URL.
|
||||||
@ -67,7 +83,7 @@ func sendRequest(req *gmi.Request) error {
|
|||||||
case gmi.StatusClassRedirect:
|
case gmi.StatusClassRedirect:
|
||||||
fmt.Println("Redirecting to", resp.Meta)
|
fmt.Println("Redirecting to", resp.Meta)
|
||||||
// Make the request to the same host
|
// Make the request to the same host
|
||||||
red, err := gmi.NewRequestTo(req.Host, resp.Meta)
|
red, err := gmi.NewRequestTo(resp.Meta, req.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user