Skip to content

Commit e6782d3

Browse files
isra-felYeming Liu
andauthored
enable import EC key via byok (#14002)
Co-authored-by: Yeming Liu <[email protected]>
1 parent a26241d commit e6782d3

File tree

11 files changed

+137
-33
lines changed

11 files changed

+137
-33
lines changed

src/KeyVault/KeyVault.Test/PesterTests/Key.Tests.ps1

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,15 @@ Describe "Update key" {
1616
Get-AzKeyVaultKey -VaultName $vaultName -Name $keyName -IncludeVersions | Update-AzKeyVaultKey -Enable $true
1717
Get-AzKeyVaultKey -VaultName $vaultName -Name $keyName -IncludeVersions | ForEach-Object { $_.Enabled | Should -BeTrue }
1818
}
19+
}
20+
21+
Describe "Add key" {
22+
It "should throw when key type EC and curve name are not paired" {
23+
{
24+
Add-AzKeyVaultKey -VaultName veakkine-kv -Name PSECImportedKey -KeyFilePath E:\targetBlob.byok -KeyType EC -ErrorAction Stop
25+
} | Should -Throw "CurveName"
26+
{
27+
Add-AzKeyVaultKey -VaultName veakkine-kv -Name PSECImportedKey -KeyFilePath E:\targetBlob.byok -CurveName P-256 -ErrorAction Stop
28+
} | Should -Throw "KeyType"
29+
}
1930
}

src/KeyVault/KeyVault/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- Additional information about change #1
1919
-->
2020
## Upcoming Release
21+
* Supported specifying key type and curve name when importing keys via a BYOK file
2122

2223
## Version 3.3.1
2324
* Fixed an issue in Secret Management module

src/KeyVault/KeyVault/Commands/AddAzureKeyVaultKey.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15+
using Microsoft.Azure.Commands.Common.Exceptions;
16+
using Microsoft.Azure.Commands.KeyVault.Helpers;
1517
using Microsoft.Azure.Commands.KeyVault.Models;
1618
using Microsoft.Azure.Commands.KeyVault.Properties;
1719
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
@@ -271,11 +273,17 @@ public class AddAzureKeyVaultKey : KeyVaultCmdletBase
271273

272274
[Parameter(Mandatory = true,
273275
ParameterSetName = HsmInteractiveCreateParameterSet,
274-
HelpMessage = "Specifies the key type of this key.")]
276+
HelpMessage = "Specifies the key type of this key. When importing BYOK keys, it defaults to 'RSA'.")]
275277
[Parameter(Mandatory = true,
276278
ParameterSetName = HsmInputObjectCreateParameterSet)]
277279
[Parameter(Mandatory = true,
278280
ParameterSetName = HsmResourceIdCreateParameterSet)]
281+
[Parameter(Mandatory = false,
282+
ParameterSetName = InteractiveImportParameterSet)]
283+
[Parameter(Mandatory = false,
284+
ParameterSetName = InputObjectImportParameterSet)]
285+
[Parameter(Mandatory = false,
286+
ParameterSetName = ResourceIdImportParameterSet)]
279287
[PSArgumentCompleter("RSA", "EC", "oct")]
280288
public string KeyType { get; set; }
281289

@@ -286,6 +294,12 @@ public class AddAzureKeyVaultKey : KeyVaultCmdletBase
286294
ParameterSetName = HsmInputObjectCreateParameterSet)]
287295
[Parameter(Mandatory = false,
288296
ParameterSetName = HsmResourceIdCreateParameterSet)]
297+
[Parameter(Mandatory = false,
298+
ParameterSetName = InteractiveImportParameterSet)]
299+
[Parameter(Mandatory = false,
300+
ParameterSetName = InputObjectImportParameterSet)]
301+
[Parameter(Mandatory = false,
302+
ParameterSetName = ResourceIdImportParameterSet)]
289303
[PSArgumentCompleter("P-256", "P-256K", "P-384", "P-521")]
290304
public string CurveName { get; set; }
291305
#endregion
@@ -405,14 +419,35 @@ internal PSKeyVaultKeyAttributes CreateKeyAttributes()
405419

406420
internal JsonWebKey CreateWebKeyFromFile()
407421
{
422+
ValidateEcParameters();
423+
408424
FileInfo keyFile = new FileInfo(this.GetUnresolvedProviderPathFromPSPath(this.KeyFilePath));
409425
if (!keyFile.Exists)
410426
{
411427
throw new FileNotFoundException(string.Format(Resources.KeyFileNotFound, this.KeyFilePath));
412428
}
413429

414430
var converterChain = WebKeyConverterFactory.CreateConverterChain();
415-
return converterChain.ConvertKeyFromFile(keyFile, KeyFilePassword);
431+
var converterExtraInfo = new WebKeyConverterExtraInfo()
432+
{
433+
KeyType = KeyType,
434+
CurveName = CurveName
435+
};
436+
437+
return converterChain.ConvertKeyFromFile(keyFile, KeyFilePassword, converterExtraInfo);
438+
}
439+
440+
private void ValidateEcParameters()
441+
{
442+
if (JwkHelper.IsEC(KeyType) && string.IsNullOrEmpty(CurveName))
443+
{
444+
throw new AzPSArgumentException(Resources.EcButNoCurveName, nameof(CurveName));
445+
}
446+
447+
if (!string.IsNullOrEmpty(CurveName) && !JwkHelper.IsEC(KeyType))
448+
{
449+
throw new AzPSArgumentException(Resources.CurveNameButNotEc, nameof(KeyType));
450+
}
416451
}
417452

418453
internal Track2Sdk.JsonWebKey CreateTrack2WebKeyFromFile()

src/KeyVault/KeyVault/Helpers/JwkHelper.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,5 +137,9 @@ private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bo
137137
}
138138
}
139139
}
140+
141+
internal static bool IsEC(string keyType) =>
142+
string.Equals(keyType, JsonWebKeyType.EllipticCurve, StringComparison.OrdinalIgnoreCase) ||
143+
string.Equals(keyType, JsonWebKeyType.EllipticCurveHsm, StringComparison.OrdinalIgnoreCase);
140144
}
141145
}

src/KeyVault/KeyVault/Models/ByokWebKeyConverter.cs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
using Track2Sdk = Azure.Security.KeyVault.Keys;
2020
using Track1Sdk = Microsoft.Azure.KeyVault.WebKey;
2121
using System.Security.Cryptography;
22+
using Microsoft.Azure.KeyVault.WebKey;
23+
using Microsoft.Azure.Commands.KeyVault.Helpers;
2224

2325
namespace Microsoft.Azure.Commands.KeyVault.Models
2426
{
@@ -32,12 +34,22 @@ public ByokWebKeyConverter(IWebKeyConverter next = null)
3234
this.next = next;
3335
}
3436

35-
public Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password)
37+
public Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password, WebKeyConverterExtraInfo extraInfo = null)
3638
{
3739
if (CanProcess(fileInfo))
38-
return Convert(fileInfo.FullName);
40+
{
41+
var jwk = Convert(fileInfo.FullName);
42+
43+
if (JwkHelper.IsEC(extraInfo?.KeyType))
44+
{
45+
jwk.Kty = JsonWebKeyType.EllipticCurveHsm; // byok -> hsm
46+
jwk.CurveName = extraInfo.CurveName;
47+
}
48+
49+
return jwk;
50+
}
3951
else if (next != null)
40-
return next.ConvertKeyFromFile(fileInfo, password);
52+
return next.ConvertKeyFromFile(fileInfo, password, extraInfo);
4153
else
4254
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.UnsupportedFileFormat, fileInfo.Name));
4355
}
@@ -72,19 +84,19 @@ private Track1Sdk.JsonWebKey Convert(string byokFileName)
7284
T = byokBlob,
7385
};
7486
}
75-
87+
7688
private Track2Sdk.JsonWebKey ConvertToTrack2SdkJsonWebKey(string byokFileName)
7789
{
7890
byte[] byokBlob = File.ReadAllBytes(byokFileName);
7991

80-
if (byokBlob == null || byokBlob.Length == 0)
81-
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.InvalidKeyBlob, "BYOK"));
92+
if (byokBlob == null || byokBlob.Length == 0)
93+
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.InvalidKeyBlob, "BYOK"));
8294

83-
return new Track2Sdk.JsonWebKey(new RSACryptoServiceProvider())
84-
{
85-
KeyType = Track2Sdk.KeyType.RsaHsm,
86-
T = byokBlob,
87-
};
95+
return new Track2Sdk.JsonWebKey(new RSACryptoServiceProvider())
96+
{
97+
KeyType = Track2Sdk.KeyType.RsaHsm,
98+
T = byokBlob,
99+
};
88100
}
89101

90102
private IWebKeyConverter next;

src/KeyVault/KeyVault/Models/IWebKeyConverter.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,16 @@ namespace Microsoft.Azure.Commands.KeyVault.Models
2121
{
2222
internal interface IWebKeyConverter
2323
{
24-
Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password);
24+
Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password, WebKeyConverterExtraInfo extraInfo = null);
2525

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

29-
29+
/// <summary>
30+
/// Extra information you may append to the converted JWK
31+
/// </summary>
32+
internal class WebKeyConverterExtraInfo {
33+
public string KeyType;
34+
public string CurveName;
35+
}
3036
}

src/KeyVault/KeyVault/Models/PfxWebKeyConverter.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
using KeyVaultProperties = Microsoft.Azure.Commands.KeyVault.Properties;
2121
using Track2Sdk = Azure.Security.KeyVault.Keys;
2222
using Track1Sdk = Microsoft.Azure.KeyVault.WebKey;
23-
using Microsoft.Azure.KeyVault.WebKey;
2423

2524
namespace Microsoft.Azure.Commands.KeyVault.Models
2625
{
@@ -31,12 +30,12 @@ public PfxWebKeyConverter(IWebKeyConverter next = null)
3130
this.next = next;
3231
}
3332

34-
public Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password)
33+
public Track1Sdk.JsonWebKey ConvertKeyFromFile(FileInfo fileInfo, SecureString password, WebKeyConverterExtraInfo extraInfo = null)
3534
{
3635
if (CanProcess(fileInfo))
3736
return Convert(fileInfo.FullName, password);
3837
if (next != null)
39-
return next.ConvertKeyFromFile(fileInfo, password);
38+
return next.ConvertKeyFromFile(fileInfo, password, extraInfo);
4039
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.UnsupportedFileFormat, fileInfo.Name));
4140
}
4241

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

8079
return CreateJWK(key);
8180
}
82-
81+
8382
private Track2Sdk.JsonWebKey ConvertToTrack2SdkJsonWebKey(string pfxFileName, SecureString pfxPassword)
8483
{
8584
X509Certificate2 certificate;
@@ -103,7 +102,7 @@ private Track2Sdk.JsonWebKey ConvertToTrack2SdkJsonWebKey(string pfxFileName, Se
103102
// to do: support converting oct to jsonwebKey
104103

105104
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.ImportNotSupported, "oct-HSM"));
106-
105+
107106
}
108107

109108
private static Track1Sdk.JsonWebKey CreateJWK(RSA rsa)
@@ -151,7 +150,7 @@ private static Track2Sdk.JsonWebKey CreateTrack2SdkJWK(RSA rsa)
151150

152151
private static Track2Sdk.JsonWebKey CreateTrack2SdkJWK(ECDsa ecdSa)
153152
{
154-
if (ecdSa == null)
153+
if (ecdSa == null)
155154
{
156155
throw new ArgumentNullException("ecdSa");
157156
}

src/KeyVault/KeyVault/Properties/Resources.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/KeyVault/KeyVault/Properties/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,4 +585,10 @@ You can find the object ID using Azure Active Directory Module for Windows Power
585585
<data name="SelectiveRestoreFailed" xml:space="preserve">
586586
<value>Failed to selective restore key {0} of managed HSM {1}.</value>
587587
</data>
588+
<data name="EcButNoCurveName" xml:space="preserve">
589+
<value>Please input a valid 'CurveName' when KeyType is 'EC'.</value>
590+
</data>
591+
<data name="CurveNameButNotEc" xml:space="preserve">
592+
<value>When '-CurveName' is specified, '-KeyType' must be 'EC'.</value>
593+
</data>
588594
</root>

src/KeyVault/KeyVault/help/Add-AzKeyVaultKey.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ Add-AzKeyVaultKey [-VaultName] <String> [-Name] <String> -Destination <String> [
2424
```
2525
Add-AzKeyVaultKey [-VaultName] <String> [-Name] <String> -KeyFilePath <String>
2626
[-KeyFilePassword <SecureString>] [-Destination <String>] [-Disable] [-KeyOps <String[]>]
27-
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-DefaultProfile <IAzureContextContainer>]
28-
[-WhatIf] [-Confirm] [<CommonParameters>]
27+
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-KeyType <String>] [-CurveName <String>]
28+
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
2929
```
3030

3131
### HsmInteractiveCreate
@@ -53,8 +53,8 @@ Add-AzKeyVaultKey [-InputObject] <PSKeyVault> [-Name] <String> -Destination <Str
5353
```
5454
Add-AzKeyVaultKey [-InputObject] <PSKeyVault> [-Name] <String> -KeyFilePath <String>
5555
[-KeyFilePassword <SecureString>] [-Destination <String>] [-Disable] [-KeyOps <String[]>]
56-
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-DefaultProfile <IAzureContextContainer>]
57-
[-WhatIf] [-Confirm] [<CommonParameters>]
56+
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-KeyType <String>] [-CurveName <String>]
57+
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
5858
```
5959

6060
### HsmInputObjectCreate
@@ -83,8 +83,8 @@ Add-AzKeyVaultKey [-ResourceId] <String> [-Name] <String> -Destination <String>
8383
```
8484
Add-AzKeyVaultKey [-ResourceId] <String> [-Name] <String> -KeyFilePath <String>
8585
[-KeyFilePassword <SecureString>] [-Destination <String>] [-Disable] [-KeyOps <String[]>]
86-
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-DefaultProfile <IAzureContextContainer>]
87-
[-WhatIf] [-Confirm] [<CommonParameters>]
86+
[-Expires <DateTime>] [-NotBefore <DateTime>] [-Tag <Hashtable>] [-KeyType <String>] [-CurveName <String>]
87+
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
8888
```
8989

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

295295
```yaml
296296
Type: System.String
297-
Parameter Sets: HsmInteractiveCreate, HsmInputObjectCreate, HsmResourceIdCreate
297+
Parameter Sets: InteractiveImport, HsmInteractiveCreate, InputObjectImport, HsmInputObjectCreate, ResourceIdImport, HsmResourceIdCreate
298298
Aliases:
299299

300300
Required: False
@@ -518,7 +518,19 @@ Accept wildcard characters: False
518518
```
519519

520520
### -KeyType
521-
Specifies the key type of this key.
521+
Specifies the key type of this key. When importing BYOK keys, it defaults to 'RSA'.
522+
523+
```yaml
524+
Type: System.String
525+
Parameter Sets: InteractiveImport, InputObjectImport, ResourceIdImport
526+
Aliases:
527+
528+
Required: False
529+
Position: Named
530+
Default value: None
531+
Accept pipeline input: False
532+
Accept wildcard characters: False
533+
```
522534

523535
```yaml
524536
Type: System.String

src/KeyVault/KeyVault/help/Get-AzKeyVaultKey.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,19 @@ Get-AzKeyVaultKey [-HsmObject] <PSManagedHsm> [-Name] <String> [-IncludeVersions
8787

8888
### ByResourceIdVaultName
8989
```
90-
Get-AzKeyVaultKey [-ResourceId] <String> [[-Name] <String>] [-InRemovedState] [-OutFile <String>]
90+
Get-AzKeyVaultKey -ResourceId <String> [[-Name] <String>] [-InRemovedState] [-OutFile <String>]
9191
[-DefaultProfile <IAzureContextContainer>] [<CommonParameters>]
9292
```
9393

9494
### ByResourceIdKeyName
9595
```
96-
Get-AzKeyVaultKey [-ResourceId] <String> [-Name] <String> [-Version] <String> [-OutFile <String>]
96+
Get-AzKeyVaultKey -ResourceId <String> [-Name] <String> [-Version] <String> [-OutFile <String>]
9797
[-DefaultProfile <IAzureContextContainer>] [<CommonParameters>]
9898
```
9999

100100
### ByResourceIdKeyVersions
101101
```
102-
Get-AzKeyVaultKey [-ResourceId] <String> [-Name] <String> [-IncludeVersions] [-OutFile <String>]
102+
Get-AzKeyVaultKey -ResourceId <String> [-Name] <String> [-IncludeVersions] [-OutFile <String>]
103103
[-DefaultProfile <IAzureContextContainer>] [<CommonParameters>]
104104
```
105105

@@ -471,7 +471,7 @@ Parameter Sets: ByResourceIdVaultName, ByResourceIdKeyName, ByResourceIdKeyVersi
471471
Aliases:
472472

473473
Required: True
474-
Position: 0
474+
Position: Named
475475
Default value: None
476476
Accept pipeline input: True (ByPropertyName)
477477
Accept wildcard characters: False

0 commit comments

Comments
 (0)