rio/internal/certificates/certificate.go

137 lines
3.5 KiB
Go

package certificates
import (
"crypto/rsa"
"crypto/tls"
"encoding/base64"
"encoding/pem"
"time"
"bytes"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
)
func RenewCertificate(old *CertificateWrapper, acmeClient *lego.Client) (CertificateWrapper, error) {
pk, _ := base64.StdEncoding.DecodeString(old.PrivateKeyEncoded)
res := certificate.Resource{
PrivateKey: pk,
Certificate: old.Certificate,
CSR: old.CSR,
}
new, err := acmeClient.Certificate.Renew(res, true, false, "")
if err != nil {
return CertificateWrapper{}, err
}
// Convert the new certificate into a wrapper struct
tlsCert, err := tls.X509KeyPair(new.Certificate, new.PrivateKey)
if err != nil {
return CertificateWrapper{}, err
}
wrapper := CertificateWrapper{
TlsCertificate: &tlsCert,
Domain: old.Domain,
NotAfter: time.Now().Add(time.Hour * 24 * 60),
PrivateKeyEncoded: base64.StdEncoding.EncodeToString(new.PrivateKey),
Certificate: new.Certificate,
CSR: new.CSR,
}
return wrapper, nil
}
func ObtainNewCertificate(domains []string, acmeClient *lego.Client) (CertificateWrapper, error) {
req := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
}
cert, err := acmeClient.Certificate.Obtain(req)
if err != nil {
return CertificateWrapper{}, err
}
tlsCert, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
if err != nil {
return CertificateWrapper{}, err
}
wrapper := CertificateWrapper{
TlsCertificate: &tlsCert,
Domain: cert.Domain,
//NotAfter: tlsCert.Leaf.NotAfter,
NotAfter: time.Now().Add(time.Hour * 24 * 60),
PrivateKeyEncoded: base64.StdEncoding.EncodeToString(cert.PrivateKey),
Certificate: cert.Certificate,
CSR: cert.CSR,
}
return wrapper, nil
}
// Generate a fallback certificate for the domain.
func MakeFallbackCertificate(pagesDomain string) (*CertificateWrapper, error) {
key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048)
if err != nil {
return nil, err
}
notAfter := time.Now().Add(time.Hour * 24 * 7)
cert := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: pagesDomain,
Organization: []string{"Pages Server"},
},
NotAfter: notAfter,
NotBefore: time.Now(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(
rand.Reader,
&cert,
&cert,
&key.(*rsa.PrivateKey).PublicKey,
key,
)
if err != nil {
return nil, err
}
out := &bytes.Buffer{}
err = pem.Encode(out, &pem.Block{
Bytes: certBytes,
Type: "CERTIFICATE",
})
if err != nil {
return nil, err
}
outBytes := out.Bytes()
res := &certificate.Resource{
PrivateKey: certcrypto.PEMEncode(key),
Certificate: outBytes,
IssuerCertificate: outBytes,
Domain: pagesDomain,
}
tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey)
if err != nil {
return nil, err
}
return &CertificateWrapper{
TlsCertificate: &tlsCertificate,
Domain: pagesDomain,
NotAfter: notAfter,
PrivateKeyEncoded: base64.StdEncoding.EncodeToString(certcrypto.PEMEncode(key)),
Certificate: outBytes,
CSR: []byte{},
}, nil
}