From b33af5c15d0a173c37429248d1387b0e88f946d9 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 6 Sep 2019 01:20:32 -0400 Subject: [PATCH] provider: Implement replacement functions for tls_private_key and tls_self_signed_certificate test configuration resources Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/10023 --- aws/tls.go | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ aws/tls_test.go | 23 +++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 aws/tls.go create mode 100644 aws/tls_test.go diff --git a/aws/tls.go b/aws/tls.go new file mode 100644 index 00000000000..5b36cbd2bc5 --- /dev/null +++ b/aws/tls.go @@ -0,0 +1,86 @@ +package aws + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "strings" + "time" +) + +const ( + pemBlockTypeCertificate = `CERTIFICATE` + pemBlockTypeRsaPrivateKey = `RSA PRIVATE KEY` +) + +var tlsX509CertificateSerialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) + +// tlsRsaPrivateKeyPem generates a RSA private key PEM string. +// To allow simple fmt.Sprintf() configurations such as: private_key_pem = %[1]q +// Escape the result with strings.ReplaceAll(key, "\n", "\\n") +func tlsRsaPrivateKeyPem(bits int) string { + key, err := rsa.GenerateKey(rand.Reader, bits) + + if err != nil { + panic(err) + } + + block := &pem.Block{ + Bytes: x509.MarshalPKCS1PrivateKey(key), + Type: pemBlockTypeRsaPrivateKey, + } + + return string(pem.EncodeToMemory(block)) +} + +// tlsRsaX509SelfSignedCertificatePem generates a x509 certificate PEM string. +// To allow simple fmt.Sprintf() configurations such as: certificate_pem = %[1]q +// Escape the result with strings.ReplaceAll(certificate, "\n", "\\n") +func tlsRsaX509SelfSignedCertificatePem(keyPem, commonName string) string { + keyBlock, _ := pem.Decode([]byte(keyPem)) + + key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) + + if err != nil { + panic(err) + } + + serialNumber, err := rand.Int(rand.Reader, tlsX509CertificateSerialNumberLimit) + + if err != nil { + panic(err) + } + + certificate := &x509.Certificate{ + BasicConstraintsValid: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + NotAfter: time.Now().Add(24 * time.Hour), + NotBefore: time.Now(), + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"ACME Examples, Inc"}, + }, + } + + certificateBytes, err := x509.CreateCertificate(rand.Reader, certificate, certificate, &key.PublicKey, key) + + if err != nil { + panic(err) + } + + certificateBlock := &pem.Block{ + Bytes: certificateBytes, + Type: pemBlockTypeCertificate, + } + + return string(pem.EncodeToMemory(certificateBlock)) +} + +func tlsPemEscapeNewlines(pem string) string { + return strings.ReplaceAll(pem, "\n", "\\n") +} diff --git a/aws/tls_test.go b/aws/tls_test.go new file mode 100644 index 00000000000..3ffa26f6d6d --- /dev/null +++ b/aws/tls_test.go @@ -0,0 +1,23 @@ +package aws + +import ( + "strings" + "testing" +) + +func TestTlsRsaPrivateKeyPem(t *testing.T) { + key := tlsRsaPrivateKeyPem(2048) + + if !strings.Contains(key, pemBlockTypeRsaPrivateKey) { + t.Errorf("key does not contain RSA PRIVATE KEY: %s", key) + } +} + +func TestTlsRsaX509CertificatePem(t *testing.T) { + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509SelfSignedCertificatePem(key, "example.com") + + if !strings.Contains(certificate, pemBlockTypeCertificate) { + t.Errorf("key does not contain CERTIFICATE: %s", certificate) + } +}