package tls import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" "fmt" "github.com/hashicorp/terraform/helper/schema" ) type keyAlgo func(d *schema.ResourceData) (interface{}, error) type keyParser func([]byte) (interface{}, error) var keyAlgos map[string]keyAlgo = map[string]keyAlgo{ "RSA": func(d *schema.ResourceData) (interface{}, error) { rsaBits := d.Get("rsa_bits").(int) return rsa.GenerateKey(rand.Reader, rsaBits) }, "ECDSA": func(d *schema.ResourceData) (interface{}, error) { curve := d.Get("ecdsa_curve").(string) switch curve { case "P224": return ecdsa.GenerateKey(elliptic.P224(), rand.Reader) case "P256": return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) case "P384": return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) case "P521": return ecdsa.GenerateKey(elliptic.P521(), rand.Reader) default: return nil, fmt.Errorf("invalid ecdsa_curve; must be P224, P256, P384 or P521") } }, } var keyParsers map[string]keyParser = map[string]keyParser{ "RSA": func(der []byte) (interface{}, error) { return x509.ParsePKCS1PrivateKey(der) }, "ECDSA": func(der []byte) (interface{}, error) { return x509.ParseECPrivateKey(der) }, } func resourcePrivateKey() *schema.Resource { return &schema.Resource{ Create: CreatePrivateKey, Delete: DeletePrivateKey, Read: ReadPrivateKey, Schema: map[string]*schema.Schema{ "algorithm": &schema.Schema{ Type: schema.TypeString, Required: true, Description: "Name of the algorithm to use to generate the private key", ForceNew: true, }, "rsa_bits": &schema.Schema{ Type: schema.TypeInt, Optional: true, Description: "Number of bits to use when generating an RSA key", ForceNew: true, Default: 2048, }, "ecdsa_curve": &schema.Schema{ Type: schema.TypeString, Optional: true, Description: "ECDSA curve to use when generating a key", ForceNew: true, Default: "P224", }, "private_key_pem": &schema.Schema{ Type: schema.TypeString, Computed: true, }, }, } } func CreatePrivateKey(d *schema.ResourceData, meta interface{}) error { keyAlgoName := d.Get("algorithm").(string) var keyFunc keyAlgo var ok bool if keyFunc, ok = keyAlgos[keyAlgoName]; !ok { return fmt.Errorf("invalid key_algorithm %#v", keyAlgoName) } key, err := keyFunc(d) if err != nil { return err } var keyPemBlock *pem.Block switch k := key.(type) { case *rsa.PrivateKey: keyPemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} case *ecdsa.PrivateKey: b, err := x509.MarshalECPrivateKey(k) if err != nil { return fmt.Errorf("error encoding key to PEM: %s", err) } keyPemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} default: return fmt.Errorf("unsupported private key type") } keyPem := string(pem.EncodeToMemory(keyPemBlock)) pubKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey(key)) if err != nil { return fmt.Errorf("failed to marshal public key: %s", err) } d.SetId(hashForState(string((pubKeyBytes)))) d.Set("private_key_pem", keyPem) return nil } func DeletePrivateKey(d *schema.ResourceData, meta interface{}) error { d.SetId("") return nil } func ReadPrivateKey(d *schema.ResourceData, meta interface{}) error { return nil } func publicKey(priv interface{}) interface{} { switch k := priv.(type) { case *rsa.PrivateKey: return &k.PublicKey case *ecdsa.PrivateKey: return &k.PublicKey default: return nil } }