(certPath, keyPath string)
| 118 | } |
| 119 | |
| 120 | func createNodeCA(certPath, keyPath string) (*NodeCA, error) { |
| 121 | priv, err := rsa.GenerateKey(rand.Reader, 4096) |
| 122 | if err != nil { |
| 123 | return nil, fmt.Errorf("generate CA key: %w", err) |
| 124 | } |
| 125 | |
| 126 | serial, err := randomSerial() |
| 127 | if err != nil { |
| 128 | return nil, err |
| 129 | } |
| 130 | |
| 131 | now := time.Now() |
| 132 | template := &x509.Certificate{ |
| 133 | SerialNumber: serial, |
| 134 | Subject: pkix.Name{ |
| 135 | CommonName: "pulse-node-ca", |
| 136 | Organization: []string{"pulse"}, |
| 137 | }, |
| 138 | NotBefore: now.Add(-time.Minute), |
| 139 | NotAfter: now.AddDate(10, 0, 0), |
| 140 | KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, |
| 141 | BasicConstraintsValid: true, |
| 142 | IsCA: true, |
| 143 | } |
| 144 | |
| 145 | der, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv) |
| 146 | if err != nil { |
| 147 | return nil, fmt.Errorf("create CA certificate: %w", err) |
| 148 | } |
| 149 | |
| 150 | caCert, err := x509.ParseCertificate(der) |
| 151 | if err != nil { |
| 152 | return nil, fmt.Errorf("parse newly created CA cert: %w", err) |
| 153 | } |
| 154 | |
| 155 | certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) |
| 156 | keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) |
| 157 | |
| 158 | if err := os.MkdirAll(filepath.Dir(certPath), 0o700); err != nil { |
| 159 | return nil, fmt.Errorf("create cert dir: %w", err) |
| 160 | } |
| 161 | if err := os.MkdirAll(filepath.Dir(keyPath), 0o700); err != nil { |
| 162 | return nil, fmt.Errorf("create key dir: %w", err) |
| 163 | } |
| 164 | if err := os.WriteFile(certPath, certPEM, 0o644); err != nil { |
| 165 | return nil, fmt.Errorf("write CA cert file: %w", err) |
| 166 | } |
| 167 | if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil { |
| 168 | return nil, fmt.Errorf("write CA key file: %w", err) |
| 169 | } |
| 170 | |
| 171 | return &NodeCA{cert: caCert, key: priv, certPEM: certPEM}, nil |
| 172 | } |
| 173 | |
| 174 | func randomSerial() (*big.Int, error) { |
| 175 | limit := new(big.Int).Lsh(big.NewInt(1), 128) |
no test coverage detected