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] support importing EC key by byok file #14002

Merged
merged 1 commit into from
Jan 28, 2021
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
11 changes: 11 additions & 0 deletions src/KeyVault/KeyVault.Test/PesterTests/Key.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,15 @@ Describe "Update key" {
Get-AzKeyVaultKey -VaultName $vaultName -Name $keyName -IncludeVersions | Update-AzKeyVaultKey -Enable $true
Get-AzKeyVaultKey -VaultName $vaultName -Name $keyName -IncludeVersions | ForEach-Object { $_.Enabled | Should -BeTrue }
}
}

Describe "Add key" {
It "should throw when key type EC and curve name are not paired" {
{
Add-AzKeyVaultKey -VaultName veakkine-kv -Name PSECImportedKey -KeyFilePath E:\targetBlob.byok -KeyType EC -ErrorAction Stop
} | Should -Throw "CurveName"
{
Add-AzKeyVaultKey -VaultName veakkine-kv -Name PSECImportedKey -KeyFilePath E:\targetBlob.byok -CurveName P-256 -ErrorAction Stop
} | Should -Throw "KeyType"
}
}
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 specifying key type and curve name when importing keys via a BYOK file

## Version 3.3.1
* Fixed an issue in Secret Management module
Expand Down
39 changes: 37 additions & 2 deletions src/KeyVault/KeyVault/Commands/AddAzureKeyVaultKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// limitations under the License.
// ----------------------------------------------------------------------------------

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;
Expand Down Expand Up @@ -271,11 +273,17 @@ public class AddAzureKeyVaultKey : KeyVaultCmdletBase

[Parameter(Mandatory = true,
ParameterSetName = HsmInteractiveCreateParameterSet,
HelpMessage = "Specifies the key type of this key.")]
HelpMessage = "Specifies the key type of this key. When importing BYOK keys, it defaults to 'RSA'.")]
[Parameter(Mandatory = true,
ParameterSetName = HsmInputObjectCreateParameterSet)]
[Parameter(Mandatory = true,
ParameterSetName = HsmResourceIdCreateParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = InteractiveImportParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = InputObjectImportParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = ResourceIdImportParameterSet)]
[PSArgumentCompleter("RSA", "EC", "oct")]
public string KeyType { get; set; }

Expand All @@ -286,6 +294,12 @@ public class AddAzureKeyVaultKey : KeyVaultCmdletBase
ParameterSetName = HsmInputObjectCreateParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = HsmResourceIdCreateParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = InteractiveImportParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = InputObjectImportParameterSet)]
[Parameter(Mandatory = false,
ParameterSetName = ResourceIdImportParameterSet)]
[PSArgumentCompleter("P-256", "P-256K", "P-384", "P-521")]
public string CurveName { get; set; }
#endregion
Expand Down Expand Up @@ -405,14 +419,35 @@ internal PSKeyVaultKeyAttributes CreateKeyAttributes()

internal JsonWebKey CreateWebKeyFromFile()
{
ValidateEcParameters();

FileInfo keyFile = new FileInfo(this.GetUnresolvedProviderPathFromPSPath(this.KeyFilePath));
if (!keyFile.Exists)
{
throw new FileNotFoundException(string.Format(Resources.KeyFileNotFound, this.KeyFilePath));
}

var converterChain = WebKeyConverterFactory.CreateConverterChain();
return converterChain.ConvertKeyFromFile(keyFile, KeyFilePassword);
var converterExtraInfo = new WebKeyConverterExtraInfo()
{
KeyType = KeyType,
CurveName = CurveName
};

return converterChain.ConvertKeyFromFile(keyFile, KeyFilePassword, converterExtraInfo);
}

private void ValidateEcParameters()
{
if (JwkHelper.IsEC(KeyType) && string.IsNullOrEmpty(CurveName))
{
throw new AzPSArgumentException(Resources.EcButNoCurveName, nameof(CurveName));
}

if (!string.IsNullOrEmpty(CurveName) && !JwkHelper.IsEC(KeyType))
{
throw new AzPSArgumentException(Resources.CurveNameButNotEc, nameof(KeyType));
}
}

internal Track2Sdk.JsonWebKey CreateTrack2WebKeyFromFile()
Expand Down
4 changes: 4 additions & 0 deletions src/KeyVault/KeyVault/Helpers/JwkHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,9 @@ private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bo
}
}
}

internal static bool IsEC(string keyType) =>
string.Equals(keyType, JsonWebKeyType.EllipticCurve, StringComparison.OrdinalIgnoreCase) ||
string.Equals(keyType, JsonWebKeyType.EllipticCurveHsm, StringComparison.OrdinalIgnoreCase);
}
}
34 changes: 23 additions & 11 deletions src/KeyVault/KeyVault/Models/ByokWebKeyConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
using Track2Sdk = Azure.Security.KeyVault.Keys;
using Track1Sdk = Microsoft.Azure.KeyVault.WebKey;
using System.Security.Cryptography;
using Microsoft.Azure.KeyVault.WebKey;
using Microsoft.Azure.Commands.KeyVault.Helpers;

namespace Microsoft.Azure.Commands.KeyVault.Models
{
Expand All @@ -32,12 +34,22 @@ public ByokWebKeyConverter(IWebKeyConverter next = null)
this.next = next;
}

public Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password)
public Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password, WebKeyConverterExtraInfo extraInfo = null)
{
if (CanProcess(fileInfo))
return Convert(fileInfo.FullName);
{
var jwk = Convert(fileInfo.FullName);

if (JwkHelper.IsEC(extraInfo?.KeyType))
{
jwk.Kty = JsonWebKeyType.EllipticCurveHsm; // byok -> hsm
jwk.CurveName = extraInfo.CurveName;
}

return jwk;
}
else if (next != null)
return next.ConvertKeyFromFile(fileInfo, password);
return next.ConvertKeyFromFile(fileInfo, password, extraInfo);
else
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.UnsupportedFileFormat, fileInfo.Name));
}
Expand Down Expand Up @@ -72,19 +84,19 @@ private Track1Sdk.JsonWebKey Convert(string byokFileName)
T = byokBlob,
};
}

private Track2Sdk.JsonWebKey ConvertToTrack2SdkJsonWebKey(string byokFileName)
{
byte[] byokBlob = File.ReadAllBytes(byokFileName);

if (byokBlob == null || byokBlob.Length == 0)
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.InvalidKeyBlob, "BYOK"));
if (byokBlob == null || byokBlob.Length == 0)
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.InvalidKeyBlob, "BYOK"));

return new Track2Sdk.JsonWebKey(new RSACryptoServiceProvider())
{
KeyType = Track2Sdk.KeyType.RsaHsm,
T = byokBlob,
};
return new Track2Sdk.JsonWebKey(new RSACryptoServiceProvider())
{
KeyType = Track2Sdk.KeyType.RsaHsm,
T = byokBlob,
};
}

private IWebKeyConverter next;
Expand Down
10 changes: 8 additions & 2 deletions src/KeyVault/KeyVault/Models/IWebKeyConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ namespace Microsoft.Azure.Commands.KeyVault.Models
{
internal interface IWebKeyConverter
{
Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password);
Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password, WebKeyConverterExtraInfo extraInfo = null);

Track2Sdk.JsonWebKey ConvertToTrack2SdkKeyFromFile(FileInfo fileInfo, SecureString password);
}


/// <summary>
/// Extra information you may append to the converted JWK
/// </summary>
internal class WebKeyConverterExtraInfo {
public string KeyType;
public string CurveName;
}
}
11 changes: 5 additions & 6 deletions src/KeyVault/KeyVault/Models/PfxWebKeyConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
using KeyVaultProperties = Microsoft.Azure.Commands.KeyVault.Properties;
using Track2Sdk = Azure.Security.KeyVault.Keys;
using Track1Sdk = Microsoft.Azure.KeyVault.WebKey;
using Microsoft.Azure.KeyVault.WebKey;

namespace Microsoft.Azure.Commands.KeyVault.Models
{
Expand All @@ -31,12 +30,12 @@ public PfxWebKeyConverter(IWebKeyConverter next = null)
this.next = next;
}

public Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password)
public Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password, WebKeyConverterExtraInfo extraInfo = null)
{
if (CanProcess(fileInfo))
return Convert(fileInfo.FullName, password);
if (next != null)
return next.ConvertKeyFromFile(fileInfo, password);
return next.ConvertKeyFromFile(fileInfo, password, extraInfo);
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.UnsupportedFileFormat, fileInfo.Name));
}

Expand Down Expand Up @@ -79,7 +78,7 @@ private Track1Sdk.JsonWebKey Convert(string pfxFileName, SecureString pfxPasswor

return CreateJWK(key);
}

private Track2Sdk.JsonWebKey ConvertToTrack2SdkJsonWebKey(string pfxFileName, SecureString pfxPassword)
{
X509Certificate2 certificate;
Expand All @@ -103,7 +102,7 @@ private Track2Sdk.JsonWebKey ConvertToTrack2SdkJsonWebKey(string pfxFileName, Se
// to do: support converting oct to jsonwebKey

throw new ArgumentException(string.Format(KeyVaultProperties.Resources.ImportNotSupported, "oct-HSM"));

}

private static Track1Sdk.JsonWebKey CreateJWK(RSA rsa)
Expand Down Expand Up @@ -151,7 +150,7 @@ private static Track2Sdk.JsonWebKey CreateTrack2SdkJWK(RSA rsa)

private static Track2Sdk.JsonWebKey CreateTrack2SdkJWK(ECDsa ecdSa)
{
if (ecdSa == null)
if (ecdSa == null)
{
throw new ArgumentNullException("ecdSa");
}
Expand Down
18 changes: 18 additions & 0 deletions src/KeyVault/KeyVault/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/KeyVault/KeyVault/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,10 @@ You can find the object ID using Azure Active Directory Module for Windows Power
<data name="SelectiveRestoreFailed" xml:space="preserve">
<value>Failed to selective restore key {0} of managed HSM {1}.</value>
</data>
<data name="EcButNoCurveName" xml:space="preserve">
<value>Please input a valid 'CurveName' when KeyType is 'EC'.</value>
</data>
<data name="CurveNameButNotEc" xml:space="preserve">
<value>When '-CurveName' is specified, '-KeyType' must be 'EC'.</value>
</data>
</root>
28 changes: 20 additions & 8 deletions src/KeyVault/KeyVault/help/Add-AzKeyVaultKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Add-AzKeyVaultKey [-VaultName] <String> [-Name] <String> -Destination <String> [
```
Add-AzKeyVaultKey [-VaultName] <String> [-Name] <String> -KeyFilePath <String>
[-KeyFilePassword <SecureString>] [-Destination <String>] [-Disable] [-KeyOps <String[]>]
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-DefaultProfile <IAzureContextContainer>]
[-WhatIf] [-Confirm] [<CommonParameters>]
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-KeyType <String>] [-CurveName <String>]
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
```

### HsmInteractiveCreate
Expand Down Expand Up @@ -53,8 +53,8 @@ Add-AzKeyVaultKey [-InputObject] <PSKeyVault> [-Name] <String> -Destination <Str
```
Add-AzKeyVaultKey [-InputObject] <PSKeyVault> [-Name] <String> -KeyFilePath <String>
[-KeyFilePassword <SecureString>] [-Destination <String>] [-Disable] [-KeyOps <String[]>]
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-DefaultProfile <IAzureContextContainer>]
[-WhatIf] [-Confirm] [<CommonParameters>]
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-KeyType <String>] [-CurveName <String>]
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
```

### HsmInputObjectCreate
Expand Down Expand Up @@ -83,8 +83,8 @@ Add-AzKeyVaultKey [-ResourceId] <String> [-Name] <String> -Destination <String>
```
Add-AzKeyVaultKey [-ResourceId] <String> [-Name] <String> -KeyFilePath <String>
[-KeyFilePassword <SecureString>] [-Destination <String>] [-Disable] [-KeyOps <String[]>]
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-DefaultProfile <IAzureContextContainer>]
[-WhatIf] [-Confirm] [<CommonParameters>]
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-KeyType <String>] [-CurveName <String>]
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
```

### HsmResourceIdCreate
Expand Down Expand Up @@ -294,7 +294,7 @@ Specifies the curve name of elliptic curve cryptography, this value is valid whe

```yaml
Type: System.String
Parameter Sets: HsmInteractiveCreate, HsmInputObjectCreate, HsmResourceIdCreate
Parameter Sets: InteractiveImport, HsmInteractiveCreate, InputObjectImport, HsmInputObjectCreate, ResourceIdImport, HsmResourceIdCreate
Aliases:

Required: False
Expand Down Expand Up @@ -518,7 +518,19 @@ Accept wildcard characters: False
```

### -KeyType
Specifies the key type of this key.
Specifies the key type of this key. When importing BYOK keys, it defaults to 'RSA'.

```yaml
Type: System.String
Parameter Sets: InteractiveImport, InputObjectImport, ResourceIdImport
Aliases:

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

```yaml
Type: System.String
Expand Down
8 changes: 4 additions & 4 deletions src/KeyVault/KeyVault/help/Get-AzKeyVaultKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,19 @@ Get-AzKeyVaultKey [-HsmObject] <PSManagedHsm> [-Name] <String> [-IncludeVersions

### ByResourceIdVaultName
```
Get-AzKeyVaultKey [-ResourceId] <String> [[-Name] <String>] [-InRemovedState] [-OutFile <String>]
Get-AzKeyVaultKey -ResourceId <String> [[-Name] <String>] [-InRemovedState] [-OutFile <String>]
[-DefaultProfile <IAzureContextContainer>] [<CommonParameters>]
```

### ByResourceIdKeyName
```
Get-AzKeyVaultKey [-ResourceId] <String> [-Name] <String> [-Version] <String> [-OutFile <String>]
Get-AzKeyVaultKey -ResourceId <String> [-Name] <String> [-Version] <String> [-OutFile <String>]
[-DefaultProfile <IAzureContextContainer>] [<CommonParameters>]
```

### ByResourceIdKeyVersions
```
Get-AzKeyVaultKey [-ResourceId] <String> [-Name] <String> [-IncludeVersions] [-OutFile <String>]
Get-AzKeyVaultKey -ResourceId <String> [-Name] <String> [-IncludeVersions] [-OutFile <String>]
[-DefaultProfile <IAzureContextContainer>] [<CommonParameters>]
```

Expand Down Expand Up @@ -471,7 +471,7 @@ Parameter Sets: ByResourceIdVaultName, ByResourceIdKeyName, ByResourceIdKeyVersi
Aliases:

Required: True
Position: 0
Position: Named
Default value: None
Accept pipeline input: True (ByPropertyName)
Accept wildcard characters: False
Expand Down