...

Source file src/github.com/letsencrypt/boulder/test/cert-ceremonies/generate.go

Documentation: github.com/letsencrypt/boulder/test/cert-ceremonies

     1  // generate.go is a helper utility for integration tests.
     2  package main
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"os/exec"
     9  	"regexp"
    10  	"text/template"
    11  
    12  	"github.com/letsencrypt/boulder/cmd"
    13  	blog "github.com/letsencrypt/boulder/log"
    14  )
    15  
    16  // createSlot initializes a SoftHSM slot and token. SoftHSM chooses the highest empty
    17  // slot, initializes it, and then assigns it a new randomly chosen slot ID. Since we can't
    18  // predict this ID we need to parse out the new ID so that we can use it in the ceremony
    19  // configs.
    20  func createSlot(label string) (string, error) {
    21  	output, err := exec.Command("softhsm2-util", "--init-token", "--free", "--label", label, "--pin", "1234", "--so-pin", "5678").CombinedOutput()
    22  	if err != nil {
    23  		return "", err
    24  	}
    25  	re := regexp.MustCompile(`to slot (\d+)`)
    26  	matches := re.FindSubmatch(output)
    27  	if len(matches) != 2 {
    28  		return "", errors.New("unexpected number of slot matches")
    29  	}
    30  	return string(matches[1]), nil
    31  }
    32  
    33  // genKey is used to run a key ceremony with a given config, replacing SlotID in
    34  // the YAML with a specific slot ID.
    35  func genKey(path string, inSlot string) error {
    36  	tmpPath, err := rewriteConfig(path, map[string]string{"SlotID": inSlot})
    37  	if err != nil {
    38  		return err
    39  	}
    40  	output, err := exec.Command("bin/ceremony", "-config", tmpPath).CombinedOutput()
    41  	if err != nil {
    42  		return fmt.Errorf("error running ceremony for %s: %s:\n%s", tmpPath, err, string(output))
    43  	}
    44  	return nil
    45  }
    46  
    47  // rewriteConfig creates a temporary config based on the template at path
    48  // using the variables in rewrites.
    49  func rewriteConfig(path string, rewrites map[string]string) (string, error) {
    50  	tmplBytes, err := os.ReadFile(path)
    51  	if err != nil {
    52  		return "", err
    53  	}
    54  	tmp, err := os.CreateTemp(os.TempDir(), "ceremony-config")
    55  	if err != nil {
    56  		return "", err
    57  	}
    58  	defer tmp.Close()
    59  	tmpl, err := template.New("config").Parse(string(tmplBytes))
    60  	if err != nil {
    61  		return "", err
    62  	}
    63  	err = tmpl.Execute(tmp, rewrites)
    64  	if err != nil {
    65  		return "", err
    66  	}
    67  	return tmp.Name(), nil
    68  }
    69  
    70  // genCert is used to run a key ceremony with a given config.
    71  func genCert(path string) error {
    72  	output, err := exec.Command("bin/ceremony", "-config", path).CombinedOutput()
    73  	if err != nil {
    74  		return fmt.Errorf("error running ceremony for %s: %s:\n%s", path, err, string(output))
    75  	}
    76  	return nil
    77  }
    78  
    79  func main() {
    80  	_ = blog.Set(blog.StdoutLogger(6))
    81  	defer cmd.AuditPanic()
    82  
    83  	// If one of the output files already exists, assume this ran once
    84  	// already for the container and don't re-run.
    85  	outputFile := "/hierarchy/root-signing-pub-rsa.pem"
    86  	if loc, err := os.Stat(outputFile); err == nil && loc.Mode().IsRegular() {
    87  		fmt.Println("skipping certificate generation: already exists")
    88  		return
    89  	} else if err == nil && !loc.Mode().IsRegular() {
    90  		cmd.Fail(fmt.Sprintf("statting %q: not a regular file", outputFile))
    91  	} else if err != nil && !os.IsNotExist(err) {
    92  		cmd.Fail(fmt.Sprintf("statting %q: %s", outputFile, err))
    93  	}
    94  	// Create SoftHSM slots for the root signing keys
    95  	rsaRootKeySlot, err := createSlot("root signing key (rsa)")
    96  	cmd.FailOnError(err, "failed creating softhsm2 slot for RSA root key")
    97  	ecdsaRootKeySlot, err := createSlot("root signing key (ecdsa)")
    98  	cmd.FailOnError(err, "failed creating softhsm2 slot for root key")
    99  
   100  	// Generate the root signing keys and certificates
   101  	err = genKey("test/cert-ceremonies/root-ceremony-rsa.yaml", rsaRootKeySlot)
   102  	cmd.FailOnError(err, "failed to generate RSA root key + root cert")
   103  	err = genKey("test/cert-ceremonies/root-ceremony-ecdsa.yaml", ecdsaRootKeySlot)
   104  	cmd.FailOnError(err, "failed to generate ECDSA root key + root cert")
   105  
   106  	// Create SoftHSM slots for the intermediate signing keys
   107  	rsaIntermediateKeySlot, err := createSlot("intermediate signing key (rsa)")
   108  	cmd.FailOnError(err, "failed to create softhsm2 slot for RSA intermediate key")
   109  	ecdsaIntermediateKeySlot, err := createSlot("intermediate signing key (ecdsa)")
   110  	cmd.FailOnError(err, "failed to create softhsm2 slot for ECDSA intermediate key")
   111  
   112  	// Generate the intermediate signing keys
   113  	err = genKey("test/cert-ceremonies/intermediate-key-ceremony-rsa.yaml", rsaIntermediateKeySlot)
   114  	cmd.FailOnError(err, "failed to generate RSA intermediate key")
   115  	err = genKey("test/cert-ceremonies/intermediate-key-ceremony-ecdsa.yaml", ecdsaIntermediateKeySlot)
   116  	cmd.FailOnError(err, "failed to generate ECDSA intermediate key")
   117  
   118  	// Create the A intermediate ceremony config files with the root
   119  	// signing key slots and IDs
   120  	rsaTmpIntermediateA, err := rewriteConfig("test/cert-ceremonies/intermediate-ceremony-rsa.yaml", map[string]string{
   121  		"SlotID":     rsaRootKeySlot,
   122  		"CertPath":   "/hierarchy/intermediate-cert-rsa-a.pem",
   123  		"CommonName": "CA intermediate (RSA) A",
   124  	})
   125  	cmd.FailOnError(err, "failed to rewrite RSA intermediate cert config with key ID")
   126  	ecdsaTmpIntermediateA, err := rewriteConfig("test/cert-ceremonies/intermediate-ceremony-ecdsa.yaml", map[string]string{
   127  		"SlotID":     ecdsaRootKeySlot,
   128  		"CertPath":   "/hierarchy/intermediate-cert-ecdsa-a.pem",
   129  		"CommonName": "CA intermediate (ECDSA) A",
   130  	})
   131  	cmd.FailOnError(err, "failed to rewrite ECDSA intermediate cert config with key ID")
   132  
   133  	// Create the A intermediate certificates
   134  	err = genCert(rsaTmpIntermediateA)
   135  	cmd.FailOnError(err, "failed to generate RSA intermediate cert")
   136  	err = genCert(ecdsaTmpIntermediateA)
   137  	cmd.FailOnError(err, "failed to generate ECDSA intermediate cert")
   138  
   139  	// Create the B intermediate ceremony config files with the root
   140  	// signing key slots and IDs
   141  	rsaTmpIntermediateB, err := rewriteConfig("test/cert-ceremonies/intermediate-ceremony-rsa.yaml", map[string]string{
   142  		"SlotID":     rsaRootKeySlot,
   143  		"CertPath":   "/hierarchy/intermediate-cert-rsa-b.pem",
   144  		"CommonName": "CA intermediate (RSA) B",
   145  	})
   146  	cmd.FailOnError(err, "failed to rewrite RSA intermediate cert config with key ID")
   147  	ecdsaTmpIntermediateB, err := rewriteConfig("test/cert-ceremonies/intermediate-ceremony-ecdsa.yaml", map[string]string{
   148  		"SlotID":     ecdsaRootKeySlot,
   149  		"CertPath":   "/hierarchy/intermediate-cert-ecdsa-b.pem",
   150  		"CommonName": "CA intermediate (ECDSA) B",
   151  	})
   152  	cmd.FailOnError(err, "failed to rewrite ECDSA intermediate cert config with key ID")
   153  
   154  	// Create the B intermediate certificates
   155  	err = genCert(rsaTmpIntermediateB)
   156  	cmd.FailOnError(err, "failed to generate RSA intermediate cert")
   157  	err = genCert(ecdsaTmpIntermediateB)
   158  	cmd.FailOnError(err, "failed to generate ECDSA intermediate cert")
   159  
   160  	// Create the A and B cross-signed intermediates with the root signing key
   161  	// slots and IDs. In this case, the issuer is an RSA root, but the
   162  	// subordinates are ECDSA.
   163  	ecdsaTmpCrossIntermediateA, err := rewriteConfig("test/cert-ceremonies/intermediate-cross-cert-ceremony.yaml", map[string]string{
   164  		"SlotID":         rsaRootKeySlot,
   165  		"RootAlgorithm":  "rsa",
   166  		"PublicKeyPath":  "/hierarchy/intermediate-signing-pub-ecdsa.pem",
   167  		"IssuerCertPath": "/hierarchy/root-cert-rsa.pem",
   168  		"InputCertPath":  "/hierarchy/intermediate-cert-ecdsa-a.pem",
   169  		"OutputCertPath": "/hierarchy/intermediate-cross-cert-ecdsa-a.pem",
   170  		"CommonName":     "CA intermediate (ECDSA) A",
   171  		"SigAlgorithm":   "SHA256WithRSA",
   172  	})
   173  	cmd.FailOnError(err, "failed to rewrite ECDSA intermediate cert config with key ID")
   174  	err = genCert(ecdsaTmpCrossIntermediateA)
   175  	cmd.FailOnError(err, "failed to generate ECDSA cross-signed intermediate cert A")
   176  
   177  	ecdsaTmpCrossIntermediateB, err := rewriteConfig("test/cert-ceremonies/intermediate-cross-cert-ceremony.yaml", map[string]string{
   178  		"SlotID":         rsaRootKeySlot,
   179  		"RootAlgorithm":  "rsa",
   180  		"PublicKeyPath":  "/hierarchy/intermediate-signing-pub-ecdsa.pem",
   181  		"IssuerCertPath": "/hierarchy/root-cert-rsa.pem",
   182  		"InputCertPath":  "/hierarchy/intermediate-cert-ecdsa-b.pem",
   183  		"OutputCertPath": "/hierarchy/intermediate-cross-cert-ecdsa-b.pem",
   184  		"CommonName":     "CA intermediate (ECDSA) B",
   185  		"SigAlgorithm":   "SHA256WithRSA",
   186  	})
   187  	cmd.FailOnError(err, "failed to rewrite ECDSA cross-signed intermediate cert config with key ID")
   188  	err = genCert(ecdsaTmpCrossIntermediateB)
   189  	cmd.FailOnError(err, "failed to generate ECDSA cross-signed intermediate cert B")
   190  
   191  	// Create CRLs stating that the intermediates are not revoked.
   192  	rsaTmpCRLConfig, err := rewriteConfig("test/cert-ceremonies/root-crl-rsa.yaml", map[string]string{
   193  		"SlotID": rsaRootKeySlot,
   194  	})
   195  	cmd.FailOnError(err, "failed to rewrite RSA root CRL config with key ID")
   196  	err = genCert(rsaTmpCRLConfig)
   197  	cmd.FailOnError(err, "failed to generate RSA root CRL")
   198  
   199  	ecdsaTmpCRLConfig, err := rewriteConfig("test/cert-ceremonies/root-crl-ecdsa.yaml", map[string]string{
   200  		"SlotID": ecdsaRootKeySlot,
   201  	})
   202  	cmd.FailOnError(err, "failed to rewrite ECDSA root CRL config with key ID")
   203  	err = genCert(ecdsaTmpCRLConfig)
   204  	cmd.FailOnError(err, "failed to generate ECDSA root CRL")
   205  }
   206  

View as plain text