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"
 | 
			
		||||
	"crypto/ed25519"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"math/big"
 | 
			
		||||
@ -14,27 +15,25 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CertificateStore maps hostnames to certificates.
 | 
			
		||||
type CertificateStore struct {
 | 
			
		||||
	store map[string]*x509.Certificate // map of hostnames to certificates
 | 
			
		||||
type CertificateStore map[string]*tls.Certificate
 | 
			
		||||
 | 
			
		||||
// NewCertificateStore creates and returns a new certificate store.
 | 
			
		||||
func NewCertificateStore() CertificateStore {
 | 
			
		||||
	return map[string]*tls.Certificate{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewCertificateStore() *CertificateStore {
 | 
			
		||||
	return &CertificateStore{
 | 
			
		||||
		store: map[string]*x509.Certificate{},
 | 
			
		||||
// NewCertificate creates and returns a new parsed certificate.
 | 
			
		||||
func NewCertificate(host string, duration time.Duration) (tls.Certificate, error) {
 | 
			
		||||
	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) {
 | 
			
		||||
	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.
 | 
			
		||||
// NewRawCertificate creates and returns a raw certificate for the given host.
 | 
			
		||||
// 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
 | 
			
		||||
	_, priv, err := ed25519.GenerateKey(rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -47,8 +46,7 @@ func NewCertificate(host string) (crt, key []byte, err error) {
 | 
			
		||||
	keyUsage := x509.KeyUsageDigitalSignature
 | 
			
		||||
 | 
			
		||||
	notBefore := time.Now()
 | 
			
		||||
	validFor := 365 * 24 * time.Hour
 | 
			
		||||
	notAfter := notBefore.Add(validFor)
 | 
			
		||||
	notAfter := notBefore.Add(duration)
 | 
			
		||||
 | 
			
		||||
	// Generate the serial number
 | 
			
		||||
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								client.go
									
									
									
									
									
								
							@ -185,11 +185,11 @@ type Client struct {
 | 
			
		||||
	KnownHosts KnownHosts
 | 
			
		||||
 | 
			
		||||
	// 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
 | 
			
		||||
	// (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
 | 
			
		||||
	// client should trust the given certificate.
 | 
			
		||||
@ -205,14 +205,14 @@ func (c *Client) Send(req *Request) (*Response, error) {
 | 
			
		||||
		MinVersion:         tls.VersionTLS12,
 | 
			
		||||
		GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
 | 
			
		||||
			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
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if req.Certificate == nil {
 | 
			
		||||
				return &tls.Certificate{}, nil
 | 
			
		||||
			if req.Certificate != nil {
 | 
			
		||||
				return req.Certificate, nil
 | 
			
		||||
			}
 | 
			
		||||
			return req.Certificate, nil
 | 
			
		||||
			return &tls.Certificate{}, nil
 | 
			
		||||
		},
 | 
			
		||||
		VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
 | 
			
		||||
			// Parse the certificate
 | 
			
		||||
 | 
			
		||||
@ -4,19 +4,21 @@ package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.sr.ht/~adnano/go-gemini"
 | 
			
		||||
	gmi "git.sr.ht/~adnano/go-gemini"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	host := "localhost"
 | 
			
		||||
 | 
			
		||||
	crt, key, err := gemini.NewCertificate(host)
 | 
			
		||||
	duration := 365 * 24 * time.Hour
 | 
			
		||||
	crt, key, err := gmi.NewRawCertificate(host, duration)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := gemini.WriteCertificate(host, crt, key); err != nil {
 | 
			
		||||
	if err := gmi.WriteCertificate(host, crt, key); err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,21 +4,22 @@ package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.sr.ht/~adnano/go-gemini"
 | 
			
		||||
	gmi "git.sr.ht/~adnano/go-gemini"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	scanner = bufio.NewScanner(os.Stdin)
 | 
			
		||||
	client  *gmi.Client
 | 
			
		||||
	client  = &gmi.Client{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// Initialize the client
 | 
			
		||||
	client = &gmi.Client{}
 | 
			
		||||
	client.KnownHosts.Load() // Load known hosts
 | 
			
		||||
	client.TrustCertificate = func(hostname string, cert *x509.Certificate, knownHosts *gmi.KnownHosts) error {
 | 
			
		||||
		err := knownHosts.Lookup(hostname, cert)
 | 
			
		||||
@ -45,6 +46,21 @@ func init() {
 | 
			
		||||
		}
 | 
			
		||||
		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.
 | 
			
		||||
@ -67,7 +83,7 @@ func sendRequest(req *gmi.Request) error {
 | 
			
		||||
	case gmi.StatusClassRedirect:
 | 
			
		||||
		fmt.Println("Redirecting to", resp.Meta)
 | 
			
		||||
		// 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 {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user