From 57548f969ad3120432523b563b8996ad6ad60531 Mon Sep 17 00:00:00 2001 From: Bob Pokorny <55611381+rcpokorny@users.noreply.github.com> Date: Thu, 1 Dec 2022 12:51:29 -0600 Subject: [PATCH 01/29] Doc updates (#27) * Added Apache License to source code and removed unfinished work comments. --- IISU/IISManager.cs | 16 +++++++++++++++- IISU/JobProperties.cs | 16 +++++++++++++++- IISU/Jobs/Inventory.cs | 14 ++++++++++++++ IISU/Jobs/Management.cs | 16 +++++++++++++++- IISU/Jobs/ReEnrollment.cs | 17 +++++++++++++++-- IISU/PSCertStoreException.cs | 16 +++++++++++++++- IISU/PSCertificate.cs | 16 +++++++++++++++- IISU/PowerShellCertException.cs | 16 +++++++++++++++- IISU/PowerShellCertStore.cs | 18 ++++++++++++++++-- 9 files changed, 135 insertions(+), 10 deletions(-) diff --git a/IISU/IISManager.cs b/IISU/IISManager.cs index 30aea1d..b5926e3 100644 --- a/IISU/IISManager.cs +++ b/IISU/IISManager.cs @@ -1,4 +1,18 @@ -using System; +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; using System.Linq; using System.Management.Automation; using System.Management.Automation.Runspaces; diff --git a/IISU/JobProperties.cs b/IISU/JobProperties.cs index aa3ccc0..7c78948 100644 --- a/IISU/JobProperties.cs +++ b/IISU/JobProperties.cs @@ -1,4 +1,18 @@ -using System.ComponentModel; +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; using Newtonsoft.Json; namespace Keyfactor.Extensions.Orchestrator.IISU diff --git a/IISU/Jobs/Inventory.cs b/IISU/Jobs/Inventory.cs index 535b1cb..be8acb5 100644 --- a/IISU/Jobs/Inventory.cs +++ b/IISU/Jobs/Inventory.cs @@ -1,3 +1,17 @@ +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + using System; using System.Collections.Generic; using System.Linq; diff --git a/IISU/Jobs/Management.cs b/IISU/Jobs/Management.cs index fc015cd..944e21e 100644 --- a/IISU/Jobs/Management.cs +++ b/IISU/Jobs/Management.cs @@ -1,4 +1,18 @@ -using System; +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; using System.Linq; using System.Management.Automation; using System.Management.Automation.Runspaces; diff --git a/IISU/Jobs/ReEnrollment.cs b/IISU/Jobs/ReEnrollment.cs index 7a6d04c..e4750cc 100644 --- a/IISU/Jobs/ReEnrollment.cs +++ b/IISU/Jobs/ReEnrollment.cs @@ -1,4 +1,18 @@ -using System; +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; using System.Collections.ObjectModel; using System.Linq; using System.Management.Automation; @@ -108,7 +122,6 @@ private JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submi ps.AddScript($"Add-Content $infFilename '[Extensions]'"); ps.AddScript(@"Add-Content $infFilename '2.5.29.17 = ""{text}""'"); - // Todo: Parse SAN by '&' and add the below entry for each DSN foreach (string s in SAN.ToString().Split("&")) { ps.AddScript($"Add-Content $infFilename '_continue_ = \"{s + "&"}\"'"); diff --git a/IISU/PSCertStoreException.cs b/IISU/PSCertStoreException.cs index 702dc8f..5afc67b 100644 --- a/IISU/PSCertStoreException.cs +++ b/IISU/PSCertStoreException.cs @@ -1,4 +1,18 @@ -using System; +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; using System.Runtime.Serialization; namespace Keyfactor.Extensions.Orchestrator.IISU diff --git a/IISU/PSCertificate.cs b/IISU/PSCertificate.cs index 832bee7..5bfdf7f 100644 --- a/IISU/PSCertificate.cs +++ b/IISU/PSCertificate.cs @@ -1,4 +1,18 @@ -using System; +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; namespace Keyfactor.Extensions.Orchestrator.IISU { diff --git a/IISU/PowerShellCertException.cs b/IISU/PowerShellCertException.cs index 160176d..87d915c 100644 --- a/IISU/PowerShellCertException.cs +++ b/IISU/PowerShellCertException.cs @@ -1,4 +1,18 @@ -using System; +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Text; diff --git a/IISU/PowerShellCertStore.cs b/IISU/PowerShellCertStore.cs index ecf11f7..ce9505a 100644 --- a/IISU/PowerShellCertStore.cs +++ b/IISU/PowerShellCertStore.cs @@ -1,4 +1,18 @@ -using System; +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; using System.Collections.Generic; using System.Management.Automation; using System.Management.Automation.Runspaces; @@ -50,7 +64,7 @@ private void Initalize() { using var ps = PowerShell.Create(); ps.Runspace = RunSpace; - //todo: accept StoreType and Store Name enum for which to open + var certStoreScript = $@" $certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('{StorePath}','LocalMachine') $certStore.Open('ReadOnly') From 3ceb4629a724ffb24e9dd3b004683b54359604b7 Mon Sep 17 00:00:00 2001 From: Brian Hill <76450501+bhillkeyfactor@users.noreply.github.com> Date: Thu, 1 Dec 2022 14:12:55 -0500 Subject: [PATCH 02/29] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f059019..529d23b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +2.0.0 +* PAM Support added (requires Univesal Orchestrator Framework version 10.1) + 1.1.3 * Made WinRM port a store parameter * Made WinRM protocol a store parameter From 55b45ab36d1c32b7ac1fb99dfd1eebf4cae39f3d Mon Sep 17 00:00:00 2001 From: Bob Pokorny <55611381+rcpokorny@users.noreply.github.com> Date: Thu, 1 Dec 2022 13:57:50 -0600 Subject: [PATCH 03/29] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 529d23b..c416c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 2.0.0 * PAM Support added (requires Univesal Orchestrator Framework version 10.1) +* Added HSM Support (Use Powershell certutil -csplist to obtain a list of supported Crypto Providers) 1.1.3 * Made WinRM port a store parameter From bb63cd6945050d08741e77c66797bba20c28aae1 Mon Sep 17 00:00:00 2001 From: Rex Wheeler Date: Thu, 1 Dec 2022 13:31:58 -0800 Subject: [PATCH 04/29] Update CHANGELOG.md --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c416c5d..04ea1b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 2.0.0 -* PAM Support added (requires Univesal Orchestrator Framework version 10.1) -* Added HSM Support (Use Powershell certutil -csplist to obtain a list of supported Crypto Providers) +* Add support for reenrollment jobs (On Device Key Generation) with the ability to specify a cryptographic provider. Specification of cryptographic provider allows HSM (Hardware Security Module) use. +* Local PAM Support added (requires Univesal Orchestrator Framework version 10.1) +* Certificate store type changed from IISBin to IISU. See readme for migration notes. 1.1.3 * Made WinRM port a store parameter From 0d628f714d90a2aca5b3d53d1755c584d30e9d3f Mon Sep 17 00:00:00 2001 From: Bob Pokorny <55611381+rcpokorny@users.noreply.github.com> Date: Thu, 1 Dec 2022 15:45:20 -0600 Subject: [PATCH 05/29] Add files via upload --- Migration-Scripts/IIS-Conversion.sql | 190 +++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 Migration-Scripts/IIS-Conversion.sql diff --git a/Migration-Scripts/IIS-Conversion.sql b/Migration-Scripts/IIS-Conversion.sql new file mode 100644 index 0000000..4048861 --- /dev/null +++ b/Migration-Scripts/IIS-Conversion.sql @@ -0,0 +1,190 @@ +-- REQUIREMENTS: +-- SQL Server 2016 or later + +-- PREREQUISITES: +-- 1) The certificate store types you are attempting to convert TO need to already be properly set up in Keyfactor Command +-- 2) Make sure to back up the targeted Keyfactor Command database before running this + + +-- !!! SPECIAL NOTE - PLEASE READ !!! +-- If you are converting from JKS-SSH to the appropriate RemoteFile type, be aware that java keystores of type JKS need to be converted to RFJKS while java keystores +-- of type PKCS12 need to be converted to RFPkcs12. The conversion process below will do one or the other. If the client in question has a mix, you either need to: +-- 1) Choose the type that covers the most stores, run this script, and then manually convert the the stores with the incorrect RemoteFile type to the correct one, or +-- 2) Do not use the script below to convert java keystores and set up each store manually. + + +-- *** BEGIN SET UP *** + +-- *** STEP 1 OF 4: set store type short name of the stores you wish to convert. *** +declare @IISBinShortName varchar(50) = 'IISWBin' -- By convention usually 'IISWBin' + +-- *** STEP 2 OF 4: set the corresponding short name of the store type you wish to convert stores to +declare @IISUShortName varchar(50) = 'IISU' -- By convention usually 'IISWBin'. + +-- *** STEP 3 OF 4: set whether you want to update the client's database permanently or just do a test run where results will be displayed but the database will NOT be updated +declare @RunInTestMode bit = 0 -- 0=database will be updated, 1=test run, client database will NOT be updated + +-- *** STEP 4 OF 4: set whether you want to remove the IISWBin certificate store type after converting +declare @RemoveIISWBin bit = 1 -- 0=Do Not Remove, 1=Remove + +-- *** END SET UP *** + + + +declare @RFPEMStoreTypeId int + +declare @StoreTypesToConvert TABLE(ToStoreShortName varchar(50), FromCertStoreTypeId int, ToCertStoreTypeId int, + FromServerRegistration int, ToServerRegistration int, + FromInventoryJobType uniqueidentifier, ToInventoryJobType uniqueidentifier, + FromManagementJobType uniqueidentifier, ToManagementJobType uniqueidentifier) + +-- validate input +print 'validating...' +if (@IISBinShortName <> '' and not exists (select StoreType from cms_agents.CertStoreTypes where ShortName = @IISBinShortName)) +begin + select 'IISWBin store type not found'; + return; +end +if (@IISUShortName <> '' and not exists (select StoreType from cms_agents.CertStoreTypes where ShortName = @IISUShortName)) +begin + select 'IISU certificate store type not found'; + return; +end + + +-- Set up store type temp table +print 'get stores to convert' +insert into @StoreTypesToConvert +(ToStoreShortName, FromCertStoretypeId, ToCertStoreTypeId, FromServerRegistration, ToServerRegistration, + FromInventoryJobType, ToInventoryJobType, FromManagementJobType, ToManagementJobType) +select b.ShortName, a.StoreType, b.StoreType, a.ServerRegistration, b.ServerRegistration, + a.InventoryJobType, b.InventoryJobType, a.ManagementJobType, b.ManagementJobType +from cms_agents.CertStoreTypes a +inner join cms_agents.CertStoreTypes b on 1=1 +where (a.ShortName = @IISBinShortName and b.ShortName = @IISUShortName) + + + +begin try +begin transaction + + print 'convert containers' + -- convert certificate store containers + update a + set CertStoreType = b.ToCertStoreTypeId + from cms_agents.CertStoreContainers a + inner join @StoreTypesToConvert b on a.CertStoreType = b.FromCertStoreTypeId + + print 'convert certificate stores' + -- convert certificate stores + update a + set a.CertStoreType = b.ToCertStoreTypeId + from cms_agents.CertStores a + inner join @StoreTypesToConvert b on a.CertStoreType = b.FromCertStoreTypeId + + print 'convert certificate store servers' + -- convert certificate store servers + update a + set a.ServerType = b.ToServerRegistration + from cms_agents.CertStoreServers a + inner join @StoreTypesToConvert b on a.ServerType = b.FromServerRegistration + where not exists (select * from cms_agents.CertStoreServers c where c.Name = a.Name and c.ServerType = b.ToServerRegistration) + + print 'convert existing entry parameters' + update a + set a.StoreTypeId = b.ToCertStoreTypeId + from cms_agents.CertStoreTypeEntryParameters a + inner join @StoreTypesToConvert b on a.StoreTypeId = b.FromCertStoreTypeId + + print 'Convert job schedules' + update a + set a.JobTypeId = b.ToJobType + from cms_agents.AgentSchedules a + inner join (select FromInventoryJobType FromJobType, ToInventoryJobType ToJobType + from @StoreTypesToConvert + union all + select FromManagementJobType FromJobType, ToManagementJobType ToJobType + from @StoreTypesToConvert + ) b on a.JobTypeId = b.FromJobType + + print 'Convert agent capabilities' + update a + set a.JobTypeId = b.ToJobType + from cms_agents.AgentCapabilities a + inner join (select FromInventoryJobType FromJobType, ToInventoryJobType ToJobType + from @StoreTypesToConvert + union all + select FromManagementJobType FromJobType, ToManagementJobType ToJobType + from @StoreTypesToConvert + ) b on a.JobTypeId = b.FromJobType + + print 'Convert blueprint jobs' + update a + set a.JobType = b.ToJobType + from cms_agents.AgentBlueprintJobs a + inner join (select FromInventoryJobType FromJobType, ToInventoryJobType ToJobType + from @StoreTypesToConvert + union all + select FromManagementJobType FromJobType, ToManagementJobType ToJobType + from @StoreTypesToConvert + ) b on a.JobType = b.FromJobType + + if (@RemoveIISWBin = 1) + begin + print 'Remove old IISWBin store type entry parameters' + delete a + from cms_agents.CertStoreTypeEntryParameters a + inner join @StoreTypesToConvert b on a.StoreTypeId = b.FromCertStoreTypeId + + print 'Remove old IISWBin store type custom properties' + delete a + from cms_agents.CertStoreTypeProperties a + inner join @StoreTypesToConvert b on a.StoreTypeId = b.FromCertStoreTypeId + + print 'Remove old IISWBin store type' + delete a + from cms_agents.CertStoreTypes a + inner join @StoreTypesToConvert b on a.StoreType = b.FromCertStoreTypeId + end + + select * + from cms_agents.CertStoreTypes + + select * + from cms_agents.CertStoreTypeProperties + + select * + from cms_agents.CertStoreTypeEntryParameters + + select * + from cms_agents.certstores + + select * + from cms_agents.CertStoreServers + + select * + from cms_agents.CertStoreContainers + + select * + from cms_agents.AgentSchedules + + if (@RunInTestMode = 1) + begin + rollback tran; + end + else + begin + commit tran; + end + +end try + +begin catch + if @@TRANCOUNT > 0 + rollback tran; + + SELECT + ERROR_MESSAGE(), + ERROR_SEVERITY(), + ERROR_STATE(); +end catch From c3b1ff81d101d5f73b74a102875085247f3faca2 Mon Sep 17 00:00:00 2001 From: Bob Pokorny Date: Thu, 1 Dec 2022 15:53:11 -0600 Subject: [PATCH 06/29] Moved sql scripts to new folder --- .../IISWBin 1.1.3 upgrade script.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename IISWBin 1.1.3 upgrade script.sql => Migration-Scripts/IISWBin 1.1.3 upgrade script.sql (100%) diff --git a/IISWBin 1.1.3 upgrade script.sql b/Migration-Scripts/IISWBin 1.1.3 upgrade script.sql similarity index 100% rename from IISWBin 1.1.3 upgrade script.sql rename to Migration-Scripts/IISWBin 1.1.3 upgrade script.sql From f5e8c208b3841fe85cbe1a083ae2a2158fe1e852 Mon Sep 17 00:00:00 2001 From: Mikey Henderson Date: Thu, 1 Dec 2022 15:24:16 -0800 Subject: [PATCH 07/29] update release_dir (#30) release_dir updated in integration-manifest.json --- .github/workflows/keyfactor-starter-workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/keyfactor-starter-workflow.yml b/.github/workflows/keyfactor-starter-workflow.yml index dbda95e..bc2e627 100644 --- a/.github/workflows/keyfactor-starter-workflow.yml +++ b/.github/workflows/keyfactor-starter-workflow.yml @@ -11,7 +11,7 @@ jobs: with: release_version: ${{ needs.call-create-github-release-workflow.outputs.release_version }} release_url: ${{ needs.call-create-github-release-workflow.outputs.release_url }} - release_dir: IISWithBindings/bin/Release/netcoreapp3.1 # output directory to upload as a release, relative to checkout workspace + release_dir: IISU/bin/Release/netcoreapp3.1 secrets: token: ${{ secrets.PRIVATE_PACKAGE_ACCESS }} From 382633c6dc036b9ee239a6e3389c494df56797e6 Mon Sep 17 00:00:00 2001 From: Rex Wheeler Date: Fri, 2 Dec 2022 11:07:04 -0800 Subject: [PATCH 08/29] Add more context to Provider Name and SAN parms --- readme_source.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/readme_source.md b/readme_source.md index 12b4497..7511832 100644 --- a/readme_source.md +++ b/readme_source.md @@ -81,8 +81,9 @@ This section must be configured with binding fields. The parameters will be popu - 1 - SNI Enabled - 2 - Non SNI Binding - 3 - SNI Binding -- **Provider Name** - Optional. To get a list of Crypto Providers, open PowerShell and issue the 'certutil -csplist' command. If no Provider Name is provided, the 'Microsoft Strong Cryptographic Provider' will be used. -- **SAN** - Required. The SAN must have one entry that matches the Subject Name when using ReEnrollment. Multiple SANs maybe chained together using '&'. Example: dns=www.mysite.com&dns=www.mysite2.com. +- **Provider Name** - Optional. Name of the Windows cryptographic provider to use when generating and storing the private key for the certificate being enrolled by a reenrollment job. If not specified, defaults to 'Microsoft Strong Cryptographic Provider'. This value would typically be changed when leveraging a Hardware Security Module (HSM). The specified cryptographic provider must be available on the target IIS server being managed. The list of installed cryptographic providers can be obtained by running 'certutil -csplist' in a command shell on the target IIS Server. + +- **SAN** - Required. Specifies Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Certificate templates generally require a SAN that matches the subject of the certificate (per RFC 2818). Format is a list of = entries separated by ampersands. Examples: 'dns=www.mysite.com' for a single SAN or 'dns=www.mysite.com&dns=www.mysite2.com' for multiple SANs. Parameter Name|Parameter Type|Default Value|Required ---|---|---|--- From 9cec5e3d7d6998b29325b9b2a883fa265344c87f Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Fri, 2 Dec 2022 19:08:00 +0000 Subject: [PATCH 09/29] Update generated README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b471a4a..186ba1f 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,9 @@ This section must be configured with binding fields. The parameters will be popu - 1 - SNI Enabled - 2 - Non SNI Binding - 3 - SNI Binding -- **Provider Name** - Optional. To get a list of Crypto Providers, open PowerShell and issue the 'certutil -csplist' command. If no Provider Name is provided, the 'Microsoft Strong Cryptographic Provider' will be used. -- **SAN** - Required. The SAN must have one entry that matches the Subject Name when using ReEnrollment. Multiple SANs maybe chained together using '&'. Example: dns=www.mysite.com&dns=www.mysite2.com. +- **Provider Name** - Optional. Name of the Windows cryptographic provider to use when generating and storing the private key for the certificate being enrolled by a reenrollment job. If not specified, defaults to 'Microsoft Strong Cryptographic Provider'. This value would typically be changed when leveraging a Hardware Security Module (HSM). The specified cryptographic provider must be available on the target IIS server being managed. The list of installed cryptographic providers can be obtained by running 'certutil -csplist' in a command shell on the target IIS Server. + +- **SAN** - Required. Specifies Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Certificate templates generally require a SAN that matches the subject of the certificate (per RFC 2818). Format is a list of = entries separated by ampersands. Examples: 'dns=www.mysite.com' for a single SAN or 'dns=www.mysite.com&dns=www.mysite2.com' for multiple SANs. Parameter Name|Parameter Type|Default Value|Required ---|---|---|--- From d0f228f5950da7255e911b6e501b33a8cb78b7cf Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Fri, 2 Dec 2022 15:18:18 -0500 Subject: [PATCH 10/29] Fixed Remove Bug Found after ReEnrollment Refactoring --- IISU/Jobs/Management.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/IISU/Jobs/Management.cs b/IISU/Jobs/Management.cs index 944e21e..492107b 100644 --- a/IISU/Jobs/Management.cs +++ b/IISU/Jobs/Management.cs @@ -92,10 +92,11 @@ private JobResult PerformRemoval(ManagementJobConfiguration config) try { _logger.MethodEntry(); - var siteName = config.JobProperties["Site Name"]; + var siteName = config.JobProperties["SiteName"]; var port = config.JobProperties["Port"]; - var hostName = config.JobProperties["Host Name"]; + var hostName = config.JobProperties["HostName"]; var protocol = config.JobProperties["Protocol"]; + var ipAddress = config.JobProperties["IPAddress"].ToString(); _logger.LogTrace($"Removing Site: {siteName}, Port:{port}, hostName:{hostName}, protocol:{protocol}"); var storePath = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, @@ -141,6 +142,7 @@ private JobResult PerformRemoval(ManagementJobConfiguration config) .AddParameter("Name", siteName) .AddParameter("Port", port) .AddParameter("HostHeader", hostName) + .AddParameter("IPAddress",ipAddress) .AddStatement(); From 327cfc6cf1f0c04e51a34cbb0ea9eaed7f1e3ae7 Mon Sep 17 00:00:00 2001 From: Bob Pokorny <55611381+rcpokorny@users.noreply.github.com> Date: Fri, 2 Dec 2022 16:15:06 -0600 Subject: [PATCH 11/29] Update IIS-Conversion.sql Removed previously left in steps from initial code base. --- Migration-Scripts/IIS-Conversion.sql | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Migration-Scripts/IIS-Conversion.sql b/Migration-Scripts/IIS-Conversion.sql index 4048861..ab55fb4 100644 --- a/Migration-Scripts/IIS-Conversion.sql +++ b/Migration-Scripts/IIS-Conversion.sql @@ -5,14 +5,6 @@ -- 1) The certificate store types you are attempting to convert TO need to already be properly set up in Keyfactor Command -- 2) Make sure to back up the targeted Keyfactor Command database before running this - --- !!! SPECIAL NOTE - PLEASE READ !!! --- If you are converting from JKS-SSH to the appropriate RemoteFile type, be aware that java keystores of type JKS need to be converted to RFJKS while java keystores --- of type PKCS12 need to be converted to RFPkcs12. The conversion process below will do one or the other. If the client in question has a mix, you either need to: --- 1) Choose the type that covers the most stores, run this script, and then manually convert the the stores with the incorrect RemoteFile type to the correct one, or --- 2) Do not use the script below to convert java keystores and set up each store manually. - - -- *** BEGIN SET UP *** -- *** STEP 1 OF 4: set store type short name of the stores you wish to convert. *** From 9f9d709b762ddd3b2ae736dd494317fc5d40acba Mon Sep 17 00:00:00 2001 From: Bob Pokorny <55611381+rcpokorny@users.noreply.github.com> Date: Fri, 2 Dec 2022 17:31:38 -0600 Subject: [PATCH 12/29] Updated ReadMe documenation Updated the entry parameters grid to provide additional information on required parameters. --- readme_source.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/readme_source.md b/readme_source.md index 7511832..f666886 100644 --- a/readme_source.md +++ b/readme_source.md @@ -83,18 +83,18 @@ This section must be configured with binding fields. The parameters will be popu - 3 - SNI Binding - **Provider Name** - Optional. Name of the Windows cryptographic provider to use when generating and storing the private key for the certificate being enrolled by a reenrollment job. If not specified, defaults to 'Microsoft Strong Cryptographic Provider'. This value would typically be changed when leveraging a Hardware Security Module (HSM). The specified cryptographic provider must be available on the target IIS server being managed. The list of installed cryptographic providers can be obtained by running 'certutil -csplist' in a command shell on the target IIS Server. -- **SAN** - Required. Specifies Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Certificate templates generally require a SAN that matches the subject of the certificate (per RFC 2818). Format is a list of = entries separated by ampersands. Examples: 'dns=www.mysite.com' for a single SAN or 'dns=www.mysite.com&dns=www.mysite2.com' for multiple SANs. +- **SAN** - Optional. Specifies Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Certificate templates generally require a SAN that matches the subject of the certificate (per RFC 2818). Format is a list of = entries separated by ampersands. Examples: 'dns=www.mysite.com' for a single SAN or 'dns=www.mysite.com&dns=www.mysite2.com' for multiple SANs. -Parameter Name|Parameter Type|Default Value|Required +Parameter Name|Parameter Type|Default Value|Required When ---|---|---|--- -Port|String|443|Yes -IP Address|String|*|Yes -Host Name |String||No -Site Name |String|Default Web Site|Yes -Sni Flag |String|0 - No SNI|No -Protocol |Multiple Choice|https|Yes -Provider Name |String||No -SAN |String||Yes +Port|String|443|Adding Entry, Removing Entry, Reenrolling and Entry +IP Address|String|*|Adding Entry, Reenrolling an Entry +Host Name |String|| +Site Name |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry +Sni Flag |String|0 - No SNI| +Protocol |Multiple Choice|https|Adding Entry, Removing Entry, Reenrolling an Entry +Provider Name |String|| +SAN |String||Reenrolling an Entry and the CA follows RFC 2818 specifications ![](images/screen2.png) From c40e3c5244e20a20c455384bded0d82b8a69b1f0 Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Fri, 2 Dec 2022 23:32:16 +0000 Subject: [PATCH 13/29] Update generated README --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 186ba1f..a641bb3 100644 --- a/README.md +++ b/README.md @@ -127,18 +127,18 @@ This section must be configured with binding fields. The parameters will be popu - 3 - SNI Binding - **Provider Name** - Optional. Name of the Windows cryptographic provider to use when generating and storing the private key for the certificate being enrolled by a reenrollment job. If not specified, defaults to 'Microsoft Strong Cryptographic Provider'. This value would typically be changed when leveraging a Hardware Security Module (HSM). The specified cryptographic provider must be available on the target IIS server being managed. The list of installed cryptographic providers can be obtained by running 'certutil -csplist' in a command shell on the target IIS Server. -- **SAN** - Required. Specifies Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Certificate templates generally require a SAN that matches the subject of the certificate (per RFC 2818). Format is a list of = entries separated by ampersands. Examples: 'dns=www.mysite.com' for a single SAN or 'dns=www.mysite.com&dns=www.mysite2.com' for multiple SANs. +- **SAN** - Optional. Specifies Subject Alternative Name (SAN) to be used when performing reenrollment jobs. Certificate templates generally require a SAN that matches the subject of the certificate (per RFC 2818). Format is a list of = entries separated by ampersands. Examples: 'dns=www.mysite.com' for a single SAN or 'dns=www.mysite.com&dns=www.mysite2.com' for multiple SANs. -Parameter Name|Parameter Type|Default Value|Required +Parameter Name|Parameter Type|Default Value|Required When ---|---|---|--- -Port|String|443|Yes -IP Address|String|*|Yes -Host Name |String||No -Site Name |String|Default Web Site|Yes -Sni Flag |String|0 - No SNI|No -Protocol |Multiple Choice|https|Yes -Provider Name |String||No -SAN |String||Yes +Port|String|443|Adding Entry, Removing Entry, Reenrolling and Entry +IP Address|String|*|Adding Entry, Reenrolling an Entry +Host Name |String|| +Site Name |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry +Sni Flag |String|0 - No SNI| +Protocol |Multiple Choice|https|Adding Entry, Removing Entry, Reenrolling an Entry +Provider Name |String|| +SAN |String||Reenrolling an Entry and the CA follows RFC 2818 specifications ![](images/screen2.png) From 6a728d66b09d6260f9d8d008d1ad0d6945633b2a Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Mon, 5 Dec 2022 10:54:53 -0500 Subject: [PATCH 14/29] Fixed Removal Bug and Updated Readme --- readme_source.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/readme_source.md b/readme_source.md index 12b4497..d9a72d0 100644 --- a/readme_source.md +++ b/readme_source.md @@ -84,16 +84,16 @@ This section must be configured with binding fields. The parameters will be popu - **Provider Name** - Optional. To get a list of Crypto Providers, open PowerShell and issue the 'certutil -csplist' command. If no Provider Name is provided, the 'Microsoft Strong Cryptographic Provider' will be used. - **SAN** - Required. The SAN must have one entry that matches the Subject Name when using ReEnrollment. Multiple SANs maybe chained together using '&'. Example: dns=www.mysite.com&dns=www.mysite2.com. -Parameter Name|Parameter Type|Default Value|Required +Parameter Name|Parameter Type|Default Value|Required When ---|---|---|--- -Port|String|443|Yes -IP Address|String|*|Yes +Port|String|443|Adding Entry, Removing Entry, Reenrolling an Entry +IP Address|String|*|Adding Entry, Removing Entry, Reenrolling an Entry Host Name |String||No -Site Name |String|Default Web Site|Yes +Site Name |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry Sni Flag |String|0 - No SNI|No -Protocol |Multiple Choice|https|Yes +Protocol |Multiple Choice|https|Adding Entry, Removing Entry, Reenrolling an Entry Provider Name |String||No -SAN |String||Yes +SAN |String||Reenrolling an Entry ![](images/screen2.png) @@ -128,7 +128,7 @@ Inventory Schedule |The interval that the system will use to report on what cert #### TEST CASES Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot ----|------------------------|------------------------------------|--------------|----------------|------------------------- -1 |New Cert Enrollment To New Binding|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Created with Enrollment Params specified|True|![](images/TestCase1Results.gif) +1 |New Cert Enrollment To New Binding With KFSecret Creds|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Created with Enrollment Params specified creds pulled from KFSecret|True|![](images/TestCase1Results.gif) 2 |New Cert Enrollment To Existing Binding|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|Existing Binding From Case 1 Updated with New Cert|True|![](images/TestCase2Results.gif) 3 |New Cert Enrollment To Existing Binding Enable SNI |**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|Will Update Site In Case 2 to Have Sni Enabled|True|![](images/TestCase3Results.gif) 4 |New Cert Enrollment New IP Address|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsite.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|New Binding Created With New IP and New SNI on Same Port|True|![](images/TestCase4Results.gif) @@ -141,6 +141,7 @@ Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot 11 |Renew Same Cert on Same Site Same Binding Settings Different IPs|`BINDING 1`
**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https
`BINDING 2`
**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.160`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|Cert will be renewed on both bindings because it has the same thrumbprint|True|![](images/TestCase11Binding1.gif)![](images/TestCase11Binding2.gif) 12 |Renew Same Cert on Same Site Same Binding Settings Different Ports|`BINDING 1`
**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https
`BINDING 2`
**Site Name:** FirstSite
**Port:** 543
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|Cert will be renewed on both bindings because it has the same thrumbprint|True|![](images/TestCase12Binding1.gif)![](images/TestCase12Binding2.gif) 13 |ReEnrollment to Fortanix HSM|**Subject Name:** cn=www.mysite.com
**Port:** 433
**IP Address:**`*`
**Host Name:** mysite.command.local
**Site Name:**Default Web Site
**Sni Flag:** 0 - No SNI
**Protocol:** https
**Provider Name:** Fortanix KMS CNG Provider
**SAN:** dns=www.mysite.com&dns=mynewsite.com|Cert will be generated with keys stored in Fortanix HSM and the cert will be bound to the supplied site.|true|![](images/ReEnrollment1a.png)![](images/ReEnrollment1b.png) +14 |New Cert Enrollment To New Binding With Pam Creds|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Created with Enrollment Params specified creds pulled from Pam Provider|True|![](images/TestCase1Results.gif) From 3595bcf181cd706c31f36ded993cfcc5b6d6894f Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Mon, 5 Dec 2022 15:55:40 +0000 Subject: [PATCH 15/29] Update generated README --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b471a4a..5a9d734 100644 --- a/README.md +++ b/README.md @@ -128,16 +128,16 @@ This section must be configured with binding fields. The parameters will be popu - **Provider Name** - Optional. To get a list of Crypto Providers, open PowerShell and issue the 'certutil -csplist' command. If no Provider Name is provided, the 'Microsoft Strong Cryptographic Provider' will be used. - **SAN** - Required. The SAN must have one entry that matches the Subject Name when using ReEnrollment. Multiple SANs maybe chained together using '&'. Example: dns=www.mysite.com&dns=www.mysite2.com. -Parameter Name|Parameter Type|Default Value|Required +Parameter Name|Parameter Type|Default Value|Required When ---|---|---|--- -Port|String|443|Yes -IP Address|String|*|Yes +Port|String|443|Adding Entry, Removing Entry, Reenrolling an Entry +IP Address|String|*|Adding Entry, Removing Entry, Reenrolling an Entry Host Name |String||No -Site Name |String|Default Web Site|Yes +Site Name |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry Sni Flag |String|0 - No SNI|No -Protocol |Multiple Choice|https|Yes +Protocol |Multiple Choice|https|Adding Entry, Removing Entry, Reenrolling an Entry Provider Name |String||No -SAN |String||Yes +SAN |String||Reenrolling an Entry ![](images/screen2.png) @@ -172,7 +172,7 @@ Inventory Schedule |The interval that the system will use to report on what cert #### TEST CASES Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot ----|------------------------|------------------------------------|--------------|----------------|------------------------- -1 |New Cert Enrollment To New Binding|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Created with Enrollment Params specified|True|![](images/TestCase1Results.gif) +1 |New Cert Enrollment To New Binding With KFSecret Creds|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Created with Enrollment Params specified creds pulled from KFSecret|True|![](images/TestCase1Results.gif) 2 |New Cert Enrollment To Existing Binding|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|Existing Binding From Case 1 Updated with New Cert|True|![](images/TestCase2Results.gif) 3 |New Cert Enrollment To Existing Binding Enable SNI |**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|Will Update Site In Case 2 to Have Sni Enabled|True|![](images/TestCase3Results.gif) 4 |New Cert Enrollment New IP Address|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsite.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|New Binding Created With New IP and New SNI on Same Port|True|![](images/TestCase4Results.gif) @@ -185,6 +185,7 @@ Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot 11 |Renew Same Cert on Same Site Same Binding Settings Different IPs|`BINDING 1`
**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https
`BINDING 2`
**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.160`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|Cert will be renewed on both bindings because it has the same thrumbprint|True|![](images/TestCase11Binding1.gif)![](images/TestCase11Binding2.gif) 12 |Renew Same Cert on Same Site Same Binding Settings Different Ports|`BINDING 1`
**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https
`BINDING 2`
**Site Name:** FirstSite
**Port:** 543
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|Cert will be renewed on both bindings because it has the same thrumbprint|True|![](images/TestCase12Binding1.gif)![](images/TestCase12Binding2.gif) 13 |ReEnrollment to Fortanix HSM|**Subject Name:** cn=www.mysite.com
**Port:** 433
**IP Address:**`*`
**Host Name:** mysite.command.local
**Site Name:**Default Web Site
**Sni Flag:** 0 - No SNI
**Protocol:** https
**Provider Name:** Fortanix KMS CNG Provider
**SAN:** dns=www.mysite.com&dns=mynewsite.com|Cert will be generated with keys stored in Fortanix HSM and the cert will be bound to the supplied site.|true|![](images/ReEnrollment1a.png)![](images/ReEnrollment1b.png) +14 |New Cert Enrollment To New Binding With Pam Creds|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Created with Enrollment Params specified creds pulled from Pam Provider|True|![](images/TestCase1Results.gif) From afd7b357384cb2fd0a6631943f6f8a1a25248a1f Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Mon, 5 Dec 2022 13:16:16 -0500 Subject: [PATCH 16/29] Update to migration script --- Migration-Scripts/IIS-Conversion.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Migration-Scripts/IIS-Conversion.sql b/Migration-Scripts/IIS-Conversion.sql index 4048861..10320a1 100644 --- a/Migration-Scripts/IIS-Conversion.sql +++ b/Migration-Scripts/IIS-Conversion.sql @@ -146,6 +146,16 @@ begin transaction from cms_agents.CertStoreTypes a inner join @StoreTypesToConvert b on a.StoreType = b.FromCertStoreTypeId end + + --Update Cert Store Param Name to not have space (will not show on reenrolmment screen with space, KF Bug) + update [cms_agents].[CertStoreTypeEntryParameters] + set [Name]='SiteName' where Name='Site Name' + and [StoreTypeId] in (select StoreType from [cms_agents].[CertStoreTypes] where Name='IISU') + + --Update Cert Store Param Name to not have space (will not show on reenrolmment screen with space, KF Bug) + update [cms_agents].[CertStoreTypeEntryParameters] + set [Name]='HostName' where Name='Host Name' + and [StoreTypeId] in (select StoreType from [cms_agents].[CertStoreTypes] where Name='IISU') select * from cms_agents.CertStoreTypes From 5afbc83fafc007b9119afa12beb3a5072800d633 Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Wed, 7 Dec 2022 10:40:44 -0500 Subject: [PATCH 17/29] Pam Documentation Updates --- integration-manifest.json | 1 + readme-src/readme-pam-support.md | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 readme-src/readme-pam-support.md diff --git a/integration-manifest.json b/integration-manifest.json index fb483e8..1a80118 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -8,6 +8,7 @@ "about": { "orchestrator": { "UOFramework": "10.1", + "pam_support": true, "win": { "supportsCreateStore": false, "supportsDiscovery": false, diff --git a/readme-src/readme-pam-support.md b/readme-src/readme-pam-support.md new file mode 100644 index 0000000..ead4d6b --- /dev/null +++ b/readme-src/readme-pam-support.md @@ -0,0 +1,4 @@ +|Name|Description| +|----|-----------| +|Server Username|The user id that will be used to authenticate into the server hosting the store| +|Server Password|The password that will be used to authenticate into the server hosting the store| \ No newline at end of file From 048f70c406a9524322ff28c72842738bdbbf48a4 Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Wed, 7 Dec 2022 10:42:50 -0500 Subject: [PATCH 18/29] Doc Updates --- readme-src/readme-pam-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme-src/readme-pam-support.md b/readme-src/readme-pam-support.md index ead4d6b..5c80269 100644 --- a/readme-src/readme-pam-support.md +++ b/readme-src/readme-pam-support.md @@ -1,4 +1,4 @@ |Name|Description| |----|-----------| -|Server Username|The user id that will be used to authenticate into the server hosting the store| +|Server UserName|The user id that will be used to authenticate into the server hosting the store| |Server Password|The password that will be used to authenticate into the server hosting the store| \ No newline at end of file From 93b705fcf8fec82790c887b4282702f1bff73112 Mon Sep 17 00:00:00 2001 From: Mikey Henderson Date: Wed, 7 Dec 2022 10:58:14 -0800 Subject: [PATCH 19/29] fix formatting in manifest (#36) --- integration-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-manifest.json b/integration-manifest.json index 1a80118..72177f4 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -8,7 +8,7 @@ "about": { "orchestrator": { "UOFramework": "10.1", - "pam_support": true, + "pam_support": true, "win": { "supportsCreateStore": false, "supportsDiscovery": false, From 757624ae167fb8ee16a98451a14fa44fcc0b418b Mon Sep 17 00:00:00 2001 From: Mikey Henderson Date: Wed, 7 Dec 2022 12:31:44 -0800 Subject: [PATCH 20/29] Merge cleaup-docs (#37) From c806000e3fd91b295d1f26fcdcf5c53385e3f75f Mon Sep 17 00:00:00 2001 From: Mikey Henderson Date: Wed, 7 Dec 2022 12:48:06 -0800 Subject: [PATCH 21/29] replace tab with spaces (#38) * replace tab with spaces * Update generated README Co-authored-by: Michael Henderson --- README.md | 14 ++++++++++++++ integration-manifest.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bf27cb1..a7fb17d 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,20 @@ The Keyfactor Universal Orchestrator may be installed on either Windows or Linux |Supports Inventory|✓ | | +## PAM Integration + +This orchestrator extension has the ability to connect to a variety of supported PAM providers to allow for the retrieval of various client hosted secrets right from the orchestrator server itself. This eliminates the need to set up the PAM integration on Keyfactor Command which may be in an environment that the client does not want to have access to their PAM provider. + +The secrets that this orchestrator extension supports for use with a PAM Provider are: + +|Name|Description| +|----|-----------| +|Server UserName|The user id that will be used to authenticate into the server hosting the store| +|Server Password|The password that will be used to authenticate into the server hosting the store| +It is not necessary to implement all of the secrets available to be managed by a PAM provider. For each value that you want managed by a PAM provider, simply enter the key value inside your specific PAM provider that will hold this value into the corresponding field when setting up the certificate store, discovery job, or API call. + +Setting up a PAM provider for use involves adding an additional section to the manifest.json file for this extension as well as setting up the PAM provider you will be using. Each of these steps is specific to the PAM provider you will use and are documented in the specific GitHub repo for that provider. For a list of Keyfactor supported PAM providers, please reference the [Keyfactor Integration Catalog](https://keyfactor.github.io/integrations-catalog/content/pam). + --- diff --git a/integration-manifest.json b/integration-manifest.json index 72177f4..2cf1679 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -8,7 +8,7 @@ "about": { "orchestrator": { "UOFramework": "10.1", - "pam_support": true, + "pam_support": true, "win": { "supportsCreateStore": false, "supportsDiscovery": false, From eaa0b286d7d2e8432932741186266acc8423b01e Mon Sep 17 00:00:00 2001 From: Brian Hill <76450501+bhillkeyfactor@users.noreply.github.com> Date: Wed, 7 Dec 2022 16:22:33 -0500 Subject: [PATCH 22/29] Update readme-pam-support.md --- readme-src/readme-pam-support.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme-src/readme-pam-support.md b/readme-src/readme-pam-support.md index 5c80269..c4730c0 100644 --- a/readme-src/readme-pam-support.md +++ b/readme-src/readme-pam-support.md @@ -1,4 +1,5 @@ |Name|Description| |----|-----------| |Server UserName|The user id that will be used to authenticate into the server hosting the store| -|Server Password|The password that will be used to authenticate into the server hosting the store| \ No newline at end of file +|Server Password|The password that will be used to authenticate into the server hosting the store| + From 8e139e017b65b981b07ccd9ee5f35132488fb852 Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Wed, 7 Dec 2022 21:23:10 +0000 Subject: [PATCH 23/29] Update generated README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a7fb17d..1d1eec0 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ The secrets that this orchestrator extension supports for use with a PAM Provide |----|-----------| |Server UserName|The user id that will be used to authenticate into the server hosting the store| |Server Password|The password that will be used to authenticate into the server hosting the store| + It is not necessary to implement all of the secrets available to be managed by a PAM provider. For each value that you want managed by a PAM provider, simply enter the key value inside your specific PAM provider that will hold this value into the corresponding field when setting up the certificate store, discovery job, or API call. Setting up a PAM provider for use involves adding an additional section to the manifest.json file for this extension as well as setting up the PAM provider you will be using. Each of these steps is specific to the PAM provider you will use and are documented in the specific GitHub repo for that provider. For a list of Keyfactor supported PAM providers, please reference the [Keyfactor Integration Catalog](https://keyfactor.github.io/integrations-catalog/content/pam). From b08fc363315128bc3f1ed23b14cf632c4be022e8 Mon Sep 17 00:00:00 2001 From: Brian Hill <76450501+bhillkeyfactor@users.noreply.github.com> Date: Thu, 8 Dec 2022 23:40:39 -0500 Subject: [PATCH 24/29] Null host name (#39) * null host name fix * Update generated README * null logger issue * New Test case added * Update generated README Co-authored-by: unknown Co-authored-by: Keyfactor --- IISU/IISManager.cs | 2 +- IISU/Jobs/ReEnrollment.cs | 1 - README.md | 2 ++ images/TestCase15Results.gif | Bin 0 -> 35417 bytes readme_source.md | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 images/TestCase15Results.gif diff --git a/IISU/IISManager.cs b/IISU/IISManager.cs index b5926e3..39de8a8 100644 --- a/IISU/IISManager.cs +++ b/IISU/IISManager.cs @@ -103,7 +103,7 @@ public IISManager(ManagementJobConfiguration config, string serverUserName, stri { SiteName = config.JobProperties["SiteName"].ToString(); Port = config.JobProperties["Port"].ToString(); - HostName = config.JobProperties["HostName"].ToString(); + HostName = config.JobProperties["HostName"]?.ToString(); Protocol = config.JobProperties["Protocol"].ToString(); SniFlag = config.JobProperties["SniFlag"].ToString()?.Substring(0, 1); IpAddress = config.JobProperties["IPAddress"].ToString(); diff --git a/IISU/Jobs/ReEnrollment.cs b/IISU/Jobs/ReEnrollment.cs index e4750cc..5c2c337 100644 --- a/IISU/Jobs/ReEnrollment.cs +++ b/IISU/Jobs/ReEnrollment.cs @@ -50,7 +50,6 @@ private string ResolvePamField(string name, string value) public JobResult ProcessJob(ReenrollmentJobConfiguration config, SubmitReenrollmentCSR submitReEnrollmentUpdate) { - _logger.MethodEntry(); _logger = LogHandler.GetClassLogger(); _logger.LogTrace($"Job Configuration: {JsonConvert.SerializeObject(config)}"); var storePath = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate }); diff --git a/README.md b/README.md index 1d1eec0..3794fd3 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ The secrets that this orchestrator extension supports for use with a PAM Provide |Server UserName|The user id that will be used to authenticate into the server hosting the store| |Server Password|The password that will be used to authenticate into the server hosting the store| + It is not necessary to implement all of the secrets available to be managed by a PAM provider. For each value that you want managed by a PAM provider, simply enter the key value inside your specific PAM provider that will hold this value into the corresponding field when setting up the certificate store, discovery job, or API call. Setting up a PAM provider for use involves adding an additional section to the manifest.json file for this extension as well as setting up the PAM provider you will be using. Each of these steps is specific to the PAM provider you will use and are documented in the specific GitHub repo for that provider. For a list of Keyfactor supported PAM providers, please reference the [Keyfactor Integration Catalog](https://keyfactor.github.io/integrations-catalog/content/pam). @@ -201,6 +202,7 @@ Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot 12 |Renew Same Cert on Same Site Same Binding Settings Different Ports|`BINDING 1`
**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https
`BINDING 2`
**Site Name:** FirstSite
**Port:** 543
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|Cert will be renewed on both bindings because it has the same thrumbprint|True|![](images/TestCase12Binding1.gif)![](images/TestCase12Binding2.gif) 13 |ReEnrollment to Fortanix HSM|**Subject Name:** cn=www.mysite.com
**Port:** 433
**IP Address:**`*`
**Host Name:** mysite.command.local
**Site Name:**Default Web Site
**Sni Flag:** 0 - No SNI
**Protocol:** https
**Provider Name:** Fortanix KMS CNG Provider
**SAN:** dns=www.mysite.com&dns=mynewsite.com|Cert will be generated with keys stored in Fortanix HSM and the cert will be bound to the supplied site.|true|![](images/ReEnrollment1a.png)![](images/ReEnrollment1b.png) 14 |New Cert Enrollment To New Binding With Pam Creds|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Created with Enrollment Params specified creds pulled from Pam Provider|True|![](images/TestCase1Results.gif) +15 |New Cert Enrollment Default Site No HostName|**Site Name:** Default Web Site
**Port:** 443
**IP Address:**`*`
**Host Name:**
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Installed with no HostName|True|![](images/TestCase15Results.gif) diff --git a/images/TestCase15Results.gif b/images/TestCase15Results.gif new file mode 100644 index 0000000000000000000000000000000000000000..543a8d87bbad228bca2115fcce06385f42e1f332 GIT binary patch literal 35417 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

_t81?vYz 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 Wv>k5oiz77R8aMC6p-t(40027~g4VzQ literal 0 HcmV?d00001 diff --git a/readme_source.md b/readme_source.md index 504b748..989c209 100644 --- a/readme_source.md +++ b/readme_source.md @@ -142,6 +142,7 @@ Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot 12 |Renew Same Cert on Same Site Same Binding Settings Different Ports|`BINDING 1`
**Site Name:** FirstSite
**Port:** 443
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https
`BINDING 2`
**Site Name:** FirstSite
**Port:** 543
**IP Address:**`192.168.58.162`
**Host Name:** www.firstsitebinding1.com
**Sni Flag:** 1 - SNI Enabled
**Protocol:** https|Cert will be renewed on both bindings because it has the same thrumbprint|True|![](images/TestCase12Binding1.gif)![](images/TestCase12Binding2.gif) 13 |ReEnrollment to Fortanix HSM|**Subject Name:** cn=www.mysite.com
**Port:** 433
**IP Address:**`*`
**Host Name:** mysite.command.local
**Site Name:**Default Web Site
**Sni Flag:** 0 - No SNI
**Protocol:** https
**Provider Name:** Fortanix KMS CNG Provider
**SAN:** dns=www.mysite.com&dns=mynewsite.com|Cert will be generated with keys stored in Fortanix HSM and the cert will be bound to the supplied site.|true|![](images/ReEnrollment1a.png)![](images/ReEnrollment1b.png) 14 |New Cert Enrollment To New Binding With Pam Creds|**Site Name:** FirstSite
**Port:** 443
**IP Address:**`*`
**Host Name:** www.firstsite.com
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Created with Enrollment Params specified creds pulled from Pam Provider|True|![](images/TestCase1Results.gif) +15 |New Cert Enrollment Default Site No HostName|**Site Name:** Default Web Site
**Port:** 443
**IP Address:**`*`
**Host Name:**
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Installed with no HostName|True|![](images/TestCase15Results.gif) From c85b3d1a0c7813c66ef15e78d8b7334eaa78ef33 Mon Sep 17 00:00:00 2001 From: Bob Pokorny <55611381+rcpokorny@users.noreply.github.com> Date: Tue, 13 Dec 2022 22:38:29 +0000 Subject: [PATCH 25/29] Fixed Null entry errors when Hostname and SAN is empty. (#40) Co-authored-by: Bob Pokorny --- IISU/IISManager.cs | 2 +- IISU/Jobs/ReEnrollment.cs | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/IISU/IISManager.cs b/IISU/IISManager.cs index 39de8a8..9d56989 100644 --- a/IISU/IISManager.cs +++ b/IISU/IISManager.cs @@ -64,7 +64,7 @@ public IISManager(ReenrollmentJobConfiguration config,string serverUserName,stri { SiteName = config.JobProperties["SiteName"].ToString(); Port = config.JobProperties["Port"].ToString(); - HostName = config.JobProperties["HostName"].ToString(); + HostName = config.JobProperties["HostName"]?.ToString(); Protocol = config.JobProperties["Protocol"].ToString(); SniFlag = config.JobProperties["SniFlag"].ToString()?.Substring(0, 1); IpAddress = config.JobProperties["IPAddress"].ToString(); diff --git a/IISU/Jobs/ReEnrollment.cs b/IISU/Jobs/ReEnrollment.cs index 5c2c337..edb7036 100644 --- a/IISU/Jobs/ReEnrollment.cs +++ b/IISU/Jobs/ReEnrollment.cs @@ -118,12 +118,15 @@ private JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submi ps.AddScript($"Add-Content $infFilename 'KeyLength={keySize}'"); ps.AddScript($"Add-Content $infFilename 'KeySpec = 0'"); - ps.AddScript($"Add-Content $infFilename '[Extensions]'"); - ps.AddScript(@"Add-Content $infFilename '2.5.29.17 = ""{text}""'"); - - foreach (string s in SAN.ToString().Split("&")) + if(SAN != null) { - ps.AddScript($"Add-Content $infFilename '_continue_ = \"{s + "&"}\"'"); + ps.AddScript($"Add-Content $infFilename '[Extensions]'"); + ps.AddScript(@"Add-Content $infFilename '2.5.29.17 = ""{text}""'"); + + foreach (string s in SAN.ToString().Split("&")) + { + ps.AddScript($"Add-Content $infFilename '_continue_ = \"{s + "&"}\"'"); + } } // Execute the -new command From 406c84d2abd3a7d463c406f0dc07d2ea64f110ff Mon Sep 17 00:00:00 2001 From: Adam Joyner <36858035+joynerar@users.noreply.github.com> Date: Wed, 14 Dec 2022 18:03:21 -0500 Subject: [PATCH 26/29] v2.0 Readme Updates (#41) - Added reenrollment to store type configuration table - Removed spaces from entry parameters to match new orchestrator requirements - Changed wording on SAN enrollment parameter requirement --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3794fd3..2c72dcd 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Store Path Type |Determines what restrictions are applied to the store path fiel Store Path Value|A comma separated list of options to select from for the Store Path. This, combined with the hostname, will determine the location used for the certificate store management and inventory. Must be My, WebHosting Private Keys |This determines if Keyfactor can send the private key associated with a certificate to the store. This is required since IIS will need the private key material to establish TLS connections. PFX Password Style |This determines how the platform generate passwords to protect a PFX enrollment job that is delivered to the store. This can be either Default (system generated) or Custom (user determined). -Job Types |Inventory, Add, and Remove are the supported job types. +Job Types |Inventory, Add, Remove, and Reenrollment are the supported job types. ![](images/certstoretype.png) @@ -147,13 +147,13 @@ This section must be configured with binding fields. The parameters will be popu Parameter Name|Parameter Type|Default Value|Required When ---|---|---|--- Port|String|443|Adding Entry, Removing Entry, Reenrolling and Entry -IP Address|String|*|Adding Entry, Reenrolling an Entry -Host Name |String|| -Site Name |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry -Sni Flag |String|0 - No SNI| +IPAddress|String|*|Adding Entry, Reenrolling an Entry +HostName |String|| +SiteName |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry +SniFlag |String|0 - No SNI| Protocol |Multiple Choice|https|Adding Entry, Removing Entry, Reenrolling an Entry -Provider Name |String|| -SAN |String||Reenrolling an Entry and the CA follows RFC 2818 specifications +ProviderName |String|| +SAN |String||Reenrolling an Entry (if the CA follows RFC 2818 specifications) ![](images/screen2.png) From a6a8a4029cdd5417630d1149dbd64c31469398f9 Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Wed, 14 Dec 2022 23:03:55 +0000 Subject: [PATCH 27/29] Update generated README --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2c72dcd..3794fd3 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Store Path Type |Determines what restrictions are applied to the store path fiel Store Path Value|A comma separated list of options to select from for the Store Path. This, combined with the hostname, will determine the location used for the certificate store management and inventory. Must be My, WebHosting Private Keys |This determines if Keyfactor can send the private key associated with a certificate to the store. This is required since IIS will need the private key material to establish TLS connections. PFX Password Style |This determines how the platform generate passwords to protect a PFX enrollment job that is delivered to the store. This can be either Default (system generated) or Custom (user determined). -Job Types |Inventory, Add, Remove, and Reenrollment are the supported job types. +Job Types |Inventory, Add, and Remove are the supported job types. ![](images/certstoretype.png) @@ -147,13 +147,13 @@ This section must be configured with binding fields. The parameters will be popu Parameter Name|Parameter Type|Default Value|Required When ---|---|---|--- Port|String|443|Adding Entry, Removing Entry, Reenrolling and Entry -IPAddress|String|*|Adding Entry, Reenrolling an Entry -HostName |String|| -SiteName |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry -SniFlag |String|0 - No SNI| +IP Address|String|*|Adding Entry, Reenrolling an Entry +Host Name |String|| +Site Name |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry +Sni Flag |String|0 - No SNI| Protocol |Multiple Choice|https|Adding Entry, Removing Entry, Reenrolling an Entry -ProviderName |String|| -SAN |String||Reenrolling an Entry (if the CA follows RFC 2818 specifications) +Provider Name |String|| +SAN |String||Reenrolling an Entry and the CA follows RFC 2818 specifications ![](images/screen2.png) From 3ec8a057e31efc9465b5fe25a21f10b36d399b3a Mon Sep 17 00:00:00 2001 From: Adam Joyner <36858035+joynerar@users.noreply.github.com> Date: Wed, 14 Dec 2022 18:50:09 -0500 Subject: [PATCH 28/29] Update v2.0 readme source (#42) --- readme_source.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/readme_source.md b/readme_source.md index 989c209..41b421a 100644 --- a/readme_source.md +++ b/readme_source.md @@ -40,7 +40,7 @@ Store Path Type |Determines what restrictions are applied to the store path fiel Store Path Value|A comma separated list of options to select from for the Store Path. This, combined with the hostname, will determine the location used for the certificate store management and inventory. Must be My, WebHosting Private Keys |This determines if Keyfactor can send the private key associated with a certificate to the store. This is required since IIS will need the private key material to establish TLS connections. PFX Password Style |This determines how the platform generate passwords to protect a PFX enrollment job that is delivered to the store. This can be either Default (system generated) or Custom (user determined). -Job Types |Inventory, Add, and Remove are the supported job types. +Job Types |Inventory, Add, Remove, and Reenrollment are the supported job types. ![](images/certstoretype.png) @@ -87,13 +87,13 @@ This section must be configured with binding fields. The parameters will be popu Parameter Name|Parameter Type|Default Value|Required When ---|---|---|--- Port|String|443|Adding Entry, Removing Entry, Reenrolling and Entry -IP Address|String|*|Adding Entry, Reenrolling an Entry -Host Name |String|| -Site Name |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry -Sni Flag |String|0 - No SNI| +IPAddress|String|*|Adding Entry, Reenrolling an Entry +HostName |String|| +SiteName |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry +SniFlag |String|0 - No SNI| Protocol |Multiple Choice|https|Adding Entry, Removing Entry, Reenrolling an Entry -Provider Name |String|| -SAN |String||Reenrolling an Entry and the CA follows RFC 2818 specifications +ProviderName |String|| +SAN |String||Reenrolling an Entry (if the CA follows RFC 2818 specifications) ![](images/screen2.png) @@ -148,4 +148,3 @@ Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot - From 15385dd248a9c0f493984e50c9b2c29c2fb61755 Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Wed, 14 Dec 2022 23:50:55 +0000 Subject: [PATCH 29/29] Update generated README --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3794fd3..8227e7e 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Store Path Type |Determines what restrictions are applied to the store path fiel Store Path Value|A comma separated list of options to select from for the Store Path. This, combined with the hostname, will determine the location used for the certificate store management and inventory. Must be My, WebHosting Private Keys |This determines if Keyfactor can send the private key associated with a certificate to the store. This is required since IIS will need the private key material to establish TLS connections. PFX Password Style |This determines how the platform generate passwords to protect a PFX enrollment job that is delivered to the store. This can be either Default (system generated) or Custom (user determined). -Job Types |Inventory, Add, and Remove are the supported job types. +Job Types |Inventory, Add, Remove, and Reenrollment are the supported job types. ![](images/certstoretype.png) @@ -147,13 +147,13 @@ This section must be configured with binding fields. The parameters will be popu Parameter Name|Parameter Type|Default Value|Required When ---|---|---|--- Port|String|443|Adding Entry, Removing Entry, Reenrolling and Entry -IP Address|String|*|Adding Entry, Reenrolling an Entry -Host Name |String|| -Site Name |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry -Sni Flag |String|0 - No SNI| +IPAddress|String|*|Adding Entry, Reenrolling an Entry +HostName |String|| +SiteName |String|Default Web Site|Adding Entry, Removing Entry, Reenrolling an Entry +SniFlag |String|0 - No SNI| Protocol |Multiple Choice|https|Adding Entry, Removing Entry, Reenrolling an Entry -Provider Name |String|| -SAN |String||Reenrolling an Entry and the CA follows RFC 2818 specifications +ProviderName |String|| +SAN |String||Reenrolling an Entry (if the CA follows RFC 2818 specifications) ![](images/screen2.png) @@ -209,4 +209,3 @@ Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot -