Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_key_vault_key: add support for EC curve based keys #1814

Merged
merged 8 commits into from
Jun 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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