Skip to content

Commit

Permalink
azurerm_key_vault_key: add support for EC curve based keys (#1814)
Browse files Browse the repository at this point in the history
  • Loading branch information
phekmat authored and katbyte committed Jun 18, 2019
1 parent d308df8 commit 5eccd92
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 8 deletions.
64 changes: 59 additions & 5 deletions azurerm/resource_arm_key_vault_key.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package azurerm

import (
"encoding/base64"
"fmt"
"log"

Expand Down Expand Up @@ -61,15 +62,17 @@ func resourceArmKeyVaultKey() *schema.Resource {
// TODO: add `oct` back in once this is fixed
// https://github.com/Azure/azure-rest-api-specs/issues/1739#issuecomment-332236257
string(keyvault.EC),
string(keyvault.ECHSM),
string(keyvault.RSA),
string(keyvault.RSAHSM),
}, false),
},

"key_size": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"curve"},
},

"key_opts": {
Expand All @@ -90,6 +93,23 @@ func resourceArmKeyVaultKey() *schema.Resource {
},
},

"curve": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
string(keyvault.P256),
string(keyvault.P384),
string(keyvault.P521),
string(keyvault.SECP256K1),
}, false),
// TODO: the curve name should probably be mandatory for EC in the future,
// but handle the diff so that we don't break existing configurations and
// imported EC keys
ConflictsWith: []string{"key_size"},
},

// Computed
"version": {
Type: schema.TypeString,
Expand All @@ -106,6 +126,16 @@ func resourceArmKeyVaultKey() *schema.Resource {
Computed: true,
},

"x": {
Type: schema.TypeString,
Computed: true,
},

"y": {
Type: schema.TypeString,
Computed: true,
},

"tags": tagsSchema(),
},
}
Expand Down Expand Up @@ -165,10 +195,23 @@ func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) erro
KeyAttributes: &keyvault.KeyAttributes{
Enabled: utils.Bool(true),
},
KeySize: utils.Int32(int32(d.Get("key_size").(int))),
Tags: expandTags(tags),

Tags: expandTags(tags),
}

if parameters.Kty == keyvault.EC || parameters.Kty == keyvault.ECHSM {
curveName := d.Get("curve").(string)
parameters.Curve = keyvault.JSONWebKeyCurveName(curveName)
} else if parameters.Kty == keyvault.RSA || parameters.Kty == keyvault.RSAHSM {
keySize, ok := d.GetOk("key_size")
if !ok {
return fmt.Errorf("Key size is required when creating an RSA key")
}
parameters.KeySize = utils.Int32(int32(keySize.(int)))
}
// TODO: support `oct` once this is fixed
// https://github.com/Azure/azure-rest-api-specs/issues/1739#issuecomment-332236257

if _, err := client.CreateKey(ctx, keyVaultBaseUri, name, parameters); err != nil {
return fmt.Errorf("Error Creating Key: %+v", err)
}
Expand Down Expand Up @@ -283,6 +326,17 @@ func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error

d.Set("n", key.N)
d.Set("e", key.E)
d.Set("x", key.X)
d.Set("y", key.Y)
if key.N != nil {
nBytes, err := base64.RawURLEncoding.DecodeString(*key.N)
if err != nil {
return fmt.Errorf("Could not decode N: %+v", err)
}
d.Set("key_size", len(nBytes)*8)
}

d.Set("curve", key.Crv)
}

// Computed
Expand Down
155 changes: 155 additions & 0 deletions azurerm/resource_arm_key_vault_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,51 @@ func TestAccAzureRMKeyVaultKey_requiresImport(t *testing.T) {
})
}

func TestAccAzureRMKeyVaultKey_basicECHSM(t *testing.T) {
resourceName := "azurerm_key_vault_key.test"
rs := acctest.RandString(6)
config := testAccAzureRMKeyVaultKey_basicECHSM(rs, testLocation())

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMKeyVaultKeyExists(resourceName),
),
},
},
})
}

func TestAccAzureRMKeyVaultKey_curveEC(t *testing.T) {
resourceName := "azurerm_key_vault_key.test"
rs := acctest.RandString(6)
config := testAccAzureRMKeyVaultKey_curveEC(rs, testLocation())

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMKeyVaultKeyExists(resourceName),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMKeyVaultKey_basicRSA(t *testing.T) {
resourceName := "azurerm_key_vault_key.test"
rs := acctest.RandString(6)
Expand Down Expand Up @@ -727,3 +772,113 @@ resource "azurerm_key_vault_key" "test" {
}
`, rString, location, rString, rString)
}

func testAccAzureRMKeyVaultKey_curveEC(rString string, location string) string {
return fmt.Sprintf(`
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "test" {
name = "acctestRG-%s"
location = "%s"
}
resource "azurerm_key_vault" "test" {
name = "acctestkv-%s"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
tenant_id = "${data.azurerm_client_config.current.tenant_id}"
sku {
name = "premium"
}
access_policy {
tenant_id = "${data.azurerm_client_config.current.tenant_id}"
object_id = "${data.azurerm_client_config.current.service_principal_object_id}"
key_permissions = [
"create",
"delete",
"get",
]
secret_permissions = [
"get",
"delete",
"set",
]
}
tags = {
environment = "Production"
}
}
resource "azurerm_key_vault_key" "test" {
name = "key-%s"
vault_uri = "${azurerm_key_vault.test.vault_uri}"
key_type = "EC"
curve = "P-521"
key_opts = [
"sign",
"verify",
]
}
`, rString, location, rString, rString)
}

func testAccAzureRMKeyVaultKey_basicECHSM(rString string, location string) string {
return fmt.Sprintf(`
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "test" {
name = "acctestRG-%s"
location = "%s"
}
resource "azurerm_key_vault" "test" {
name = "acctestkv-%s"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
tenant_id = "${data.azurerm_client_config.current.tenant_id}"
sku {
name = "premium"
}
access_policy {
tenant_id = "${data.azurerm_client_config.current.tenant_id}"
object_id = "${data.azurerm_client_config.current.service_principal_object_id}"
key_permissions = [
"create",
"delete",
"get",
]
secret_permissions = [
"get",
"delete",
"set",
]
}
tags = {
environment = "Production"
}
}
resource "azurerm_key_vault_key" "test" {
name = "key-%s"
vault_uri = "${azurerm_key_vault.test.vault_uri}"
key_type = "EC-HSM"
curve = "P-521"
key_opts = [
"sign",
"verify",
]
}
`, rString, location, rString, rString)
}
10 changes: 7 additions & 3 deletions website/docs/r/key_vault_key.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,13 @@ The following arguments are supported:

* `name` - (Required) Specifies the name of the Key Vault Key. Changing this forces a new resource to be created.

* `key_vault_id` - (Required) The ID of the Key Vault where the Key should be created.
* `key_vault_id` - (Required) The ID of the Key Vault where the Key should be created. Changing this forces a new resource to be created.

* `key_type` - (Required) Specifies the Key Type to use for this Key Vault Key. Possible values are `EC` (Elliptic Curve), `Oct` (Octet), `RSA` and `RSA-HSM`. Changing this forces a new resource to be created.
* `key_type` - (Required) Specifies the Key Type to use for this Key Vault Key. Possible values are `EC` (Elliptic Curve), `EC-HSM`, `Oct` (Octet), `RSA` and `RSA-HSM`. Changing this forces a new resource to be created.

* `key_size` - (Required) Specifies the Size of the Key to create in bytes. For example, 1024 or 2048. Changing this forces a new resource to be created.
* `key_size` - (Optional) Specifies the Size of the RSA key to create in bytes. For example, 1024 or 2048. *Note*: This field is required if `key_type` is `RSA` or `RSA-HSM`. Changing this forces a new resource to be created.

* `curve` - (Optional) Specifies the curve to use when creating an `EC` key. Possible values are `P-256`, `P-384`, `P-521`, and `SECP256K1`. This field will be required in a future release if `key_type` is `EC` or `EC-HSM`. The API will default to `P-256` if nothing is specified. Changing this forces a new resource to be created.

* `key_opts` - (Required) A list of JSON web key operations. Possible values include: `decrypt`, `encrypt`, `sign`, `unwrapKey`, `verify` and `wrapKey`. Please note these values are case sensitive.

Expand All @@ -99,6 +101,8 @@ The following attributes are exported:
* `version` - The current version of the Key Vault Key.
* `n` - The RSA modulus of this Key Vault Key.
* `e` - The RSA public exponent of this Key Vault Key.
* `x` - The EC X component of this Key Vault Key.
* `y` - The EC Y component of this Key Vault Key.


## Import
Expand Down

0 comments on commit 5eccd92

Please sign in to comment.