forked from 0xERR0R/blocky
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: cleanup TLS self-signed cert generation
It's now actually a self-signed cert, instead of using a useless CA.
- Loading branch information
1 parent
4e1df50
commit 75d68db
Showing
4 changed files
with
98 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,118 +1,67 @@ | ||
package util | ||
|
||
import ( | ||
"bytes" | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"encoding/pem" | ||
"math" | ||
"crypto/x509/pkix" | ||
"fmt" | ||
"math/big" | ||
mrand "math/rand" | ||
"time" | ||
) | ||
|
||
const ( | ||
caExpiryYears = 10 | ||
certExpiryYears = 5 | ||
certSerialMaxBits = 128 | ||
certExpiryYears = 5 | ||
) | ||
|
||
//nolint:funlen | ||
func CreateSelfSignedCert() (tls.Certificate, error) { | ||
// Create CA | ||
ca := &x509.Certificate{ | ||
//nolint:gosec | ||
SerialNumber: big.NewInt(int64(mrand.Intn(math.MaxInt))), | ||
NotBefore: time.Now(), | ||
NotAfter: time.Now().AddDate(caExpiryYears, 0, 0), | ||
IsCA: true, | ||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, | ||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, | ||
BasicConstraintsValid: true, | ||
} | ||
|
||
caPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||
// TLSGenerateSelfSignedCert returns a new self-signed cert for the given domains. | ||
// | ||
// Being self-signed, no client will trust this certificate. | ||
func TLSGenerateSelfSignedCert(domains []string) (tls.Certificate, error) { | ||
serialMax := new(big.Int).Lsh(big.NewInt(1), certSerialMaxBits) | ||
serial, err := rand.Int(rand.Reader, serialMax) | ||
if err != nil { | ||
return tls.Certificate{}, err | ||
} | ||
|
||
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey) | ||
if err != nil { | ||
return tls.Certificate{}, err | ||
} | ||
template := &x509.Certificate{ | ||
SerialNumber: serial, | ||
|
||
caPEM := new(bytes.Buffer) | ||
if err = pem.Encode(caPEM, &pem.Block{ | ||
Type: "CERTIFICATE", | ||
Bytes: caBytes, | ||
}); err != nil { | ||
return tls.Certificate{}, err | ||
} | ||
Subject: pkix.Name{Organization: []string{"Blocky"}}, | ||
DNSNames: domains, | ||
|
||
caPrivKeyPEM := new(bytes.Buffer) | ||
|
||
b, err := x509.MarshalECPrivateKey(caPrivKey) | ||
if err != nil { | ||
return tls.Certificate{}, err | ||
} | ||
|
||
if err = pem.Encode(caPrivKeyPEM, &pem.Block{ | ||
Type: "EC PRIVATE KEY", | ||
Bytes: b, | ||
}); err != nil { | ||
return tls.Certificate{}, err | ||
} | ||
NotBefore: time.Now(), | ||
NotAfter: time.Now().AddDate(certExpiryYears, 0, 0), | ||
|
||
// Create certificate | ||
cert := &x509.Certificate{ | ||
//nolint:gosec | ||
SerialNumber: big.NewInt(int64(mrand.Intn(math.MaxInt))), | ||
DNSNames: []string{"*"}, | ||
NotBefore: time.Now(), | ||
NotAfter: time.Now().AddDate(certExpiryYears, 0, 0), | ||
SubjectKeyId: []byte{1, 2, 3, 4, 6}, | ||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, | ||
KeyUsage: x509.KeyUsageDigitalSignature, | ||
KeyUsage: x509.KeyUsageDigitalSignature, | ||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | ||
} | ||
|
||
certPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||
if err != nil { | ||
return tls.Certificate{}, err | ||
return tls.Certificate{}, fmt.Errorf("unable to generate private key: %w", err) | ||
} | ||
|
||
certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, &certPrivKey.PublicKey, caPrivKey) | ||
der, err := x509.CreateCertificate(rand.Reader, template, template, &privKey.PublicKey, privKey) | ||
if err != nil { | ||
return tls.Certificate{}, err | ||
return tls.Certificate{}, fmt.Errorf("cert creation from template failed: %w", err) | ||
} | ||
|
||
certPEM := new(bytes.Buffer) | ||
if err = pem.Encode(certPEM, &pem.Block{ | ||
Type: "CERTIFICATE", | ||
Bytes: certBytes, | ||
}); err != nil { | ||
return tls.Certificate{}, err | ||
} | ||
|
||
certPrivKeyPEM := new(bytes.Buffer) | ||
|
||
b, err = x509.MarshalECPrivateKey(certPrivKey) | ||
// Parse the generated DER back into a useable cert | ||
// This avoids needing to do it for each TLS handshake (see tls.Certificate.Leaf comment) | ||
cert, err := x509.ParseCertificate(der) | ||
if err != nil { | ||
return tls.Certificate{}, err | ||
} | ||
|
||
if err = pem.Encode(certPrivKeyPEM, &pem.Block{ | ||
Type: "EC PRIVATE KEY", | ||
Bytes: b, | ||
}); err != nil { | ||
return tls.Certificate{}, err | ||
return tls.Certificate{}, fmt.Errorf("generated cert DER could not be parsed: %w", err) | ||
} | ||
|
||
keyPair, err := tls.X509KeyPair(certPEM.Bytes(), certPrivKeyPEM.Bytes()) | ||
if err != nil { | ||
return tls.Certificate{}, err | ||
tlsCert := tls.Certificate{ | ||
Certificate: [][]byte{der}, | ||
PrivateKey: privKey, | ||
Leaf: cert, | ||
} | ||
|
||
return keyPair, nil | ||
return tlsCert, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package util | ||
|
||
import ( | ||
"crypto/tls" | ||
"crypto/x509" | ||
"sync" | ||
|
||
. "github.com/onsi/gomega" | ||
) | ||
|
||
// FIXME: think about how modules can be moved so this can be in helpertest | ||
// Right now it can't since it depends on util, and util has test code | ||
// that uses helpertest. | ||
// Maybe creating util_test would fix it. | ||
|
||
const tlsTestServerName = "test.blocky.invalid" | ||
|
||
type tlsData struct { | ||
ServerCfg *tls.Config | ||
ClientCfg *tls.Config | ||
} | ||
|
||
// Lazy init | ||
// | ||
//nolint:gochecknoglobals | ||
var ( | ||
initTLSData sync.Once | ||
tlsDataStorage tlsData | ||
) | ||
|
||
func getTLSData() tlsData { | ||
initTLSData.Do(func() { | ||
cert, err := TLSGenerateSelfSignedCert([]string{tlsTestServerName}) | ||
Expect(err).Should(Succeed()) | ||
|
||
tlsDataStorage.ServerCfg = &tls.Config{ | ||
Certificates: []tls.Certificate{cert}, | ||
MinVersion: tls.VersionTLS13, | ||
} | ||
|
||
certPool := x509.NewCertPool() | ||
certPool.AddCert(cert.Leaf) | ||
|
||
tlsDataStorage.ClientCfg = &tls.Config{ | ||
RootCAs: certPool, | ||
ServerName: tlsTestServerName, | ||
MinVersion: tls.VersionTLS13, | ||
} | ||
}) | ||
|
||
return tlsDataStorage | ||
} | ||
|
||
// TLSTestServerConfig returns a TLS Config for use by test servers. | ||
func TLSTestServerConfig() *tls.Config { | ||
return getTLSData().ServerCfg.Clone() | ||
} | ||
|
||
// TLSTestServerConfig returns a TLS Config for use by test clients. | ||
// | ||
// This is required to connect to a test TLS server, otherwise TLS verification fails. | ||
func TLSTestClientConfig() *tls.Config { | ||
return getTLSData().ClientCfg.Clone() | ||
} |