2015-07-18 20:50:13 +02:00
|
|
|
package tls
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/elliptic"
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/rsa"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/pem"
|
|
|
|
"fmt"
|
|
|
|
|
2016-01-17 02:30:41 +01:00
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
|
2015-07-18 20:50:13 +02:00
|
|
|
"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,
|
|
|
|
},
|
2016-01-17 02:30:41 +01:00
|
|
|
|
|
|
|
"public_key_pem": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"public_key_openssh": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
2015-07-18 20:50:13 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
2016-01-17 02:30:41 +01:00
|
|
|
keyPemBlock = &pem.Block{
|
|
|
|
Type: "RSA PRIVATE KEY",
|
|
|
|
Bytes: x509.MarshalPKCS1PrivateKey(k),
|
|
|
|
}
|
2015-07-18 20:50:13 +02:00
|
|
|
case *ecdsa.PrivateKey:
|
2016-01-17 02:30:41 +01:00
|
|
|
keyBytes, err := x509.MarshalECPrivateKey(k)
|
2015-07-18 20:50:13 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error encoding key to PEM: %s", err)
|
|
|
|
}
|
2016-01-17 02:30:41 +01:00
|
|
|
keyPemBlock = &pem.Block{
|
|
|
|
Type: "EC PRIVATE KEY",
|
|
|
|
Bytes: keyBytes,
|
|
|
|
}
|
2015-07-18 20:50:13 +02:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("unsupported private key type")
|
|
|
|
}
|
|
|
|
keyPem := string(pem.EncodeToMemory(keyPemBlock))
|
|
|
|
|
2016-01-17 02:30:41 +01:00
|
|
|
pubKey := publicKey(key)
|
|
|
|
pubKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey)
|
2015-07-18 20:50:13 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to marshal public key: %s", err)
|
|
|
|
}
|
2016-01-17 02:30:41 +01:00
|
|
|
pubKeyPemBlock := &pem.Block{
|
|
|
|
Type: "PUBLIC KEY",
|
|
|
|
Bytes: pubKeyBytes,
|
|
|
|
}
|
2015-07-18 20:50:13 +02:00
|
|
|
|
|
|
|
d.SetId(hashForState(string((pubKeyBytes))))
|
|
|
|
d.Set("private_key_pem", keyPem)
|
2016-01-17 02:30:41 +01:00
|
|
|
d.Set("public_key_pem", string(pem.EncodeToMemory(pubKeyPemBlock)))
|
|
|
|
|
|
|
|
sshPubKey, err := ssh.NewPublicKey(pubKey)
|
|
|
|
if err == nil {
|
|
|
|
// Not all EC types can be SSH keys, so we'll produce this only
|
|
|
|
// if an appropriate type was selected.
|
|
|
|
sshPubKeyBytes := ssh.MarshalAuthorizedKey(sshPubKey)
|
|
|
|
d.Set("public_key_openssh", string(sshPubKeyBytes))
|
|
|
|
} else {
|
|
|
|
d.Set("public_key_openssh", "")
|
|
|
|
}
|
2015-07-18 20:50:13 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|