Skip to content

Commit 85e3cf0

Browse files
joevanwanzeeleKFKeyfactor
andauthored
feat: release 3.2, Added entry parameter to indicate the private key should not be exportable from KeyVault
Co-authored-by: Keyfactor <keyfactor@keyfactor.github.io>
1 parent a225e59 commit 85e3cf0

File tree

6 files changed

+95
-21
lines changed

6 files changed

+95
-21
lines changed

AzureKeyVault/AzureClient.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public virtual async Task<KeyVaultResource> CreateVault()
199199
}
200200
}
201201

202-
public virtual async Task<KeyVaultCertificateWithPolicy> ImportCertificateAsync(string certName, string contents, string pfxPassword, Dictionary<string,string> tags)
202+
public virtual async Task<KeyVaultCertificateWithPolicy> ImportCertificateAsync(string certName, string contents, string pfxPassword, Dictionary<string, string> tags, bool nonExportable)
203203
{
204204
try
205205
{
@@ -221,6 +221,7 @@ public virtual async Task<KeyVaultCertificateWithPolicy> ImportCertificateAsync(
221221
logger.LogTrace($"calling ImportCertificateAsync on the KeyVault certificate client to import certificate {certName}");
222222

223223
var options = new ImportCertificateOptions(certName, p12bytes);
224+
options.Policy = new CertificatePolicy { Exportable = !nonExportable, ContentType = CertificateContentType.Pkcs12 };
224225

225226
if (tags.Any())
226227
{
@@ -388,7 +389,7 @@ public virtual (List<string>, List<string>) GetVaults()
388389
var warning = $"Exception thrown performing discovery on tenantId {searchTenantId} and subscription ID {searchSubscription}. Exception message: {ex.Message}";
389390

390391
logger.LogWarning(warning);
391-
warnings.Add(warning);
392+
warnings.Add(warning);
392393
}
393394

394395
return (vaultNames, warnings);

AzureKeyVault/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ static class AzureKeyVaultConstants
1616
static class EntryParameters {
1717
public const string TAGS = "CertificateTags";
1818
public const string PRESERVE_TAGS = "PreserveExistingTags";
19+
public const string NON_EXPORTABLE = "NonExportable";
1920
}
2021

2122
static class JobTypes

AzureKeyVault/Jobs/Management.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
using Keyfactor.Orchestrators.Extensions.Interfaces;
1818
using System.Collections.Generic;
1919
using Newtonsoft.Json;
20-
using System.Security.AccessControl;
2120

2221
namespace Keyfactor.Extensions.Orchestrator.AzureKeyVault
2322
{
@@ -46,11 +45,13 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
4645

4746
string tagsJSON;
4847
bool preserveTags;
48+
bool nonExportable;
4949

5050
logger.LogTrace("parsing entry parameters.. ");
5151

5252
tagsJSON = config.JobProperties[EntryParameters.TAGS] as string ?? string.Empty;
5353
preserveTags = config.JobProperties[EntryParameters.PRESERVE_TAGS] as bool? ?? false;
54+
nonExportable = config.JobProperties[EntryParameters.NON_EXPORTABLE] as bool? ?? false;
5455

5556
switch (config.OperationType)
5657
{
@@ -61,7 +62,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
6162
case CertStoreOperationType.Add:
6263
logger.LogDebug($"Begin Management > Add...");
6364

64-
complete = PerformAddition(config.JobCertificate.Alias, config.JobCertificate.PrivateKeyPassword, config.JobCertificate.Contents, tagsJSON, config.JobHistoryId, config.Overwrite, preserveTags);
65+
complete = PerformAddition(config.JobCertificate.Alias, config.JobCertificate.PrivateKeyPassword, config.JobCertificate.Contents, tagsJSON, config.JobHistoryId, config.Overwrite, preserveTags, nonExportable);
6566
break;
6667
case CertStoreOperationType.Remove:
6768
logger.LogDebug($"Begin Management > Remove...");
@@ -103,7 +104,7 @@ protected async Task<JobResult> PerformCreateVault(long jobHistoryId)
103104
#endregion
104105

105106
#region Add
106-
protected virtual JobResult PerformAddition(string alias, string pfxPassword, string entryContents, string tagsJSON, long jobHistoryId, bool overwrite, bool preserveTags)
107+
protected virtual JobResult PerformAddition(string alias, string pfxPassword, string entryContents, string tagsJSON, long jobHistoryId, bool overwrite, bool preserveTags, bool nonExportable)
107108
{
108109
var complete = new JobResult() { Result = OrchestratorJobStatusJobResult.Failure, JobHistoryId = jobHistoryId };
109110

@@ -138,16 +139,16 @@ protected virtual JobResult PerformAddition(string alias, string pfxPassword, st
138139
if (existing != null)
139140
{
140141
logger.LogTrace($"there is an existing cert..");
141-
}
142142

143-
existingTags = existing?.Properties.Tags as Dictionary<string, string> ?? new Dictionary<string, string>();
143+
existingTags = existing?.Properties.Tags as Dictionary<string, string> ?? new Dictionary<string, string>();
144144

145-
logger.LogTrace("existing cert tags: ");
146-
if (!existingTags.Any()) logger.LogTrace("(none)");
145+
logger.LogTrace("existing cert tags: ");
146+
if (!existingTags.Any()) logger.LogTrace("(none)");
147147

148-
foreach (var tag in existingTags)
149-
{
150-
logger.LogTrace(tag.Key + " : " + tag.Value);
148+
foreach (var tag in existingTags)
149+
{
150+
logger.LogTrace(tag.Key + " : " + tag.Value);
151+
}
151152
}
152153

153154
// if overwrite is unchecked, check for an existing cert first
@@ -173,7 +174,7 @@ protected virtual JobResult PerformAddition(string alias, string pfxPassword, st
173174
}
174175
}
175176

176-
var cert = AzClient.ImportCertificateAsync(alias, entryContents, pfxPassword, tagDict).Result;
177+
var cert = AzClient.ImportCertificateAsync(alias, entryContents, pfxPassword, tagDict, nonExportable).Result;
177178

178179
// Ensure the return object has a AKV version tag, and Thumbprint
179180
if (!string.IsNullOrEmpty(cert.Properties.Version) &&

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
- 3.2.0
2-
- Fancy new features here
2+
- Added an optional entry parameter to indicate whether the private key of the cert should be not exportable when stored in KeyVault
3+
- Now specifying the pkcs12 format when wirting certs to Azure KeyVault. This should prevent the error when a PEM cert was added outside of Command and then we attempt to update without specifying the format (Azure assumes PEM and throws an error if not).
4+
35
- 3.1.9
46
- Added optional entry parameter to indicate that existing tags should be preserved if certificate is replaced
5-
- bug fix for government cloud host name resolution
67

78
- 3.1.8
89
- Fixed bug where enrollment would fail if the CertificateTags field was not defined as an entry parameter
@@ -13,7 +14,6 @@
1314
- Added support for Azure KeyVault Certificate Metadata via Entry Parameters
1415
- Fixed issue where an error would be returned during Inventory if 0 certificates were found
1516
- Converted to BouncyCastle crypto libraries
16-
1717

1818
- 3.1.6
1919
- Preventing CertStore parameters from getting used if present but empty.

README.md

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -658,32 +658,90 @@ the Keyfactor Command Portal
658658

659659
![AKV Custom Fields Tab](docsource/images/AKV-custom-fields-store-type-dialog.png)
660660

661+
662+
###### Tenant Id
663+
The ID of the primary Azure Tenant where the KeyVaults are hosted
664+
665+
![AKV Custom Field - TenantId](docsource/images/AKV-custom-field-TenantId-dialog.png)
666+
667+
668+
669+
###### SKU Type
670+
The SKU type for newly created KeyVaults (only needed if needing to create new KeyVaults in your Azure subscription via Command)
671+
672+
![AKV Custom Field - SkuType](docsource/images/AKV-custom-field-SkuType-dialog.png)
673+
674+
675+
676+
###### Vault Region
677+
The Azure Region to put newly created KeyVaults (only needed if needing to create new KeyVaults in your Azure subscription via Command)
678+
679+
![AKV Custom Field - VaultRegion](docsource/images/AKV-custom-field-VaultRegion-dialog.png)
680+
681+
682+
683+
###### Azure Cloud
684+
The Azure Cloud where the KeyVaults are located (only necessary if not using the standard Azure Public cloud)
685+
686+
![AKV Custom Field - AzureCloud](docsource/images/AKV-custom-field-AzureCloud-dialog.png)
687+
688+
689+
690+
###### Private KeyVault Endpoint
691+
The private endpoint of your vault instance (if a private endpoint is configured in Azure)
692+
693+
![AKV Custom Field - PrivateEndpoint](docsource/images/AKV-custom-field-PrivateEndpoint-dialog.png)
694+
695+
696+
697+
698+
661699
##### Entry Parameters Tab
662700

663701
| Name | Display Name | Description | Type | Default Value | Entry has a private key | Adding an entry | Removing an entry | Reenrolling an entry |
664702
| ---- | ------------ | ---- | ------------- | ----------------------- | ---------------- | ----------------- | ------------------- | ----------- |
665703
| CertificateTags | Certificate Tags | If desired, tags can be applied to the KeyVault entries. Provide them as a JSON string of key-value pairs ie: '{'tag-name': 'tag-content', 'other-tag-name': 'other-tag-content'}' | string | | 🔲 Unchecked | 🔲 Unchecked | 🔲 Unchecked | 🔲 Unchecked |
666704
| PreserveExistingTags | Preserve Existing Tags | If true, this will perform a union of any tags provided with enrollment with the tags on the existing cert with the same alias and apply the result to the new certificate. | Bool | False | 🔲 Unchecked | 🔲 Unchecked | 🔲 Unchecked | 🔲 Unchecked |
705+
| NonExportable | Non Exportable Private Key | If true, this will mark the certificate as having a non-exportable private key when importing into Azure KeyVault | Bool | False | 🔲 Unchecked | 🔲 Unchecked | 🔲 Unchecked | 🔲 Unchecked |
667706

668707
The Entry Parameters tab should look like this:
669708

670709
![AKV Entry Parameters Tab](docsource/images/AKV-entry-parameters-store-type-dialog.png)
671710

711+
712+
##### Certificate Tags
713+
If desired, tags can be applied to the KeyVault entries. Provide them as a JSON string of key-value pairs ie: '{'tag-name': 'tag-content', 'other-tag-name': 'other-tag-content'}'
714+
715+
![AKV Entry Parameter - CertificateTags](docsource/images/AKV-entry-parameters-store-type-dialog-CertificateTags.png)
716+
717+
718+
##### Preserve Existing Tags
719+
If true, this will perform a union of any tags provided with enrollment with the tags on the existing cert with the same alias and apply the result to the new certificate.
720+
721+
![AKV Entry Parameter - PreserveExistingTags](docsource/images/AKV-entry-parameters-store-type-dialog-PreserveExistingTags.png)
722+
723+
724+
##### Non Exportable Private Key
725+
If true, this will mark the certificate as having a non-exportable private key when importing into Azure KeyVault
726+
727+
![AKV Entry Parameter - NonExportable](docsource/images/AKV-entry-parameters-store-type-dialog-NonExportable.png)
728+
729+
730+
672731
</details>
673732

674733
## Installation
675734

676735
1. **Download the latest Azure Key Vault Universal Orchestrator extension from GitHub.**
677736

678-
Navigate to the [Azure Key Vault Universal Orchestrator extension GitHub version page](https://github.com/Keyfactor/azurekeyvault-orchestrator/releases/latest). Refer to the compatibility matrix below to determine whether the `net6.0` or `net8.0` asset should be downloaded. Then, click the corresponding asset to download the zip archive.
737+
Navigate to the [Azure Key Vault Universal Orchestrator extension GitHub version page](https://github.com/Keyfactor/azurekeyvault-orchestrator/releases/latest). Refer to the compatibility matrix below to determine the asset should be downloaded. Then, click the corresponding asset to download the zip archive.
679738

680739
| Universal Orchestrator Version | Latest .NET version installed on the Universal Orchestrator server | `rollForward` condition in `Orchestrator.runtimeconfig.json` | `azurekeyvault-orchestrator` .NET version to download |
681740
| --------- | ----------- | ----------- | ----------- |
682741
| Older than `11.0.0` | | | `net6.0` |
683742
| Between `11.0.0` and `11.5.1` (inclusive) | `net6.0` | | `net6.0` |
684-
| Between `11.0.0` and `11.5.1` (inclusive) | `net8.0` | `Disable` | `net6.0` |
685-
| Between `11.0.0` and `11.5.1` (inclusive) | `net8.0` | `LatestMajor` | `net8.0` |
686-
| `11.6` _and_ newer | `net8.0` | | `net8.0` |
743+
| Between `11.0.0` and `11.5.1` (inclusive) | `net8.0` | `Disable` | `net6.0` || Between `11.0.0` and `11.5.1` (inclusive) | `net8.0` | `LatestMajor` | `net8.0` |
744+
| `11.6` _and_ newer | `net8.0` | | `net8.0` |
687745

688746
Unzip the archive containing extension assemblies to a known location.
689747

integration-manifest.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,20 @@
4646
"OnRemove": false,
4747
"OnReenrollment": false
4848
}
49-
}
49+
},
50+
{
51+
"Name": "NonExportable",
52+
"DisplayName": "Non Exportable Private Key",
53+
"Description": "If true, this will mark the certificate as having a non-exportable private key when importing into Azure KeyVault",
54+
"Type": "Bool",
55+
"DefaultValue": "False",
56+
"RequiredWhen": {
57+
"HasPrivateKey": false,
58+
"OnAdd": false,
59+
"OnRemove": false,
60+
"OnReenrollment": false
61+
}
62+
}
5063
],
5164
"JobProperties": [],
5265
"LocalStore": false,

0 commit comments

Comments
 (0)