Skip to content

Commit

Permalink
enable import EC key via byok (#14002)
Browse files Browse the repository at this point in the history
Co-authored-by: Yeming Liu <[email protected]>
  • Loading branch information
isra-fel and Yeming Liu authored Jan 28, 2021
1 parent a26241d commit e6782d3
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 33 deletions.
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

0 comments on commit e6782d3

Please sign in to comment.