mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 12:42:34 +02:00
201 lines
4.9 KiB
Go
201 lines
4.9 KiB
Go
package certs
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"errors"
|
|
"math/big"
|
|
"os"
|
|
"time"
|
|
|
|
E "github.com/yusing/go-proxy/internal/error"
|
|
"github.com/yusing/go-proxy/internal/logging"
|
|
"github.com/yusing/go-proxy/internal/utils"
|
|
)
|
|
|
|
const (
|
|
CertsDNSName = "godoxy.agent"
|
|
|
|
caCertPath = "certs/ca.crt"
|
|
caKeyPath = "certs/ca.key"
|
|
srvCertPath = "certs/agent.crt"
|
|
srvKeyPath = "certs/agent.key"
|
|
)
|
|
|
|
func loadCerts(certPath, keyPath string) (*tls.Certificate, error) {
|
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
|
return &cert, err
|
|
}
|
|
|
|
func write(b []byte, f *os.File) error {
|
|
_, err := f.Write(b)
|
|
return err
|
|
}
|
|
|
|
func saveCerts(certDER []byte, key *rsa.PrivateKey, certPath, keyPath string) ([]byte, []byte, error) {
|
|
certPEM, keyPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}),
|
|
pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
|
|
|
if certPath == "" || keyPath == "" {
|
|
return certPEM, keyPEM, nil
|
|
}
|
|
|
|
certFile, err := os.Create(certPath)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer certFile.Close()
|
|
|
|
keyFile, err := os.Create(keyPath)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer keyFile.Close()
|
|
|
|
return certPEM, keyPEM, errors.Join(
|
|
write(certPEM, certFile),
|
|
write(keyPEM, keyFile),
|
|
)
|
|
}
|
|
|
|
func checkExists(certPath, keyPath string) bool {
|
|
certExists, err := utils.FileExists(certPath)
|
|
if err != nil {
|
|
E.LogFatal("cert error", err)
|
|
}
|
|
keyExists, err := utils.FileExists(keyPath)
|
|
if err != nil {
|
|
E.LogFatal("key error", err)
|
|
}
|
|
return certExists && keyExists
|
|
}
|
|
|
|
func InitCerts() (ca *tls.Certificate, srv *tls.Certificate, isNew bool, err error) {
|
|
if checkExists(caCertPath, caKeyPath) && checkExists(srvCertPath, srvKeyPath) {
|
|
logging.Info().Msg("Loading existing certs...")
|
|
ca, err = loadCerts(caCertPath, caKeyPath)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
srv, err = loadCerts(srvCertPath, srvKeyPath)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
logging.Info().Msg("Verifying agent cert...")
|
|
|
|
roots := x509.NewCertPool()
|
|
roots.AddCert(ca.Leaf)
|
|
|
|
srvCert, err := x509.ParseCertificate(srv.Certificate[0])
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
// check if srv is signed by ca
|
|
if _, err := srvCert.Verify(x509.VerifyOptions{
|
|
Roots: roots,
|
|
}); err == nil {
|
|
logging.Info().Msg("OK")
|
|
return ca, srv, false, nil
|
|
}
|
|
logging.Error().Msg("Agent cert and CA cert mismatch, regenerating")
|
|
}
|
|
|
|
// Create the CA's certificate
|
|
caTemplate := x509.Certificate{
|
|
SerialNumber: big.NewInt(1),
|
|
Subject: pkix.Name{
|
|
Organization: []string{"GoDoxy"},
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().AddDate(1000, 0, 0), // 1000 years
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
BasicConstraintsValid: true,
|
|
IsCA: true,
|
|
}
|
|
|
|
caKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
caCertDER, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
certPEM, keyPEM, err := saveCerts(caCertDER, caKey, caCertPath, caKeyPath)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
caCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
ca = &caCert
|
|
|
|
// Generate a new private key for the server certificate
|
|
serverKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
srvTemplate := caTemplate
|
|
srvTemplate.Issuer = srvTemplate.Subject
|
|
srvTemplate.DNSNames = append(srvTemplate.DNSNames, CertsDNSName)
|
|
|
|
srvCertDER, err := x509.CreateCertificate(rand.Reader, &srvTemplate, &caTemplate, &serverKey.PublicKey, caKey)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
certPEM, keyPEM, err = saveCerts(srvCertDER, serverKey, srvCertPath, srvKeyPath)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
agentCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
|
if err != nil {
|
|
return nil, nil, false, err
|
|
}
|
|
|
|
srv = &agentCert
|
|
|
|
return ca, srv, true, nil
|
|
}
|
|
|
|
func NewClientCert(ca *tls.Certificate) ([]byte, []byte, error) {
|
|
// Generate the SSL's private key
|
|
sslKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Create the SSL's certificate
|
|
sslTemplate := &x509.Certificate{
|
|
SerialNumber: big.NewInt(2),
|
|
Subject: pkix.Name{
|
|
Organization: []string{"GoDoxy"},
|
|
CommonName: CertsDNSName,
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().AddDate(1000, 0, 0), // 1000 years
|
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}
|
|
|
|
// Sign the certificate with the CA
|
|
sslCertDER, err := x509.CreateCertificate(rand.Reader, sslTemplate, ca.Leaf, &sslKey.PublicKey, ca.PrivateKey)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return saveCerts(sslCertDER, sslKey, "", "")
|
|
}
|