From d9209578856152913721ae476c804d3f28f6d5ef Mon Sep 17 00:00:00 2001 From: Mikey Henderson Date: Wed, 8 Nov 2023 09:33:40 -0800 Subject: [PATCH 1/5] Add new field (#40) (#41) * Add new custom field Co-authored-by: Lee Fine <50836957+leefine02@users.noreply.github.com> --- CHANGELOG.md | 3 ++- README.md | 6 +++++- RemoteFile/Discovery.cs | 2 +- RemoteFile/ICertificateStoreSerializer.cs | 2 +- .../DER/DERCertificateStoreSerializer.cs | 2 +- .../JKS/JKSCertificateStoreSerializer.cs | 2 +- .../KDB/KDBCertificateStoreSerializer.cs | 2 +- .../OraWlt/OraWltCertificateStoreSerializer.cs | 4 ++-- .../PEM/PEMCertificateStoreSerializer.cs | 6 ++++-- .../PKCS12/PKCS12CertificateStoreSerializer.cs | 2 +- RemoteFile/InventoryBase.cs | 2 +- RemoteFile/ManagementBase.cs | 4 ++-- RemoteFile/RemoteCertificateStore.cs | 4 ++-- integration-manifest.json | 8 ++++++++ readme_source.md | 6 +++++- 15 files changed, 37 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23bf01ee..da7a1e8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ v2.4.0 -- Do not require store password for PEM inventory +- Add new optional custom parameter, IgnorePrivateKeyOnInventory, for RFPEM, which will allow inventorying RFPEM certificate stores where the store password is unknown. This will make the store INVENTORY ONLY. Once the store password is added, this option can be de-selected (set to False), inventory can be run again, and then renewing/removing the certificate will be allowed. +- Bug fix: Discovery "Directories to Ignore" field not being used to filter results v2.3.1 - Bug fix: Discovery - ignore /proc folder for Linux servers diff --git a/README.md b/README.md index 1a722452..a1f0dc49 100644 --- a/README.md +++ b/README.md @@ -292,6 +292,7 @@ Use cases supported: - **Name:** IncludesChain, **Display Name:** Store Includes Chain, **Type:** Bool, **Default Value:** false. This custom field is **not required**. Default value if not present is 'false'. If 'true' the full certificate chain, if sent by Keyfactor Command, will be stored in the file. The order of appearance is always assumed to be 1) end entity certificate, 2) issuing CA certificate, and 3) root certificate. If additional CA tiers are applicable, the order will be end entity certificate up to the root CA certificate. if set to 'false', only the end entity certificate and private key will be stored in this store. This setting is only valid when IsTrustStore = false. - **Name:** SeparatePrivateKeyFilePath, **Display Name:** Separate Private Key File Location, **Type:** String, **Default Value:** empty. This custom field is **not required**. If empty, or not provided, it will be assumed that the private key for the certificate stored in this file will be inside the same file as the certificate. If the full path AND file name is put here, that location will be used to store the private key as an external file. This setting is only valid when IsTrustStore = false. - **Name:** IsRSAPrivateKey, **Display Name:** Is RSA Private Key, **Type:** Bool, **Default Value:** false. This custom field is **not required**. Default value if not present is 'false'. If 'true' it will be assumed that the private key for the certificate is a PKCS#1 RSA formatted private key (BEGIN RSA PRIVATE KEY). If 'false' (default), encrypted/non-encrypted PKCS#8 (BEGIN [ENCRYPTED] PRIVATE KEY) is assumed If set to 'true' the store password **must** be set to "no password", as PKCS#1 does not support encrypted keys. This setting is only valid when IsTrustStore = false. +- **Name:** IgnorePrivateKeyOnInventory, **Display Name:** Ignore Private Key On Inventory, **Type:** Bool, **Default Value:** false. This custom field is **not required**. Default value if not present is 'false'. If 'true', inventory for this certificate store will be performed without accessing the certificate's private key or the store password. This will functionally make the store INVENTORY ONLY, as all certificates will be returned with "Private Key Entry" = false. Also, no certificate chain relationships will be maintained, and all certificates will be considered separate entries (basically a trust store). This may be useful in situations where the client does not know the store password at inventory run time, but would still like the certificates to be imported into Keyfactor Command. Once the correct store password is entered for the store, the client may de-select this option (change the value to False), schedule an inventory job, and then the appropriate private key entry and chain information should be properly stored in the Keyfactor Command location, allowing for renewal/removal of the certificate at a later time. Entry Parameters Tab: - no additional entry parameters @@ -401,7 +402,10 @@ Steps to create a new supported file based certificate store type: 1. Clone this repository from GitHub 2. Open the .net core solution in the IDE of your choice 3. Under the ImplementationStoreTypes folder, create a new folder named for the new certificate store type -4. Create a new class (with namespace of Keyfactor.Extensions.Orchestrator.RemoteFile.{NewType}) in the new folder that will implement ICertificateStoreSerializer. By convention, {StoreTypeName}CertificateSerializer would be a good choice for the class name. This interface requires you to implement two methods: DesrializeRemoteCertificateStore and SerializeRemoteCertificateStore. The first method will be called passing in a byte array containing the contents of file based store you are managing. The developer will need to convert that to an Org.BouncyCastle.Pkcs.Pkcs12Store class and return it. The second method takes in an Org.BouncyCastle.Pkcs.Pkcs12Store and converts it to a collection of custom file representations, List. This is where the majority of the development will be done. +4. Create a new class (with namespace of Keyfactor.Extensions.Orchestrator.RemoteFile.{NewType}) in the new folder that will implement ICertificateStoreSerializer. By convention, {StoreTypeName}CertificateSerializer would be a good choice for the class name. This interface requires you to implement three methods: + - DesrializeRemoteCertificateStore - This method takes in a byte array containing the contents of file based store you are managing. The developer will need to convert that to an Org.BouncyCastle.Pkcs.Pkcs12Store class and return it. + - SerializeRemoteCertificateStore - This method takes in an Org.BouncyCastle.Pkcs.Pkcs12Store and converts it to a collection of custom file representations. + - GetPrivateKeyPath - This method returns the location of the external private key file for single certificate stores. Currently this is only used for RFPEM, and all other implementations return NULL for this method. If this is not applicable to your implementation just return a NULL value for this method. 5. Create an Inventory.cs class (with namespace of Keyfactor.Extensions.Orchestrator.RemoteFile.{NewType}) under the new folder and have it inherit InventoryBase. Override the internal GetCertificateStoreSerializer() method with a one line implementation returning a new instantiation of the class created in step 4. 6. Create a Management.cs class (with namespace of Keyfactor.Extensions.Orchestrator.RemoteFile.{NewType}) under the new folder and have it inherit ManagementBase. Override the internal GetCertificateStoreSerializer() method with a one line implementation returning a new instantiation of the class created in step 4. 7. Modify the manifest.json file to add three new sections (for Inventory, Management, and Discovery). Make sure for each, the "NewType" in Certstores.{NewType}.{Operation}, matches what you will use for the certificate store type short name in Keyfactor Command. On the "TypeFullName" line for all three sections, make sure the namespace matches what you used for your new classes. Note that the namespace for Discovery uses a common class for all supported types. Discovery is a common implementation for all supported store types. diff --git a/RemoteFile/Discovery.cs b/RemoteFile/Discovery.cs index 02ee3156..69c33ac8 100644 --- a/RemoteFile/Discovery.cs +++ b/RemoteFile/Discovery.cs @@ -69,7 +69,7 @@ public JobResult ProcessJob(DiscoveryJobConfiguration config, SubmitDiscoveryUpd locations = certificateStore.FindStores(directoriesToSearch, extensionsToSearch, filesTosearch, includeSymLinks); foreach (string ignoredDir in ignoredDirs) { - locations = locations.Where(p => !p.StartsWith(ignoredDir) || !p.ToLower().StartsWith("find:")).ToList(); + locations = locations.Where(p => !p.StartsWith(ignoredDir) && !p.ToLower().StartsWith("find:")).ToList(); } } catch (Exception ex) diff --git a/RemoteFile/ICertificateStoreSerializer.cs b/RemoteFile/ICertificateStoreSerializer.cs index 6b7a1bce..3bd3291d 100644 --- a/RemoteFile/ICertificateStoreSerializer.cs +++ b/RemoteFile/ICertificateStoreSerializer.cs @@ -14,7 +14,7 @@ namespace Keyfactor.Extensions.Orchestrator.RemoteFile { interface ICertificateStoreSerializer { - Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContents, string storePath, string storePassword, IRemoteHandler remoteHandler, bool includePrivateKey); + Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContents, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory); List SerializeRemoteCertificateStore(Pkcs12Store certificateStore, string storePath, string storeFileName, string storePassword, IRemoteHandler remoteHandler); diff --git a/RemoteFile/ImplementedStoreTypes/DER/DERCertificateStoreSerializer.cs b/RemoteFile/ImplementedStoreTypes/DER/DERCertificateStoreSerializer.cs index f52385a6..ef70a6e2 100644 --- a/RemoteFile/ImplementedStoreTypes/DER/DERCertificateStoreSerializer.cs +++ b/RemoteFile/ImplementedStoreTypes/DER/DERCertificateStoreSerializer.cs @@ -38,7 +38,7 @@ public DERCertificateStoreSerializer(string storeProperties) LoadCustomProperties(storeProperties); } - public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool includePrivateKey) + public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory) { logger.MethodEntry(LogLevel.Debug); diff --git a/RemoteFile/ImplementedStoreTypes/JKS/JKSCertificateStoreSerializer.cs b/RemoteFile/ImplementedStoreTypes/JKS/JKSCertificateStoreSerializer.cs index 8ba0619d..c285ea9a 100644 --- a/RemoteFile/ImplementedStoreTypes/JKS/JKSCertificateStoreSerializer.cs +++ b/RemoteFile/ImplementedStoreTypes/JKS/JKSCertificateStoreSerializer.cs @@ -30,7 +30,7 @@ public JKSCertificateStoreSerializer(string storeProperties) logger = LogHandler.GetClassLogger(this.GetType()); } - public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContents, string storePath, string storePassword, IRemoteHandler remoteHandler, bool includePrivateKey) + public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContents, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory) { logger.MethodEntry(LogLevel.Debug); diff --git a/RemoteFile/ImplementedStoreTypes/KDB/KDBCertificateStoreSerializer.cs b/RemoteFile/ImplementedStoreTypes/KDB/KDBCertificateStoreSerializer.cs index bc64702b..2db1dd03 100644 --- a/RemoteFile/ImplementedStoreTypes/KDB/KDBCertificateStoreSerializer.cs +++ b/RemoteFile/ImplementedStoreTypes/KDB/KDBCertificateStoreSerializer.cs @@ -28,7 +28,7 @@ public KDBCertificateStoreSerializer(string storeProperties) logger = LogHandler.GetClassLogger(this.GetType()); } - public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool includePrivateKey) + public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory) { logger.MethodEntry(LogLevel.Debug); diff --git a/RemoteFile/ImplementedStoreTypes/OraWlt/OraWltCertificateStoreSerializer.cs b/RemoteFile/ImplementedStoreTypes/OraWlt/OraWltCertificateStoreSerializer.cs index f0bbf08d..c460b67c 100644 --- a/RemoteFile/ImplementedStoreTypes/OraWlt/OraWltCertificateStoreSerializer.cs +++ b/RemoteFile/ImplementedStoreTypes/OraWlt/OraWltCertificateStoreSerializer.cs @@ -34,7 +34,7 @@ public OraWltCertificateStoreSerializer(string storeProperties) LoadCustomProperties(storeProperties); } - public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool includePrivateKey) + public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory) { logger.MethodEntry(LogLevel.Debug); @@ -57,7 +57,7 @@ public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, s jksStore.Load(new MemoryStream(storeBytes), string.IsNullOrEmpty(storePassword) ? new char[0] : storePassword.ToCharArray()); JKSCertificateStoreSerializer serializer = new JKSCertificateStoreSerializer(String.Empty); - store = serializer.DeserializeRemoteCertificateStore(storeBytes, $"{WorkFolder}{tempStoreFileJKS}", storePassword, remoteHandler, includePrivateKey); + store = serializer.DeserializeRemoteCertificateStore(storeBytes, $"{WorkFolder}{tempStoreFileJKS}", storePassword, remoteHandler, isInventory); } catch (Exception ex) { diff --git a/RemoteFile/ImplementedStoreTypes/PEM/PEMCertificateStoreSerializer.cs b/RemoteFile/ImplementedStoreTypes/PEM/PEMCertificateStoreSerializer.cs index a317fed7..7515cf40 100644 --- a/RemoteFile/ImplementedStoreTypes/PEM/PEMCertificateStoreSerializer.cs +++ b/RemoteFile/ImplementedStoreTypes/PEM/PEMCertificateStoreSerializer.cs @@ -40,6 +40,7 @@ class PEMCertificateStoreSerializer : ICertificateStoreSerializer private bool IncludesChain { get; set; } private string SeparatePrivateKeyFilePath { get; set; } private bool IsRSAPrivateKey { get; set; } + private bool IgnorePrivateKeyOnInventory { get; set; } private ILogger logger; @@ -49,7 +50,7 @@ public PEMCertificateStoreSerializer(string storeProperties) LoadCustomProperties(storeProperties); } - public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool includePrivateKey) + public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory) { logger.MethodEntry(LogLevel.Debug); @@ -62,7 +63,7 @@ public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, s string storeContents = Encoding.ASCII.GetString(storeContentBytes); X509CertificateEntry[] certificates = GetCertificates(storeContents); - if (IsTrustStore || !includePrivateKey) + if (IsTrustStore || (isInventory && IgnorePrivateKeyOnInventory)) { foreach(X509CertificateEntry certificate in certificates) { @@ -185,6 +186,7 @@ private void LoadCustomProperties(string storeProperties) IncludesChain = properties.IncludesChain == null || string.IsNullOrEmpty(properties.IncludesChain.Value) ? false : bool.Parse(properties.IncludesChain.Value); SeparatePrivateKeyFilePath = properties.SeparatePrivateKeyFilePath == null || string.IsNullOrEmpty(properties.SeparatePrivateKeyFilePath.Value) ? String.Empty : properties.SeparatePrivateKeyFilePath.Value; IsRSAPrivateKey = properties.IsRSAPrivateKey == null || string.IsNullOrEmpty(properties.IsRSAPrivateKey.Value) ? false : bool.Parse(properties.IsRSAPrivateKey.Value); + IgnorePrivateKeyOnInventory = properties.IgnorePrivateKeyOnInventory == null || string.IsNullOrEmpty(properties.IgnorePrivateKeyOnInventory.Value) ? false : bool.Parse(properties.IgnorePrivateKeyOnInventory.Value); logger.MethodExit(LogLevel.Debug); } diff --git a/RemoteFile/ImplementedStoreTypes/PKCS12/PKCS12CertificateStoreSerializer.cs b/RemoteFile/ImplementedStoreTypes/PKCS12/PKCS12CertificateStoreSerializer.cs index 562de30f..f78f8114 100644 --- a/RemoteFile/ImplementedStoreTypes/PKCS12/PKCS12CertificateStoreSerializer.cs +++ b/RemoteFile/ImplementedStoreTypes/PKCS12/PKCS12CertificateStoreSerializer.cs @@ -25,7 +25,7 @@ public PKCS12CertificateStoreSerializer(string storeProperties) logger = LogHandler.GetClassLogger(this.GetType()); } - public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContents, string storePath, string storePassword, IRemoteHandler remoteHandler, bool includePrivateKey) + public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContents, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory) { Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder(); Pkcs12Store store = storeBuilder.Build(); diff --git a/RemoteFile/InventoryBase.cs b/RemoteFile/InventoryBase.cs index f23e6da9..8d0fe637 100644 --- a/RemoteFile/InventoryBase.cs +++ b/RemoteFile/InventoryBase.cs @@ -48,7 +48,7 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd ApplicationSettings.Initialize(this.GetType().Assembly.Location); certificateStore = new RemoteCertificateStore(config.CertificateStoreDetails.ClientMachine, userName, userPassword, config.CertificateStoreDetails.StorePath, storePassword, config.JobProperties); certificateStore.Initialize(); - certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, false); + certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, true); List collections = certificateStore.GetCertificateChains(); diff --git a/RemoteFile/ManagementBase.cs b/RemoteFile/ManagementBase.cs index 4b172d1b..a6f9f438 100644 --- a/RemoteFile/ManagementBase.cs +++ b/RemoteFile/ManagementBase.cs @@ -67,7 +67,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config) else throw new RemoteFileException($"Certificate store {config.CertificateStoreDetails.StorePath} does not exist on server {config.CertificateStoreDetails.ClientMachine}."); } - certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, true); + certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, false); certificateStore.AddCertificate((config.JobCertificate.Alias ?? new X509Certificate2(Convert.FromBase64String(config.JobCertificate.Contents), config.JobCertificate.PrivateKeyPassword).Thumbprint), config.JobCertificate.Contents, config.Overwrite, config.JobCertificate.PrivateKeyPassword); certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(), storePathFile.Path, storePathFile.File, storePassword, certificateStore.RemoteHandler)); @@ -82,7 +82,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config) } else { - certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, true); + certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, false); certificateStore.DeleteCertificateByAlias(config.JobCertificate.Alias); certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(), storePathFile.Path, storePathFile.File, storePassword, certificateStore.RemoteHandler)); } diff --git a/RemoteFile/RemoteCertificateStore.cs b/RemoteFile/RemoteCertificateStore.cs index 19064905..ae19b7e5 100644 --- a/RemoteFile/RemoteCertificateStore.cs +++ b/RemoteFile/RemoteCertificateStore.cs @@ -96,7 +96,7 @@ internal RemoteCertificateStore(string server, string serverId, string serverPas logger.MethodExit(LogLevel.Debug); } - internal void LoadCertificateStore(ICertificateStoreSerializer certificateStoreSerializer, string storeProperties, bool includePrivateKey) + internal void LoadCertificateStore(ICertificateStoreSerializer certificateStoreSerializer, string storeProperties, bool isInventory) { logger.MethodEntry(LogLevel.Debug); @@ -107,7 +107,7 @@ internal void LoadCertificateStore(ICertificateStoreSerializer certificateStoreS if (byteContents.Length < 5) return; - CertificateStore = certificateStoreSerializer.DeserializeRemoteCertificateStore(byteContents, StorePath, StorePassword, RemoteHandler, includePrivateKey); + CertificateStore = certificateStoreSerializer.DeserializeRemoteCertificateStore(byteContents, StorePath, StorePassword, RemoteHandler, isInventory); logger.MethodExit(LogLevel.Debug); } diff --git a/integration-manifest.json b/integration-manifest.json index feaa7e92..89f8ce5e 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -139,6 +139,14 @@ "DependsOn": "", "Type": "Bool", "DefaultValue": false + }, + { + "Name": "IgnorePrivateKeyOnInventory", + "DisplayName": "Ignore Private Key On Inventory", + "Required": false, + "DependsOn": "", + "Type": "Bool", + "DefaultValue": false } ], "EntryParameters": [] diff --git a/readme_source.md b/readme_source.md index 907ca1af..cd430817 100644 --- a/readme_source.md +++ b/readme_source.md @@ -190,6 +190,7 @@ Use cases supported: - **Name:** IncludesChain, **Display Name:** Store Includes Chain, **Type:** Bool, **Default Value:** false. This custom field is **not required**. Default value if not present is 'false'. If 'true' the full certificate chain, if sent by Keyfactor Command, will be stored in the file. The order of appearance is always assumed to be 1) end entity certificate, 2) issuing CA certificate, and 3) root certificate. If additional CA tiers are applicable, the order will be end entity certificate up to the root CA certificate. if set to 'false', only the end entity certificate and private key will be stored in this store. This setting is only valid when IsTrustStore = false. - **Name:** SeparatePrivateKeyFilePath, **Display Name:** Separate Private Key File Location, **Type:** String, **Default Value:** empty. This custom field is **not required**. If empty, or not provided, it will be assumed that the private key for the certificate stored in this file will be inside the same file as the certificate. If the full path AND file name is put here, that location will be used to store the private key as an external file. This setting is only valid when IsTrustStore = false. - **Name:** IsRSAPrivateKey, **Display Name:** Is RSA Private Key, **Type:** Bool, **Default Value:** false. This custom field is **not required**. Default value if not present is 'false'. If 'true' it will be assumed that the private key for the certificate is a PKCS#1 RSA formatted private key (BEGIN RSA PRIVATE KEY). If 'false' (default), encrypted/non-encrypted PKCS#8 (BEGIN [ENCRYPTED] PRIVATE KEY) is assumed If set to 'true' the store password **must** be set to "no password", as PKCS#1 does not support encrypted keys. This setting is only valid when IsTrustStore = false. +- **Name:** IgnorePrivateKeyOnInventory, **Display Name:** Ignore Private Key On Inventory, **Type:** Bool, **Default Value:** false. This custom field is **not required**. Default value if not present is 'false'. If 'true', inventory for this certificate store will be performed without accessing the certificate's private key or the store password. This will functionally make the store INVENTORY ONLY, as all certificates will be returned with "Private Key Entry" = false. Also, no certificate chain relationships will be maintained, and all certificates will be considered separate entries (basically a trust store). This may be useful in situations where the client does not know the store password at inventory run time, but would still like the certificates to be imported into Keyfactor Command. Once the correct store password is entered for the store, the client may de-select this option (change the value to False), schedule an inventory job, and then the appropriate private key entry and chain information should be properly stored in the Keyfactor Command location, allowing for renewal/removal of the certificate at a later time. Entry Parameters Tab: - no additional entry parameters @@ -299,7 +300,10 @@ Steps to create a new supported file based certificate store type: 1. Clone this repository from GitHub 2. Open the .net core solution in the IDE of your choice 3. Under the ImplementationStoreTypes folder, create a new folder named for the new certificate store type -4. Create a new class (with namespace of Keyfactor.Extensions.Orchestrator.RemoteFile.{NewType}) in the new folder that will implement ICertificateStoreSerializer. By convention, {StoreTypeName}CertificateSerializer would be a good choice for the class name. This interface requires you to implement two methods: DesrializeRemoteCertificateStore and SerializeRemoteCertificateStore. The first method will be called passing in a byte array containing the contents of file based store you are managing. The developer will need to convert that to an Org.BouncyCastle.Pkcs.Pkcs12Store class and return it. The second method takes in an Org.BouncyCastle.Pkcs.Pkcs12Store and converts it to a collection of custom file representations, List. This is where the majority of the development will be done. +4. Create a new class (with namespace of Keyfactor.Extensions.Orchestrator.RemoteFile.{NewType}) in the new folder that will implement ICertificateStoreSerializer. By convention, {StoreTypeName}CertificateSerializer would be a good choice for the class name. This interface requires you to implement three methods: + - DesrializeRemoteCertificateStore - This method takes in a byte array containing the contents of file based store you are managing. The developer will need to convert that to an Org.BouncyCastle.Pkcs.Pkcs12Store class and return it. + - SerializeRemoteCertificateStore - This method takes in an Org.BouncyCastle.Pkcs.Pkcs12Store and converts it to a collection of custom file representations. + - GetPrivateKeyPath - This method returns the location of the external private key file for single certificate stores. Currently this is only used for RFPEM, and all other implementations return NULL for this method. If this is not applicable to your implementation just return a NULL value for this method. 5. Create an Inventory.cs class (with namespace of Keyfactor.Extensions.Orchestrator.RemoteFile.{NewType}) under the new folder and have it inherit InventoryBase. Override the internal GetCertificateStoreSerializer() method with a one line implementation returning a new instantiation of the class created in step 4. 6. Create a Management.cs class (with namespace of Keyfactor.Extensions.Orchestrator.RemoteFile.{NewType}) under the new folder and have it inherit ManagementBase. Override the internal GetCertificateStoreSerializer() method with a one line implementation returning a new instantiation of the class created in step 4. 7. Modify the manifest.json file to add three new sections (for Inventory, Management, and Discovery). Make sure for each, the "NewType" in Certstores.{NewType}.{Operation}, matches what you will use for the certificate store type short name in Keyfactor Command. On the "TypeFullName" line for all three sections, make sure the namespace matches what you used for your new classes. Note that the namespace for Discovery uses a common class for all supported types. Discovery is a common implementation for all supported store types. From d667ad248f7e4cfdfc7239e6180a25207720a608 Mon Sep 17 00:00:00 2001 From: Michael Henderson Date: Wed, 3 Jan 2024 14:36:09 -0800 Subject: [PATCH 2/5] Use new boostrap workflow --- .../workflows/keyfactor-starter-workflow.yml | 41 ++++++++----------- integration-manifest.json | 18 ++++---- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/.github/workflows/keyfactor-starter-workflow.yml b/.github/workflows/keyfactor-starter-workflow.yml index 069a645f..6d8de532 100644 --- a/.github/workflows/keyfactor-starter-workflow.yml +++ b/.github/workflows/keyfactor-starter-workflow.yml @@ -1,28 +1,19 @@ -name: Starter Workflow -on: [workflow_dispatch, push, pull_request] +name: Keyfactor Bootstrap Workflow -jobs: - call-create-github-release-workflow: - uses: Keyfactor/actions/.github/workflows/github-release.yml@main - - call-dotnet-build-and-release-workflow: - needs: [call-create-github-release-workflow] - uses: Keyfactor/actions/.github/workflows/dotnet-build-and-release.yml@main - 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: RemoteFile/bin/Release - secrets: - token: ${{ secrets.PRIVATE_PACKAGE_ACCESS }} +on: + workflow_dispatch: + pull_request: + types: [opened, closed, synchronize, edited, reopened] + push: + create: + branches: + - 'release-*.*' - call-generate-readme-workflow: - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - uses: Keyfactor/actions/.github/workflows/generate-readme.yml@main +jobs: + call-starter-workflow: + uses: keyfactor/actions/.github/workflows/starter.yml@v2 secrets: - token: ${{ secrets.APPROVE_README_PUSH }} - - call-update-catalog-workflow: - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - uses: Keyfactor/actions/.github/workflows/update-catalog.yml@main - secrets: - token: ${{ secrets.SDK_SYNC_PAT }} + token: ${{ secrets.V2BUILDTOKEN}} + APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}} + gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }} + gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }} diff --git a/integration-manifest.json b/integration-manifest.json index 89f8ce5e..641a20a9 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -5,11 +5,13 @@ "status": "production", "link_github": true, "update_catalog": true, + "release_dir": "RemoteFile/bin/Release", "description": "The Remote File Orchestrator allows for the remote management of file-based certificate stores. Discovery, Inventory, and Management functions are supported. The orchestrator performs operations by first converting the certificate store into a BouncyCastle PKCS12Store.", "about": { "orchestrator": { "UOFramework": "10.1", "pam_support": true, + "keyfactor_platform_version": "9.10", "win": { "supportsCreateStore": true, "supportsDiscovery": true, @@ -66,9 +68,9 @@ "DependsOn": "", "Type": "String", "DefaultValue": "" - } + } ], - "EntryParameters": [] + "EntryParameters": [] }, "RFPEM": { "Name": "RFPEM", @@ -188,9 +190,9 @@ "DependsOn": "", "Type": "String", "DefaultValue": "" - } + } ], - "EntryParameters": [] + "EntryParameters": [] }, "RFDER": { "Name": "RFDER", @@ -239,7 +241,7 @@ "DefaultValue": "" } ], - "EntryParameters": [] + "EntryParameters": [] }, "RFKDB": { "Name": "RFKDB", @@ -278,9 +280,9 @@ "DependsOn": "", "Type": "String", "DefaultValue": "" - } + } ], - "EntryParameters": [] + "EntryParameters": [] }, "RFORA": { "Name": "RFORA", @@ -329,7 +331,7 @@ "DefaultValue": "" } ], - "EntryParameters": [] + "EntryParameters": [] } } } From 625f020e5f85b519b9812c4a51443aa5e9712e37 Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Mon, 5 Feb 2024 13:21:02 +0000 Subject: [PATCH 3/5] Update generated README --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a1f0dc49..c213a867 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ + # Remote File The Remote File Orchestrator allows for the remote management of file-based certificate stores. Discovery, Inventory, and Management functions are supported. The orchestrator performs operations by first converting the certificate store into a BouncyCastle PKCS12Store. #### Integration status: Production - Ready for use in production environments. - ## About the Keyfactor Universal Orchestrator Extension This repository contains a Universal Orchestrator Extension which is a plugin to the Keyfactor Universal Orchestrator. Within the Keyfactor Platform, Orchestrators are used to manage “certificate stores” — collections of certificates and roots of trust that are found within and used by various applications. @@ -13,23 +13,22 @@ The Universal Orchestrator is part of the Keyfactor software distribution and is The Universal Orchestrator is the successor to the Windows Orchestrator. This Orchestrator Extension plugin only works with the Universal Orchestrator and does not work with the Windows Orchestrator. - ## Support for Remote File Remote File ###### To report a problem or suggest a new feature, use the **[Issues](../../issues)** tab. If you want to contribute actual bug fixes or proposed enhancements, use the **[Pull requests](../../pulls)** tab. - --- +--- + ## Keyfactor Version Supported The minimum version of the Keyfactor Universal Orchestrator Framework needed to run this version of the extension is 10.1 - ## Platform Specific Notes The Keyfactor Universal Orchestrator may be installed on either Windows or Linux based platforms. The certificate operations supported by a capability may vary based what platform the capability is installed on. The table below indicates what capabilities are supported based on which platform the encompassing Universal Orchestrator is running. From 152b5a863b31858b9400220f21e0466b85414a45 Mon Sep 17 00:00:00 2001 From: leefine02 Date: Mon, 5 Feb 2024 14:01:23 +0000 Subject: [PATCH 4/5] Update BouncyCastle.Cryptography to version 2.3.0 --- RemoteFile/RemoteFile.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RemoteFile/RemoteFile.csproj b/RemoteFile/RemoteFile.csproj index 11cf94c5..e0f8735d 100644 --- a/RemoteFile/RemoteFile.csproj +++ b/RemoteFile/RemoteFile.csproj @@ -12,7 +12,7 @@ - + From 04d0baec8f0590b04835b51b2bdb4278ca01d9a6 Mon Sep 17 00:00:00 2001 From: leefine02 Date: Mon, 5 Feb 2024 14:03:35 +0000 Subject: [PATCH 5/5] Upgrade BouncyCastle.Cryptography to version 2.3.0 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bada2f3..8c76be1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +v2.4.2 +- Bug fix: Upgrade BouncyCastle.Cryptography to version 2.3.0 to allow for RFKDB HMAC-SHA-384 support + v2.4.1 - Fix logging issue for RFKDB