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

[KeyVault] Supported creating/updating key with release policy in a Managed HSM #18374

Merged
merged 5 commits into from
Jun 7, 2022
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
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