Skip to content

Commit

Permalink
Add ability to generate full set of certs for chia (#139)
Browse files Browse the repository at this point in the history
* Add basic functions to load/generate/write signed certs

* Generate the full list of required certs/keys

* Fix lint issues
  • Loading branch information
cmmarslender authored Jul 31, 2024
1 parent 07e84b6 commit f1c52af
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,6 @@ main
bin/
test/
.idea/*
/*.crt
/*.key
/test-cert-gen
3 changes: 3 additions & 0 deletions pkg/tls/certs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# About the private key here

The key is intentionally committed to this repo. They're coincidentally also committed to chia-blockchain repo. Not a bug, don't open a bug-bounty submission about this, as it will be closed as a duplicate.
19 changes: 19 additions & 0 deletions pkg/tls/chia_ca.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDKTCCAhGgAwIBAgIUXIpxI5MoZQ65/vhc7DK/d5ymoMUwDQYJKoZIhvcNAQEL
BQAwRDENMAsGA1UECgwEQ2hpYTEQMA4GA1UEAwwHQ2hpYSBDQTEhMB8GA1UECwwY
T3JnYW5pYyBGYXJtaW5nIERpdmlzaW9uMB4XDTIxMDEyMzA4NTEwNloXDTMxMDEy
MTA4NTEwNlowRDENMAsGA1UECgwEQ2hpYTEQMA4GA1UEAwwHQ2hpYSBDQTEhMB8G
A1UECwwYT3JnYW5pYyBGYXJtaW5nIERpdmlzaW9uMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAzz/L219Zjb5CIKnUkpd2julGC+j3E97KUiuOalCH9wdq
gpJi9nBqLccwPCSFXFew6CNBIBM+CW2jT3UVwgzjdXJ7pgtu8gWj0NQ6NqSLiXV2
WbpZovfrVh3x7Z4bjPgI3ouWjyehUfmK1GPIld4BfUSQtPlUJ53+XT32GRizUy+b
0CcJ84jp1XvyZAMajYnclFRNNJSw9WXtTlMUu+Z1M4K7c4ZPwEqgEnCgRc0TCaXj
180vo7mCHJQoDiNSCRATwfH+kWxOOK/nePkq2t4mPSFaX8xAS4yILISIOWYn7sNg
dy9D6gGNFo2SZ0FR3x9hjUjYEV3cPqg3BmNE3DDynQIDAQABoxMwETAPBgNVHRMB
Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAEugnFQjzHhS0eeCqUwOHmP3ww
/rXPkKF+bJ6uiQgXZl+B5W3m3zaKimJeyatmuN+5ST1gUET+boMhbA/7grXAsRsk
SFTHG0T9CWfPiuimVmGCzoxLGpWDMJcHZncpQZ72dcy3h7mjWS+U59uyRVHeiprE
hvSyoNSYmfvh7vplRKS1wYeA119LL5fRXvOQNW6pSsts17auu38HWQGagSIAd1UP
5zEvDS1HgvaU1E09hlHzlpdSdNkAx7si0DMzxKHUg9oXeRZedt6kcfyEmryd52Mj
1r1R9mf4iMIUv1zc2sHVc1omxnCw9+7U4GMWLtL5OgyJyfNyoxk3tC+D3KNU
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions pkg/tls/chia_ca.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDPP8vbX1mNvkIg
qdSSl3aO6UYL6PcT3spSK45qUIf3B2qCkmL2cGotxzA8JIVcV7DoI0EgEz4JbaNP
dRXCDON1cnumC27yBaPQ1Do2pIuJdXZZulmi9+tWHfHtnhuM+Ajei5aPJ6FR+YrU
Y8iV3gF9RJC0+VQnnf5dPfYZGLNTL5vQJwnziOnVe/JkAxqNidyUVE00lLD1Ze1O
UxS75nUzgrtzhk/ASqAScKBFzRMJpePXzS+juYIclCgOI1IJEBPB8f6RbE44r+d4
+Sra3iY9IVpfzEBLjIgshIg5Zifuw2B3L0PqAY0WjZJnQVHfH2GNSNgRXdw+qDcG
Y0TcMPKdAgMBAAECggEAa6lLgEl3HyAQACHZUNGoADOEdNlvyP26gpcn42i0SQqs
NOpQyI67Sc6o6wVZ1g+j0ePGiCAW4RT4emVriSPi4Xc4bpiP6OAvKmOlXg96gUzo
z1H0EKnTsifaLsMssr2C9gDzlKhUsF3+1biEUf5DLcz5k1nWcsIrikqO1pizR2mL
nMuhW+kRwR6bY75pGlQ8JmqQ3BTS6YBMJ+BkZ8XOBcj9c8mvxTL4tVk4Wnc9dcd+
dlkP1PkCM79WIF6pswW5ZDwGlU6pqk08qPzENjei/De6IBg8Lt4uJve79agV6Uxz
Y4xfRCWMunlj4VJwUcAzGhyjcBB1ZJi9JDBvnFmaDQKBgQDrMoBJ+vDcS0+2GQtH
k0GZ6wjhFttjqIVWFLmWgtvNO1K2+y6zwQjpFCAuAAUXXgfbVhIfFvp0HUxujjkE
3zeCYOlS8o9ESyWoPdg8woWjkZcya/4mE2jlj1+GI4SfHMska0EHUd4aaDk1en2a
vYKH27zD+yV+4IdyRsD0Bi93EwKBgQDhlHnODvf5QDVro+b3dsthkaP6F/s5oH/o
eLD1jEIGAN9Yz10njyLPfzaeiNPm6LSsKVXlgCjxQIuHTU2n0BsFke4F1ib0jOK4
o3NN4rfVRb5O9vP50THv4+VhB9806FwiB371JeOhmitCSwiYp0wZmV3WURF4e4Fr
Cr3YlC+1jwKBgQC03BTCzvFAtbkKMp/13krn7VDaphT2wbQmybEdCGu1mhS1GNqE
57/OW+eS9/jySyCHjdxJhAX8HDuWGE/Ia03oOFWzr0p0HcVLZqNNtdfGPEKkR18c
MHjNbj7qi42EPUQJMWDEHDRK4jJ76UGFKI2jo1m46vueYVJGkhn2jHsbeQKBgDim
/Ew20Col6QSmfhwKFpvjYsYtfaeEWns8zFxupCozz+PS+Dc2KGzqKwJ3pJgqOy29
l9fybtXf+uq5DFan2hF1C80lclUaiNoMGqol1TtXr6rPNIi59AumNXY/7tuvu2vE
bCsPH/L28ARPKdKEuYT4Umu/ol6aze7fHLymwrCbAoGAbm4vMLIy8wHYrAj5/een
YthUYbQ+XWOrrI1dJoGiwCIXTL4iBSO1ZjkR6M7yg9Zxv0BuqA+2tZqImLXsqIq/
g7Jtrn7LfogS3KOiVa3QAHoXegMrRf4S3DyP/GW8E27MMvqBEkdCDX53h07wvPng
0NRspbyMaX43/tp8ZWkaZf0=
-----END PRIVATE KEY-----
244 changes: 244 additions & 0 deletions pkg/tls/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
package tls

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
// Need to embed the default config into the library
_ "embed"
"encoding/pem"
"fmt"
"math/big"
"os"
"path"
"time"
)

var (
privateNodeNames = []string{
"full_node",
"wallet",
"farmer",
"harvester",
"timelord",
"crawler",
"data_layer",
"daemon",
}
publicNodeNames = []string{
"full_node",
"wallet",
"farmer",
"introducer",
"timelord",
"data_layer",
}

//go:embed chia_ca.crt
chiaCACrtBytes []byte

//go:embed chia_ca.key
chiaCAKeyBytes []byte
)

// GenerateAllCerts generates the full set of required certs for chia blockchain
func GenerateAllCerts(outDir string) error {
// First, ensure that all output directories exist
allNodes := append(privateNodeNames, publicNodeNames...)
for _, subdir := range append(allNodes, "ca") {
err := os.MkdirAll(path.Join(outDir, subdir), 0700)
if err != nil {
return fmt.Errorf("error making output directory for certs: %w", err)
}
}

// Next, copy the chia_ca cert/key
err := os.WriteFile(path.Join(outDir, "ca", "chia_ca.crt"), chiaCACrtBytes, 0600)
if err != nil {
return fmt.Errorf("error copying chia_ca.crt: %w", err)
}
err = os.WriteFile(path.Join(outDir, "ca", "chia_ca.key"), chiaCAKeyBytes, 0600)
if err != nil {
return fmt.Errorf("error copying chia_ca.key: %w", err)
}

chiaCACert, err := ParsePemCertificate(chiaCACrtBytes)
if err != nil {
return fmt.Errorf("error parsing chia_ca.crt")
}

chiaCAKey, err := ParsePemKey(chiaCAKeyBytes)
if err != nil {
return fmt.Errorf("error parsing chia_ca.key")
}

privateCACertBytes, privateCAKeyBytes, err := GenerateNewCA(path.Join(outDir, "ca", "private_ca"))
if err != nil {
return fmt.Errorf("error creating private ca pair: %w", err)
}
privateCACert, err := ParsePemCertificate(privateCACertBytes)
if err != nil {
return fmt.Errorf("error parsing generated private_ca.crt: %w", err)
}
privateCAKey, err := ParsePemKey(privateCAKeyBytes)
if err != nil {
return fmt.Errorf("error parsing generated private_ca.key: %w", err)
}

for _, node := range publicNodeNames {
_, _, err = GenerateCASignedCert(chiaCACert, chiaCAKey, path.Join(outDir, node, fmt.Sprintf("public_%s", node)))
if err != nil {
return fmt.Errorf("error generating public pair for %s: %w", node, err)
}
}

for _, node := range privateNodeNames {
_, _, err = GenerateCASignedCert(privateCACert, privateCAKey, path.Join(outDir, node, fmt.Sprintf("private_%s", node)))
if err != nil {
return fmt.Errorf("error generating private pair for %s: %w", node, err)
}
}

return nil
}

// ParsePemCertificate parses a certificate
func ParsePemCertificate(certPem []byte) (*x509.Certificate, error) {
// Load CA certificate
caCertBlock, rest := pem.Decode(certPem)
if caCertBlock == nil || caCertBlock.Type != "CERTIFICATE" {
return nil, fmt.Errorf("failed to decode CA certificate PEM")
}
if len(rest) != 0 {
return nil, fmt.Errorf("cert file had extra data at the end")
}
caCert, err := x509.ParseCertificate(caCertBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse CA certificate: %v", err)
}

return caCert, nil
}

// ParsePemKey parses a key
func ParsePemKey(keyPem []byte) (*rsa.PrivateKey, error) {
// Load CA private key
caKeyBlock, rest := pem.Decode(keyPem)
if caKeyBlock == nil || caKeyBlock.Type != "PRIVATE KEY" {
return nil, fmt.Errorf("failed to decode CA private key PEM")
}
if len(rest) != 0 {
return nil, fmt.Errorf("key file had extra data at the end")
}
parsedKey, err := x509.ParsePKCS8PrivateKey(caKeyBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse CA private key: %v", err)
}

caKey, ok := parsedKey.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("unexpected key type: %T", parsedKey)
}

return caKey, nil
}

// WriteCertAndKey Returns the written cert bytes, key bytes, and error
func WriteCertAndKey(certDER []byte, certKey *rsa.PrivateKey, certKeyBase string) ([]byte, []byte, error) {
// Write the new certificate to file
certOut := fmt.Sprintf("%s.crt", certKeyBase)
certPemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
if err := os.WriteFile(certOut, certPemBytes, 0600); err != nil {
return nil, nil, fmt.Errorf("failed to write cert PEM: %v", err)
}

// Marshal private key to PKCS#8
keyBytes, err := x509.MarshalPKCS8PrivateKey(certKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal private key to PKCS#8: %v", err)
}

// Write the new private key to file in PKCS#8 format
keyOut := fmt.Sprintf("%s.key", certKeyBase)
keyPemBytes := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})
if err := os.WriteFile(keyOut, keyPemBytes, 0600); err != nil {
return nil, nil, fmt.Errorf("failed to write key PEM: %v", err)
}

return certPemBytes, keyPemBytes, nil
}

// GenerateNewCA generates a new CA
func GenerateNewCA(certKeyBase string) ([]byte, []byte, error) {
// Generate a new RSA private key
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}

// Create new certificate template
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, nil, fmt.Errorf("failed to generate serial number: %v", err)
}

// Define the certificate template
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Organization: []string{"Chia"},
OrganizationalUnit: []string{"Organic Farming Division"},
CommonName: "Chia CA",
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0), // 10 years
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
IsCA: true,
}

// Create the self-signed certificate
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
if err != nil {
return nil, nil, err
}

return WriteCertAndKey(certDER, privateKey, certKeyBase)
}

// GenerateCASignedCert generates a new key/cert signed by the given CA
func GenerateCASignedCert(caCert *x509.Certificate, caKey *rsa.PrivateKey, certKeyBase string) ([]byte, []byte, error) {
// Generate new private key
certKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate private key: %v", err)
}

// Create new certificate template
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, nil, fmt.Errorf("failed to generate serial number: %v", err)
}
certTemplate := x509.Certificate{
Subject: pkix.Name{
CommonName: "Chia",
Organization: []string{"Chia"},
OrganizationalUnit: []string{"Organic Farming Division"},
},
SerialNumber: serialNumber,
NotBefore: time.Now().Add(-24 * time.Hour),
NotAfter: time.Date(2100, 8, 2, 0, 0, 0, 0, time.UTC),
SubjectKeyId: []byte{1, 2, 3, 4, 6},
BasicConstraintsValid: true,
DNSNames: []string{"chia.net"},
}

// Sign the new certificate
certDER, err := x509.CreateCertificate(rand.Reader, &certTemplate, caCert, &certKey.PublicKey, caKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to create certificate: %v", err)
}

return WriteCertAndKey(certDER, certKey, certKeyBase)
}

0 comments on commit f1c52af

Please sign in to comment.