From cad2d251853f1c505a2aa3d90b2966a4435fed1a Mon Sep 17 00:00:00 2001 From: adnano Date: Mon, 28 Sep 2020 00:38:11 -0400 Subject: [PATCH] Make the default client generate certificates --- README.md | 5 +++-- gemini.go | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cdf5f2f..e593e18 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,8 @@ Gemini takes advantage of client certificates for authentication. If a server responds with `StatusCertificateRequired`, clients will generate a certificate for the site and resend the request with the provided certificate. -In order for this to work, clients must specify the fields `CertificateStore` -and `GetCertificate`: +The default client handles this for you. Other clients must specify the fields +`CertificateStore` and `GetCertificate`: ```go // Initialize the certificate store. @@ -109,6 +109,7 @@ client.GetCertificate = func(hostname string, store gmi.CertificateStore) *tls.C } ``` + Servers can then authenticate their clients with the fingerprint of their certificates. diff --git a/gemini.go b/gemini.go index 8f5299f..944bd9d 100644 --- a/gemini.go +++ b/gemini.go @@ -2,8 +2,10 @@ package gmi import ( + "crypto/tls" "crypto/x509" "sync" + "time" ) // Status codes. @@ -54,11 +56,27 @@ func init() { setupDefaultClientOnce.Do(setupDefaultClient) return knownHosts.Lookup(hostname, cert) } + DefaultClient.GetCertificate = func(hostname string, store CertificateStore) *tls.Certificate { + // If the certificate is in the store, return it + if cert, ok := store[hostname]; ok { + return cert + } + // Otherwise, generate a certificate + duration := time.Hour + cert, err := NewCertificate(hostname, duration) + if err != nil { + return nil + } + // Store and return the certificate + store[hostname] = &cert + return &cert + } } var setupDefaultClientOnce sync.Once func setupDefaultClient() { + DefaultClient.CertificateStore = NewCertificateStore() DefaultClient.KnownHosts.Load() }