Skip to content

Commit

Permalink
[KeyVault] Supported creating/updating key with release policy in a M…
Browse files Browse the repository at this point in the history
…anaged HSM (#18374)

* Supported creating/updating key with release policy in a Managed HSM

* Refine codes

* Update update-azkeyvault.md

* add example for secure key

* upgrade Azure.Security.KeyVault.Keys to 4.3.0-beta.7
  • Loading branch information
BethanyZhou committed Jun 7, 2022
1 parent 31cb35a commit 8be1b24
Show file tree
Hide file tree
Showing 31 changed files with 878 additions and 197 deletions.
2 changes: 1 addition & 1 deletion src/CosmosDB/CosmosDB/CosmosDB.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<Import Project="$(MSBuildThisFileDirectory)..\..\Az.props" />
<ItemGroup>
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0-beta.4" />
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0-beta.7" />
<PackageReference Include="Microsoft.Azure.Management.CosmosDB" Version="3.7.0-preview" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ function ImportModules {
$psd1Path = Join-Path $PSScriptRoot "../../../../artifacts/Debug/" -Resolve
$accountsPsd1 = Join-Path $psd1Path "./Az.Accounts/Az.Accounts.psd1" -Resolve
$keyVaultPsd1 = Join-Path $psd1Path "./Az.KeyVault/Az.KeyVault.psd1" -Resolve
Import-Module $accountsPsd1
Import-Module $keyVaultPsd1
Import-Module $accountsPsd1 -Force
Import-Module $keyVaultPsd1 -Force
}
118 changes: 118 additions & 0 deletions src/KeyVault/KeyVault.Test/PesterTests/MhsmKey.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

$hsmName = 'bezmhsm'
. $PSScriptRoot/ManagedHsmDataPlaneTests.ps1

function Get-KeyName{
return GetRandomName "bez-key"
}


Describe "Exportable and ReleasePolicyPath shoud show up at the same time"{

It "Both Exportable and ReleasePolicyPath don't show up"{
{
Add-AzKeyVaultKey -HsmName $hsmName -KeyName (Get-KeyName) -KeyType RSA
} | Should -Not -Throw
}

It "Exportable shows up but ReleasePolicyPath not" -skip {
{
Add-AzKeyVaultKey -HsmName $hsmName -KeyName (Get-KeyName) -KeyType RSA -Exportable
} | Should -Throw
}

It "ReleasePolicyPath shows up but Exportable not" -skip {
{
Add-AzKeyVaultKey -HsmName $hsmName -KeyName (Get-KeyName) -KeyType RSA -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"
} | Should -Throw
}

It "Both ReleasePolicyPath and Exportable show up"{
$keyName = Get-KeyName
{
Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Exportable -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"
} | Should -Not -Throw
$key = Get-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName
$key.ReleasePolicy | Should -Not -BeNullOrEmpty
$key.Attributes.Exportable | Should -Be $true
}
}

Describe "Create secure key"{
It "Create a key without immutable property and release policy" {
{
Add-AzKeyVaultKey -HsmName $hsmName -KeyName (Get-KeyName) -KeyType RSA
} | Should -Not -Throw
}

It "Create a key with immutable property but release policy" -skip {
$keyName = Get-KeyName
{
Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Immutable
} | Should -Throw "Please provide release policy when Immutable is present."
}

It "Create a key with release policy but immutable property" {
$keyName = Get-KeyName
{
Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Exportable -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"
} | Should -Not -Throw
$key = Get-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName
$key.ReleasePolicy | Should -Not -BeNullOrEmpty
$key.ReleasePolicy.Immutable | Should -Be $false
}

It "Create a key with both release policy and immutable property" {
$keyName = Get-KeyName
{
Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Exportable -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json" -Immutable
} | Should -Not -Throw
$key = Get-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName
$key.ReleasePolicy | Should -Not -BeNullOrEmpty
$key.ReleasePolicy.Immutable | Should -Be $true
}
}

Describe "Update secure key"{

It "Update a key with immutable property but release policy" -skip {
$keyName = Get-KeyName
Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Exportable -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"
{ Update-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -Immutable} | Should -Throw "Please provide release policy when Immutable is present."
}


It "Update a key with release policy but immutable property" {
$keyName = Get-KeyName
Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Exportable -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"
{ Update-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"} | Should -Not -Throw
$key = Get-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName
$key.ReleasePolicy | Should -Not -BeNullOrEmpty
$key.ReleasePolicy.Immutable | Should -Be $false
}

It "Update a key with both release policy and immutable property" {
$keyName = Get-KeyName
Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Exportable -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"
{ Update-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json" -Immutable} | Should -Not -Throw
$updatedKey = Get-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName
$updatedKey.ReleasePolicy | Should -Not -BeNullOrEmpty
$updatedKey.ReleasePolicy.Immutable | Should -Be $true
}

It "Update an immutable release policy" -skip {
$keyName = Get-KeyName
$key = Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Exportable -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json" -Immutable
$key.ReleasePolicy | Should -Not -BeNullOrEmpty
$key.ReleasePolicy.Immutable | Should -Be $true
{ Update-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json" } | Should -Throw "Please provide release policy when Immutable is present."
}

It "Update a mutable release policy" {
$keyName = Get-KeyName
$key = Add-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -KeyType RSA -Exportable -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"
$key.ReleasePolicy | Should -Not -BeNullOrEmpty
$key.ReleasePolicy.Immutable | Should -Be $false
{ Update-AzKeyVaultKey -HsmName $hsmName -KeyName $keyName -ReleasePolicyPath "$PSScriptRoot\..\Resources\releasepolicy.json"} | Should -Not -Throw
}
}
15 changes: 15 additions & 0 deletions src/KeyVault/KeyVault.Test/Resources/releasepolicy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"anyOf": [
{
"authority": "https://sharedeus.eus.attest.azure.net/",
"allOf": [
{
"claim": "x-ms-sgx-is-debuggable",
"equals": "true"
}
]
}
],
"version": "1.0.0"
}

1 change: 1 addition & 0 deletions src/KeyVault/KeyVault/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- Additional information about change #1
-->
## Upcoming Release
* Supported creating/updating key with release policy in a Managed HSM

## Version 4.5.0
* Added `Rotate` into the list of permissions to keys [#17970]
Expand Down
138 changes: 126 additions & 12 deletions src/KeyVault/KeyVault/Commands/Key/AddAzureKeyVaultKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,22 @@
// limitations under the License.
// ----------------------------------------------------------------------------------

using Microsoft.Azure.Commands.Common;
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.KeyVault.Helpers;
using Microsoft.Azure.Commands.KeyVault.Models;
using Microsoft.Azure.Commands.KeyVault.Properties;
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
using Microsoft.Azure.KeyVault.WebKey;
using Microsoft.Azure.Management.Internal.Resources.Utilities.Models;
using Microsoft.WindowsAzure.Commands.Utilities.Common;

using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Net.Http;
using System.Security;
using Track2Sdk = Azure.Security.KeyVault.Keys;

Expand All @@ -49,12 +53,15 @@ public class AddAzureKeyVaultKey : KeyVaultCmdletBase
private const string InteractiveCreateParameterSet = "InteractiveCreate";
private const string InputObjectCreateParameterSet = "InputObjectCreate";
private const string ResourceIdCreateParameterSet = "ResourceIdCreate";

private const string InteractiveImportParameterSet = "InteractiveImport";
private const string InputObjectImportParameterSet = "InputObjectImport";
private const string ResourceIdImportParameterSet = "ResourceIdImport";

private const string HsmInteractiveCreateParameterSet = "HsmInteractiveCreate";
private const string HsmInputObjectCreateParameterSet = "HsmInputObjectCreate";
private const string HsmResourceIdCreateParameterSet = "HsmResourceIdCreate";

private const string HsmInteractiveImportParameterSet = "HsmInteractiveImport";
private const string HsmInputObjectImportParameterSet = "HsmInputObjectImport";
private const string HsmResourceIdImportParameterSet = "HsmResourceIdImport";
Expand All @@ -64,6 +71,12 @@ public class AddAzureKeyVaultKey : KeyVaultCmdletBase

#endregion

#region Constants

private const string DefaultCVMPolicyUrl = "https://cvmprivatepreviewsa.blob.core.windows.net/cvmpublicpreviewcontainer/skr-policy.json";

#endregion

#region Input Parameter Definitions

/// <summary>
Expand Down Expand Up @@ -314,12 +327,58 @@ public class AddAzureKeyVaultKey : KeyVaultCmdletBase
ParameterSetName = ResourceIdImportParameterSet)]
[PSArgumentCompleter("P-256", "P-256K", "P-384", "P-521")]
public string CurveName { get; set; }

[Parameter(Mandatory = false,
ParameterSetName = HsmInteractiveCreateParameterSet,
HelpMessage = "Indicates if the private key can be exported.")]
[Parameter(Mandatory = false,
ParameterSetName = HsmInputObjectCreateParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = HsmResourceIdCreateParameterSet)]
public SwitchParameter Exportable { get; set; }

[Parameter(Mandatory = false,
ParameterSetName = HsmInteractiveCreateParameterSet,
HelpMessage = "Sets the release policy as immutable state. Once marked immutable, this flag cannot be reset and the policy cannot be changed under any circumstances.")]
[Parameter(Mandatory = false,
ParameterSetName = HsmInputObjectCreateParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = HsmResourceIdCreateParameterSet)]
public SwitchParameter Immutable { get; set; }

[Parameter(Mandatory = false,
ParameterSetName = HsmInteractiveCreateParameterSet,
HelpMessage = "A path to a file containing JSON policy definition. The policy rules under which a key can be exported.")]
[Parameter(Mandatory = false,
ParameterSetName = HsmInputObjectCreateParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = HsmResourceIdCreateParameterSet)]
public string ReleasePolicyPath { get; set; }

[Parameter(Mandatory = false,
ParameterSetName = HsmInteractiveCreateParameterSet,
HelpMessage = "Specifies to use default policy under which the key can be exported for CVM disk encryption.")]
[Parameter(Mandatory = false,
ParameterSetName = HsmInputObjectCreateParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = HsmResourceIdCreateParameterSet)]
public SwitchParameter UseDefaultCVMPolicy { get; set; }
#endregion

private PSKeyReleasePolicy ReleasePolicy { get; set; }

protected override void BeginProcessing()
{
// Preprocess relative path
KeyFilePath = this.TryResolvePath(KeyFilePath);
ReleasePolicyPath = this.TryResolvePath(ReleasePolicyPath);
base.BeginProcessing();
}

public override void ExecuteCmdlet()
{
ValidateParameters();
NormalizeKeySourceParameters();
ValidateKeyExchangeKey();
if (ShouldProcess(Name, Properties.Resources.AddKey))
{
PSKeyVaultKey keyBundle;
Expand Down Expand Up @@ -359,9 +418,37 @@ private void NormalizeKeySourceParameters()
var resourceIdentifier = new ResourceIdentifier(ResourceId);
HsmName = resourceIdentifier.ResourceName;
}

if (this.UseDefaultCVMPolicy.IsPresent)
{
try
{
using (var client = new HttpClient())
{
ReleasePolicy = new PSKeyReleasePolicy()
{
PolicyContent = client.GetStringAsync(DefaultCVMPolicyUrl).ConfigureAwait(true).GetAwaiter().GetResult(),
Immutable = this.Immutable.IsPresent
};
}
}
catch(Exception e)
{
// Swallow exception to fetch default policy
WriteWarning(string.Format(Resources.FetchDefaultCVMPolicyFailed, e.Message));
}
}

if(this.IsParameterBound(c => c.ReleasePolicyPath))
{
ReleasePolicy = new PSKeyReleasePolicy(this.ReleasePolicyPath)
{
Immutable = this.Immutable.IsPresent
};
}
}

private void ValidateKeyExchangeKey()
private void ValidateParameters()
{
if (KeyOps != null && KeyOps.Contains(Constants.KeyOpsImport))
{
Expand All @@ -370,6 +457,29 @@ private void ValidateKeyExchangeKey()
// When KeyOps is 'import', KeyType MUST be RSA-HSM
if (Destination != HsmDestination) { throw new ArgumentException(Resources.KEKMustBeHSM); }
}

if (this.IsParameterBound(c => c.Exportable) && !this.IsParameterBound(c => c.ReleasePolicyPath))
{
throw new AzPSArgumentException("Exportable keys must have release policy.", nameof(ReleasePolicyPath), ErrorKind.UserError);
}
else if (this.IsParameterBound(c => c.ReleasePolicyPath) && !this.IsParameterBound(c => c.Exportable))
{
throw new AzPSArgumentException("Non-exportable keys must not have release policy.", nameof(ReleasePolicyPath), ErrorKind.UserError);
}

if (this.IsParameterBound(c => c.Immutable) && !this.IsParameterBound(c => c.ReleasePolicyPath))
{
throw new AzPSArgumentException("Please provide release policy when Immutable is present.", nameof(Immutable), ErrorKind.UserError);
}

// Verify the ReleasePolicyPath whether exists
if(this.IsParameterBound(c => c.ReleasePolicyPath))
{
if (!File.Exists(ReleasePolicyPath))
{
throw new AzPSArgumentException(string.Format(Resources.FileNotFound, this.ReleasePolicyPath), nameof(ReleasePolicyPath));
}
}
}

private PSKeyVaultKey CreateKeyVaultKey()
Expand Down Expand Up @@ -407,7 +517,7 @@ private PSKeyVaultKey CreateHsmKey()
}
else
{
WriteWarning("Specifying parameter `Disable`, `Expires`, `NotBefore` and `Tag` is not supported when importing key on Managed HSM. Please use `Update-AzKeyVaultKey` after importing.");
WriteWarning("Specifying parameter `Disable`, `Expires`, `NotBefore`, `Tag`, `Exportable`, `ReleasePolicy` and `Immutable` is not supported when importing key on Managed HSM. Please use `Update-AzKeyVaultKey` after importing.");
return this.Track2DataClient.ImportManagedHsmKey(
HsmName, Name,
CreateTrack2WebKeyFromFile());
Expand All @@ -431,13 +541,17 @@ internal PSKeyVaultKeyAttributes CreateKeyAttributes()
}
}

return new Models.PSKeyVaultKeyAttributes(
!Disable.IsPresent,
Expires,
NotBefore,
KeyType,
KeyOps,
Tag);
return new PSKeyVaultKeyAttributes()
{
Enabled = !this.Disable.IsPresent,
Expires = this.Expires,
NotBefore = this.NotBefore,
KeyType = this.KeyType,
KeyOps = this.KeyOps,
Exportable = this.Exportable.IsPresent ? true as bool? : null,
ReleasePolicy = ReleasePolicy ,
Tags = Tag
};
}

internal JsonWebKey CreateWebKeyFromFile()
Expand All @@ -447,7 +561,7 @@ internal JsonWebKey CreateWebKeyFromFile()
FileInfo keyFile = new FileInfo(this.GetUnresolvedProviderPathFromPSPath(this.KeyFilePath));
if (!keyFile.Exists)
{
throw new FileNotFoundException(string.Format(Resources.KeyFileNotFound, this.KeyFilePath));
throw new FileNotFoundException(string.Format(Resources.FileNotFound, this.KeyFilePath));
}

var converterChain = WebKeyConverterFactory.CreateConverterChain();
Expand Down Expand Up @@ -478,7 +592,7 @@ internal Track2Sdk.JsonWebKey CreateTrack2WebKeyFromFile()
FileInfo keyFile = new FileInfo(this.GetUnresolvedProviderPathFromPSPath(this.KeyFilePath));
if (!keyFile.Exists)
{
throw new FileNotFoundException(string.Format(Resources.KeyFileNotFound, this.KeyFilePath));
throw new FileNotFoundException(string.Format(Resources.FileNotFound, this.KeyFilePath));
}

var converterChain = WebKeyConverterFactory.CreateConverterChain();
Expand Down
Loading

0 comments on commit 8be1b24

Please sign in to comment.