github.com/MetalBlockchain/metalgo@v1.11.9/staking/tls.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package staking 5 6 import ( 7 "bytes" 8 "crypto/ecdsa" 9 "crypto/elliptic" 10 "crypto/rand" 11 "crypto/tls" 12 "crypto/x509" 13 "encoding/pem" 14 "fmt" 15 "math/big" 16 "os" 17 "path/filepath" 18 "time" 19 20 "github.com/MetalBlockchain/metalgo/utils/perms" 21 ) 22 23 // InitNodeStakingKeyPair generates a self-signed TLS key/cert pair to use in 24 // staking. The key and files will be placed at [keyPath] and [certPath], 25 // respectively. If there is already a file at [keyPath], returns nil. 26 func InitNodeStakingKeyPair(keyPath, certPath string) error { 27 // If there is already a file at [keyPath], do nothing 28 if _, err := os.Stat(keyPath); !os.IsNotExist(err) { 29 return nil 30 } 31 32 certBytes, keyBytes, err := NewCertAndKeyBytes() 33 if err != nil { 34 return err 35 } 36 37 // Ensure directory where key/cert will live exist 38 if err := os.MkdirAll(filepath.Dir(certPath), perms.ReadWriteExecute); err != nil { 39 return fmt.Errorf("couldn't create path for cert: %w", err) 40 } 41 if err := os.MkdirAll(filepath.Dir(keyPath), perms.ReadWriteExecute); err != nil { 42 return fmt.Errorf("couldn't create path for key: %w", err) 43 } 44 45 // Write cert to disk 46 certFile, err := os.Create(certPath) 47 if err != nil { 48 return fmt.Errorf("couldn't create cert file: %w", err) 49 } 50 if _, err := certFile.Write(certBytes); err != nil { 51 return fmt.Errorf("couldn't write cert file: %w", err) 52 } 53 if err := certFile.Close(); err != nil { 54 return fmt.Errorf("couldn't close cert file: %w", err) 55 } 56 if err := os.Chmod(certPath, perms.ReadOnly); err != nil { // Make cert read-only 57 return fmt.Errorf("couldn't change permissions on cert: %w", err) 58 } 59 60 // Write key to disk 61 keyOut, err := os.Create(keyPath) 62 if err != nil { 63 return fmt.Errorf("couldn't create key file: %w", err) 64 } 65 if _, err := keyOut.Write(keyBytes); err != nil { 66 return fmt.Errorf("couldn't write private key: %w", err) 67 } 68 if err := keyOut.Close(); err != nil { 69 return fmt.Errorf("couldn't close key file: %w", err) 70 } 71 if err := os.Chmod(keyPath, perms.ReadOnly); err != nil { // Make key read-only 72 return fmt.Errorf("couldn't change permissions on key: %w", err) 73 } 74 return nil 75 } 76 77 func LoadTLSCertFromBytes(keyBytes, certBytes []byte) (*tls.Certificate, error) { 78 cert, err := tls.X509KeyPair(certBytes, keyBytes) 79 if err != nil { 80 return nil, fmt.Errorf("failed creating cert: %w", err) 81 } 82 83 cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) 84 if err != nil { 85 return nil, fmt.Errorf("failed parsing cert: %w", err) 86 } 87 return &cert, nil 88 } 89 90 func LoadTLSCertFromFiles(keyPath, certPath string) (*tls.Certificate, error) { 91 cert, err := tls.LoadX509KeyPair(certPath, keyPath) 92 if err != nil { 93 return nil, err 94 } 95 cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) 96 if err != nil { 97 return nil, fmt.Errorf("failed parsing cert: %w", err) 98 } 99 return &cert, nil 100 } 101 102 func NewTLSCert() (*tls.Certificate, error) { 103 certBytes, keyBytes, err := NewCertAndKeyBytes() 104 if err != nil { 105 return nil, err 106 } 107 cert, err := tls.X509KeyPair(certBytes, keyBytes) 108 if err != nil { 109 return nil, err 110 } 111 cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) 112 return &cert, err 113 } 114 115 // Creates a new staking private key / staking certificate pair. 116 // Returns the PEM byte representations of both. 117 func NewCertAndKeyBytes() ([]byte, []byte, error) { 118 // Create key to sign cert with 119 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 120 if err != nil { 121 return nil, nil, fmt.Errorf("couldn't generate ecdsa key: %w", err) 122 } 123 124 // Create self-signed staking cert 125 certTemplate := &x509.Certificate{ 126 SerialNumber: big.NewInt(0), 127 NotBefore: time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC), 128 NotAfter: time.Now().AddDate(100, 0, 0), 129 KeyUsage: x509.KeyUsageDigitalSignature, 130 BasicConstraintsValid: true, 131 } 132 certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, key.Public(), key) 133 if err != nil { 134 return nil, nil, fmt.Errorf("couldn't create certificate: %w", err) 135 } 136 var certBuff bytes.Buffer 137 if err := pem.Encode(&certBuff, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}); err != nil { 138 return nil, nil, fmt.Errorf("couldn't write cert file: %w", err) 139 } 140 141 privBytes, err := x509.MarshalPKCS8PrivateKey(key) 142 if err != nil { 143 return nil, nil, fmt.Errorf("couldn't marshal private key: %w", err) 144 } 145 146 var keyBuff bytes.Buffer 147 if err := pem.Encode(&keyBuff, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil { 148 return nil, nil, fmt.Errorf("couldn't write private key: %w", err) 149 } 150 return certBuff.Bytes(), keyBuff.Bytes(), nil 151 }