From b282e1900616aa916781e94b537653c14454a80c Mon Sep 17 00:00:00 2001
From: Mikey Henderson <4452096+fiddlermikey@users.noreply.github.com>
Date: Wed, 12 Jun 2024 11:27:35 -0700
Subject: [PATCH] 2.0 release (#5) (#13)
Initial Public Release
* Change status codes to EndEntityStatus codes
* If digicert returns multiple identical certs (same order/cert ID pair), only return one of them
* Renew/reissue check added, parameter data types specified
* Don't throw exceptions when unable to retreive product IDs
* Prevent duplicate certs from syncing
* Allows for creating the CA prior to having config info available, creating with Enabled = false skips config validation.
* Port sync CA filter from DCOM gateway
Co-authored-by: Dave Galey <89407235+dgaley@users.noreply.github.com>
---
.../keyfactor-bootstrap-workflow.yml | 19 +
.../keyfactor-integrations-workflow.yml | 40 --
CHANGELOG.md | 2 +
README.md | 88 ++--
.../digicert-certcentral-anycagateway.csproj | 24 -
...y.sln => digicert-certcentral-caplugin.sln | 9 +-
.../API/CertCentralAPIBase.cs | 4 +-
.../API/CertificateChain.cs | 2 +-
.../API/CertificateTypeDetails.cs | 2 +-
.../API/CertificateTypes.cs | 2 +-
.../API/CheckDCV.cs | 2 +-
.../API/DownloadCertificateByFormat.cs | 2 +-
.../API/ListCertificateOrders.cs | 16 +-
.../API/ListContainers.cs | 2 +-
.../API/ListDomains.cs | 4 +-
.../API/ListDuplicates.cs | 4 +-
.../API/ListMetadata.cs | 2 +-
.../API/ListOrganizations.cs | 2 +-
.../API/ListReissues.cs | 4 +-
.../API/ListRequests.cs | 4 +-
.../API/OrderCertificate.cs | 9 +-
.../API/Reissue.cs | 4 +-
.../API/RevokeCertificate.cs | 2 +-
.../API/StatusChanges.cs | 5 +-
.../API/UpdateRequestStatus.cs | 2 +-
.../API/ViewCertificateOrder.cs | 4 +-
.../CertCentralCAPlugin.cs | 465 ++++++++++++++----
.../CertCentralConfig.cs | 11 +-
.../Client/CertCentralClient.cs | 12 +-
.../Client/CertCentralClientUtilities.cs | 4 +-
.../Client/Conversions.cs | 2 +-
.../Constants.cs | 3 +-
.../Models/CertCentralCertType.cs | 6 +-
.../Models/CertificateOrder.cs | 2 +-
.../Models/Contact.cs | 2 +-
.../Models/Error.cs | 2 +-
.../Models/IdInformation.cs | 2 +-
.../Models/Product.cs | 2 +-
.../Models/RequestSummary.cs | 2 +-
.../Utilities.cs | 2 +-
.../digicert-certcentral-caplugin.csproj | 19 +
integration-manifest.json | 18 +-
readme_source.md | 69 ++-
43 files changed, 608 insertions(+), 275 deletions(-)
create mode 100644 .github/workflows/keyfactor-bootstrap-workflow.yml
delete mode 100644 .github/workflows/keyfactor-integrations-workflow.yml
delete mode 100644 digicert-certcentral-anycagateway/digicert-certcentral-anycagateway.csproj
rename digicert-certcentral-anycagateway.sln => digicert-certcentral-caplugin.sln (65%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/CertCentralAPIBase.cs (89%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/CertificateChain.cs (91%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/CertificateTypeDetails.cs (99%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/CertificateTypes.cs (96%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/CheckDCV.cs (92%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/DownloadCertificateByFormat.cs (94%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ListCertificateOrders.cs (83%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ListContainers.cs (96%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ListDomains.cs (94%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ListDuplicates.cs (82%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ListMetadata.cs (95%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ListOrganizations.cs (98%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ListReissues.cs (82%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ListRequests.cs (95%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/OrderCertificate.cs (93%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/Reissue.cs (93%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/RevokeCertificate.cs (97%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/StatusChanges.cs (88%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/UpdateRequestStatus.cs (94%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/API/ViewCertificateOrder.cs (95%)
rename digicert-certcentral-anycagateway/CertCentralCAConnector.cs => digicert-certcentral-caplugin/CertCentralCAPlugin.cs (70%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/CertCentralConfig.cs (51%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Client/CertCentralClient.cs (98%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Client/CertCentralClientUtilities.cs (93%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Client/Conversions.cs (97%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Constants.cs (91%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Models/CertCentralCertType.cs (96%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Models/CertificateOrder.cs (96%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Models/Contact.cs (91%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Models/Error.cs (88%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Models/IdInformation.cs (87%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Models/Product.cs (91%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Models/RequestSummary.cs (90%)
rename {digicert-certcentral-anycagateway => digicert-certcentral-caplugin}/Utilities.cs (98%)
create mode 100644 digicert-certcentral-caplugin/digicert-certcentral-caplugin.csproj
diff --git a/.github/workflows/keyfactor-bootstrap-workflow.yml b/.github/workflows/keyfactor-bootstrap-workflow.yml
new file mode 100644
index 0000000..6d8de53
--- /dev/null
+++ b/.github/workflows/keyfactor-bootstrap-workflow.yml
@@ -0,0 +1,19 @@
+name: Keyfactor Bootstrap Workflow
+
+on:
+ workflow_dispatch:
+ pull_request:
+ types: [opened, closed, synchronize, edited, reopened]
+ push:
+ create:
+ branches:
+ - 'release-*.*'
+
+jobs:
+ call-starter-workflow:
+ uses: keyfactor/actions/.github/workflows/starter.yml@v2
+ secrets:
+ 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/.github/workflows/keyfactor-integrations-workflow.yml b/.github/workflows/keyfactor-integrations-workflow.yml
deleted file mode 100644
index 088548d..0000000
--- a/.github/workflows/keyfactor-integrations-workflow.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-name: Starter Workflow
-on: [workflow_dispatch, push, pull_request]
-
-jobs:
- call-create-github-release-workflow:
- uses: Keyfactor/actions/.github/workflows/github-release.yml@main
-
- call-assign-from-json-workflow:
- uses: Keyfactor/actions/.github/workflows/assign-env-from-json.yml@main
-
- call-dotnet-build-and-release-workflow:
- needs: [call-create-github-release-workflow, call-assign-from-json-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: ${{ needs.call-assign-from-json-workflow.outputs.release_dir }}
-
- secrets:
- token: ${{ secrets.PRIVATE_PACKAGE_ACCESS }}
-
- call-generate-readme-workflow:
- if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
- uses: Keyfactor/actions/.github/workflows/generate-readme.yml@main
- secrets:
- token: ${{ secrets.APPROVE_README_PUSH }}
-
- call-update-catalog-workflow:
- needs: call-assign-from-json-workflow
- if: needs.call-assign-from-json-workflow.outputs.update_catalog == 'True' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
- uses: Keyfactor/actions/.github/workflows/update-catalog.yml@main
- secrets:
- token: ${{ secrets.SDK_SYNC_PAT }}
-
- call-update-store-types-workflow:
- needs: call-assign-from-json-workflow
- if: needs.call-assign-from-json-workflow.outputs.integration_type == 'orchestrator' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
- uses: Keyfactor/actions/.github/workflows/update-store-types.yml@main
- secrets:
- token: ${{ secrets.UPDATE_STORE_TYPES }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e69de29..f180664 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -0,0 +1,2 @@
+### 2.0.0
+* Initial Public Release
diff --git a/README.md b/README.md
index 036f8e3..0b77ee8 100644
--- a/README.md
+++ b/README.md
@@ -1,67 +1,87 @@
-# digicert-certcentral-anycagateway
-DigiCert CertCentral plugin for the AnyCA Gateway framework
+# DigiCert CertCentral AnyCA REST Gateway Plugin
-#### Integration status: Prototype - Demonstration quality. Not for use in customer environments.
+DigiCert CertCentral plugin for the AnyCA REST Gateway framework
+#### Integration status: Production - Ready for use in production environments.
-## About the Keyfactor AnyGateway CA Connector
+## About the Keyfactor
-This repository contains an AnyGateway CA Connector, which is a plugin to the Keyfactor AnyGateway. AnyGateway CA Connectors allow Keyfactor Command to be used for inventory, issuance, and revocation of certificates from a third-party certificate authority.
+## Support for DigiCert CertCentral AnyCA REST Gateway Plugin
-
-## Support for digicert-certcentral-anycagateway
-
-digicert-certcentral-anycagateway is open source and community supported, meaning that there is **no SLA** applicable for these tools.
+DigiCert CertCentral AnyCA REST Gateway Plugin is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket via the Keyfactor Support Portal at https://support.keyfactor.com
###### 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.
+---
---
+
+# Introduction
+This AnyCA REST Gateway plug-in enables issuance, revocation, and synchronization of certificates from DigiCert's CertCentral offering.
+# Prerequisites
+## Prerequisite: Certificate Chain
-## Keyfactor AnyGateway Framework Supported
+In order to request certificates from the Keyfactor AnyGateway, the Keyfactor Command server must trust the certificate chain of trust. To ensure trust is established, download your Root and/or Subordinate CA certificates from DigiCert and import them into the appropriate local certificate stores on the Keyfactor AnyGateway and Command servers. More information can be found in the [AnyCA Gateway REST Install Guide](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/Preparing.htm)
-This gateway was compiled against version 1.0.0 of the AnyGateway Framework. You will need at least this version of the AnyGateway Framework Installed. If you have a later AnyGateway Framework Installed you will probably need to add binding redirects in the CAProxyServer.exe.config file to make things work properly.
+## Installation
+1. Download latest successful build from [GitHub Releases](../../releases/latest)
+2. Extract the .zip file, and from it, copy DigicertCAPlugin.dll and DigicertCAPlugin.deps.json to the 'C:\Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions' directory
+3. Within the 'C:\Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions\Connectors' folder, update the manifest.json file to contain the following:
----
+```json
+{
+ "extensions": {
+ "Keyfactor.AnyGateway.Extensions.IAnyCAPlugin": {
+ "CertCentralCAPlugin": {
+ "assemblypath": "../DigicertCAPlugin.dll",
+ "TypeFullName": "Keyfactor.Extensions.CAPlugin.DigiCert.CertCentralCAPlugin"
+ }
+ }
+ }
+}
+```
+NOTE: If the Connectors folder and/or the manifest.json file do not exist, they must be manually created
-# Introduction
-This AnyGateway plug-in enables issuance, revocation, and synchronization of certificates from DigiCert's CertCentral offering.
-# Prerequisites
+4. Restart the AnyCA Gateway service
-## Certificate Chain
+5. Navigate to the AnyCA Gateway REST portal and verify that the Gateway recognizes the DigiCert plugin by hovering over the ⓘ symbol to the right of the Gateway on the top left of the portal. CAPlugin Type should now be listed as CertCentralCA.
-In order to enroll for certificates the Keyfactor Command server must trust the trust chain. Once you create your Root and/or Subordinate CA, make sure to import the certificate chain into the AnyGateway and Command Server certificate store
+## Configuration
-# Install
-* Download latest successful build from [GitHub Releases](../../releases/latest)
+1. Follow the [official Keyfactor AnyCA Gateway REST documentation](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCA-Gateway.htm#Add_or_Edit_a_Certificate_Authority) to define a new Certificate Authority, using the following information to configure the CA Connection section:
-* Copy DigiCertCAGateway.dll and DigiCertCAGateway.deps.json to the Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions directory
+SETTING | REQUIRED? | DESCRIPTION
+--|--|--
+Enabled | Yes | Enables the DigiCert gateway functionality. Should almost always be set to 'true'
+APIKey | Yes | The API key the Gateway should use to communicate with the DigiCert API. Can be generated from the DigiCert portal.
+Region | No | The geographic region associated with your DigiCert account. Valid values are US and EU. Default if not provided is US.
+DivisionId | No | If your CertCentral account has multiple divisions AND uses any custom per-division product settings, provide a division ID for the gateway to use for product type lookups.
+RevokeCertificateOnly | No | If set to 'true', revoke operations will only revoke the individual certificate in question rather than the entire DigiCert order. Default if not provided is 'false'.
+SyncCAFilter | No | If you list one or more DigiCert issuing CA IDs here (comma-separated if more than one), the sync process will only return certs issued by one of those CAs. Leave this option empty to sync all certs from all CAs.
+FilterExpiredOrders | No | If set to 'true', syncing will not return certs that are expired more than a specified number of days. The number of days is specified by the SyncExpirationDays config option. Default value is 'false'.
+SyncExpirationDays | No | Only used if FilterExpiredOrders is 'true', otherwise ignored. Sets the number of days a cert has to be expired for the sync process to no longer sync it. For example, a value of 30 means sync will continue to return certs that have expired within the past 30 days, but not ones older than that. Default value is 0, meaning sync would not return any certs expired before the current day.
+
+2. After saving the CA configuration, Follow the [official AnyCA Gateway REST documentation](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCP-Gateway.htm#Certificate_Profile) to define one or more Certificate Profiles.
+3. Edit your newly configured CA, and you should now be able to modify the Templates tab. You need at least one template for each product type you wish to be able to enroll for. It is recommended to include the product type in the template name to make them easier to identify. Use the following information to configure the parameters for each template:
+
+SETTING | REQUIRED? | DESCRIPTION
+--|--|--
+LifetimeDays | No | The number of days of validity to use when requesting certs. Default if not provided is 365. NOTE FOR RENEWALS: If the value of LifetimeDays is evenly divisible by 365, the expiration day and month of the new cert will be set to the same values as the old cert if possible, to avoid renewal date drift.
+CACertId | No | The ID of the issuing CA to be used by DigiCert. If not specified, the default for your account will be used.
+Organization-Name | No | If specified, this value will override any organization name provided in the subject of the cert request on enrollment. Useful for requests (such as ACME) that contain no subject.
+RenewalWindowDays | No | The number of days from expiration that the gateway should do a reissue rather than a renewal. Default if not provided is 90, meaning any renewal request for certs that expire in more than 90 days will be treated as a reissue request.
-* Update the manifest.json file located in Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions\Connectors
- * If the manifest.json file or the Connectors folder do not exist, create them.
- ```json
-{
- "extensions": {
- "Keyfactor.AnyGateway.Extensions.ICAConnector": {
- "DigiCertCAConnector": {
- "assemblypath": "../DigiCertCAGateway.dll",
- "TypeFullName": "Keyfactor.Extensions.CAGateway.DigiCert.CertCentralCAConnector"
- }
- }
- }
-}
- ```
diff --git a/digicert-certcentral-anycagateway/digicert-certcentral-anycagateway.csproj b/digicert-certcentral-anycagateway/digicert-certcentral-anycagateway.csproj
deleted file mode 100644
index 7f095c1..0000000
--- a/digicert-certcentral-anycagateway/digicert-certcentral-anycagateway.csproj
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
- net6.0
- Keyfactor.Extensions.CAGateway.DigiCert
- enable
- disable
- DigicertCAGateway
-
-
-
-
-
-
-
-
-
-
-
- ..\Keyfactor.AnyGateway.ICAConnector.dll
-
-
-
-
diff --git a/digicert-certcentral-anycagateway.sln b/digicert-certcentral-caplugin.sln
similarity index 65%
rename from digicert-certcentral-anycagateway.sln
rename to digicert-certcentral-caplugin.sln
index 7031118..5425d02 100644
--- a/digicert-certcentral-anycagateway.sln
+++ b/digicert-certcentral-caplugin.sln
@@ -3,7 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33815.320
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "digicert-certcentral-anycagateway", "digicert-certcentral-anycagateway\digicert-certcentral-anycagateway.csproj", "{434D1E74-8EE7-4CC6-BAEC-62D224022F5F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "digicert-certcentral-caplugin", "digicert-certcentral-caplugin\digicert-certcentral-caplugin.csproj", "{434D1E74-8EE7-4CC6-BAEC-62D224022F5F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A6058317-1B5E-4E7C-9669-B3A6C0E605BB}"
+ ProjectSection(SolutionItems) = preProject
+ CHANGELOG.md = CHANGELOG.md
+ integration-manifest.json = integration-manifest.json
+ readme_source.md = readme_source.md
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/digicert-certcentral-anycagateway/API/CertCentralAPIBase.cs b/digicert-certcentral-caplugin/API/CertCentralAPIBase.cs
similarity index 89%
rename from digicert-certcentral-anycagateway/API/CertCentralAPIBase.cs
rename to digicert-certcentral-caplugin/API/CertCentralAPIBase.cs
index 4fc46b0..f920e5e 100644
--- a/digicert-certcentral-anycagateway/API/CertCentralAPIBase.cs
+++ b/digicert-certcentral-caplugin/API/CertCentralAPIBase.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Newtonsoft.Json;
using System;
@@ -7,7 +7,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public abstract class CertCentralBaseResponse
{
diff --git a/digicert-certcentral-anycagateway/API/CertificateChain.cs b/digicert-certcentral-caplugin/API/CertificateChain.cs
similarity index 91%
rename from digicert-certcentral-anycagateway/API/CertificateChain.cs
rename to digicert-certcentral-caplugin/API/CertificateChain.cs
index 2e0fd50..f4f2c8a 100644
--- a/digicert-certcentral-anycagateway/API/CertificateChain.cs
+++ b/digicert-certcentral-caplugin/API/CertificateChain.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class CertificateChainRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/CertificateTypeDetails.cs b/digicert-certcentral-caplugin/API/CertificateTypeDetails.cs
similarity index 99%
rename from digicert-certcentral-anycagateway/API/CertificateTypeDetails.cs
rename to digicert-certcentral-caplugin/API/CertificateTypeDetails.cs
index 60a439b..0a18667 100644
--- a/digicert-certcentral-anycagateway/API/CertificateTypeDetails.cs
+++ b/digicert-certcentral-caplugin/API/CertificateTypeDetails.cs
@@ -7,7 +7,7 @@
using System.Threading.Tasks;
using System.Web;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
///
/// Request to get the details for a specific certificate type.
diff --git a/digicert-certcentral-anycagateway/API/CertificateTypes.cs b/digicert-certcentral-caplugin/API/CertificateTypes.cs
similarity index 96%
rename from digicert-certcentral-anycagateway/API/CertificateTypes.cs
rename to digicert-certcentral-caplugin/API/CertificateTypes.cs
index 6dc3b81..6b14338 100644
--- a/digicert-certcentral-anycagateway/API/CertificateTypes.cs
+++ b/digicert-certcentral-caplugin/API/CertificateTypes.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
///
/// Request to get a certificate type.
diff --git a/digicert-certcentral-anycagateway/API/CheckDCV.cs b/digicert-certcentral-caplugin/API/CheckDCV.cs
similarity index 92%
rename from digicert-certcentral-anycagateway/API/CheckDCV.cs
rename to digicert-certcentral-caplugin/API/CheckDCV.cs
index f9ce0d8..63f6d37 100644
--- a/digicert-certcentral-anycagateway/API/CheckDCV.cs
+++ b/digicert-certcentral-caplugin/API/CheckDCV.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class DVCheckDCVRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/DownloadCertificateByFormat.cs b/digicert-certcentral-caplugin/API/DownloadCertificateByFormat.cs
similarity index 94%
rename from digicert-certcentral-anycagateway/API/DownloadCertificateByFormat.cs
rename to digicert-certcentral-caplugin/API/DownloadCertificateByFormat.cs
index d9b4c72..fbe0fe2 100644
--- a/digicert-certcentral-anycagateway/API/DownloadCertificateByFormat.cs
+++ b/digicert-certcentral-caplugin/API/DownloadCertificateByFormat.cs
@@ -4,7 +4,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class DownloadCertificateByFormatRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/ListCertificateOrders.cs b/digicert-certcentral-caplugin/API/ListCertificateOrders.cs
similarity index 83%
rename from digicert-certcentral-anycagateway/API/ListCertificateOrders.cs
rename to digicert-certcentral-caplugin/API/ListCertificateOrders.cs
index 3ca84cb..b9d670e 100644
--- a/digicert-certcentral-anycagateway/API/ListCertificateOrders.cs
+++ b/digicert-certcentral-caplugin/API/ListCertificateOrders.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Newtonsoft.Json;
using System;
@@ -8,16 +8,17 @@
using System.Threading.Tasks;
using System.Web;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class ListCertificateOrdersRequest : CertCentralBaseRequest
{
- public ListCertificateOrdersRequest()
+ public ListCertificateOrdersRequest(bool ignoreExpired = false)
{
this.Resource = "services/v2/order/certificate";
this.Method = "GET";
this.limit = 1000;
this.offset = 0;
+ this.ignoreExpired = ignoreExpired;
}
[JsonProperty("limit")]
@@ -26,6 +27,9 @@ public ListCertificateOrdersRequest()
[JsonProperty("offset")]
public int offset { get; set; }
+ public bool ignoreExpired { get; set; }
+ public int expiredWindow { get; set; } = 0;
+
public new string BuildParameters()
{
StringBuilder sbParamters = new StringBuilder();
@@ -33,6 +37,12 @@ public ListCertificateOrdersRequest()
sbParamters.Append("limit=").Append(this.limit.ToString());
sbParamters.Append("&offset=").Append(HttpUtility.UrlEncode(this.offset.ToString()));
+ if (ignoreExpired)
+ {
+ DateTime cutoffDate = DateTime.Today.AddDays(-1 - expiredWindow);
+ sbParamters.Append("&filters[valid_till]=>").Append(cutoffDate.ToString("yyyy-MM-dd"));
+ }
+
return sbParamters.ToString();
}
}
diff --git a/digicert-certcentral-anycagateway/API/ListContainers.cs b/digicert-certcentral-caplugin/API/ListContainers.cs
similarity index 96%
rename from digicert-certcentral-anycagateway/API/ListContainers.cs
rename to digicert-certcentral-caplugin/API/ListContainers.cs
index eaaf2d1..dc1e72f 100644
--- a/digicert-certcentral-anycagateway/API/ListContainers.cs
+++ b/digicert-certcentral-caplugin/API/ListContainers.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
///
/// Request to get the containers available.
diff --git a/digicert-certcentral-anycagateway/API/ListDomains.cs b/digicert-certcentral-caplugin/API/ListDomains.cs
similarity index 94%
rename from digicert-certcentral-anycagateway/API/ListDomains.cs
rename to digicert-certcentral-caplugin/API/ListDomains.cs
index 58f848a..5023746 100644
--- a/digicert-certcentral-anycagateway/API/ListDomains.cs
+++ b/digicert-certcentral-caplugin/API/ListDomains.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Microsoft.VisualBasic;
using Newtonsoft.Json;
@@ -9,7 +9,7 @@
using System.Threading.Tasks;
using System.Web;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class ListDomainsRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/ListDuplicates.cs b/digicert-certcentral-caplugin/API/ListDuplicates.cs
similarity index 82%
rename from digicert-certcentral-anycagateway/API/ListDuplicates.cs
rename to digicert-certcentral-caplugin/API/ListDuplicates.cs
index 2c0e8c6..d91c324 100644
--- a/digicert-certcentral-anycagateway/API/ListDuplicates.cs
+++ b/digicert-certcentral-caplugin/API/ListDuplicates.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Newtonsoft.Json;
@@ -8,7 +8,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class ListDuplicatesRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/ListMetadata.cs b/digicert-certcentral-caplugin/API/ListMetadata.cs
similarity index 95%
rename from digicert-certcentral-anycagateway/API/ListMetadata.cs
rename to digicert-certcentral-caplugin/API/ListMetadata.cs
index 6c4616b..d9713fc 100644
--- a/digicert-certcentral-anycagateway/API/ListMetadata.cs
+++ b/digicert-certcentral-caplugin/API/ListMetadata.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
///
/// Request to get the metadata fields available.
diff --git a/digicert-certcentral-anycagateway/API/ListOrganizations.cs b/digicert-certcentral-caplugin/API/ListOrganizations.cs
similarity index 98%
rename from digicert-certcentral-anycagateway/API/ListOrganizations.cs
rename to digicert-certcentral-caplugin/API/ListOrganizations.cs
index 7fe32af..4e225ff 100644
--- a/digicert-certcentral-anycagateway/API/ListOrganizations.cs
+++ b/digicert-certcentral-caplugin/API/ListOrganizations.cs
@@ -7,7 +7,7 @@
using System.Threading.Tasks;
using System.Web;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class ListOrganizationsRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/ListReissues.cs b/digicert-certcentral-caplugin/API/ListReissues.cs
similarity index 82%
rename from digicert-certcentral-anycagateway/API/ListReissues.cs
rename to digicert-certcentral-caplugin/API/ListReissues.cs
index e3fdf6c..e81b0ee 100644
--- a/digicert-certcentral-anycagateway/API/ListReissues.cs
+++ b/digicert-certcentral-caplugin/API/ListReissues.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Newtonsoft.Json;
using System;
@@ -7,7 +7,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class ListReissueRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/ListRequests.cs b/digicert-certcentral-caplugin/API/ListRequests.cs
similarity index 95%
rename from digicert-certcentral-anycagateway/API/ListRequests.cs
rename to digicert-certcentral-caplugin/API/ListRequests.cs
index a9d76e6..b692bc7 100644
--- a/digicert-certcentral-anycagateway/API/ListRequests.cs
+++ b/digicert-certcentral-caplugin/API/ListRequests.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Newtonsoft.Json;
using System;
@@ -8,7 +8,7 @@
using System.Threading.Tasks;
using System.Web;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class ListRequestsRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/OrderCertificate.cs b/digicert-certcentral-caplugin/API/OrderCertificate.cs
similarity index 93%
rename from digicert-certcentral-anycagateway/API/OrderCertificate.cs
rename to digicert-certcentral-caplugin/API/OrderCertificate.cs
index 16b57d2..9f82ea8 100644
--- a/digicert-certcentral-anycagateway/API/OrderCertificate.cs
+++ b/digicert-certcentral-caplugin/API/OrderCertificate.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Microsoft.VisualBasic;
using Newtonsoft.Json;
@@ -10,9 +10,9 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
- public class OrderRequest : CertCentralBaseRequest
+ public class OrderRequest : CertCentralBaseRequest
{
public OrderRequest(CertCentralCertType certType)
{
@@ -57,6 +57,9 @@ public OrderRequest(CertCentralCertType certType)
[JsonProperty("custom_fields")]
public List CustomFields { get; set; }
+ [JsonProperty("skip_approval")]
+ public bool SkipApproval { get; set; }
+
public void SetOrganization(int? organizationId)
{
if (organizationId.HasValue)
diff --git a/digicert-certcentral-anycagateway/API/Reissue.cs b/digicert-certcentral-caplugin/API/Reissue.cs
similarity index 93%
rename from digicert-certcentral-anycagateway/API/Reissue.cs
rename to digicert-certcentral-caplugin/API/Reissue.cs
index 696390c..432ea3b 100644
--- a/digicert-certcentral-anycagateway/API/Reissue.cs
+++ b/digicert-certcentral-caplugin/API/Reissue.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Newtonsoft.Json;
using System;
@@ -7,7 +7,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
[Serializable]
public class ReissueRequest : CertCentralBaseRequest
diff --git a/digicert-certcentral-anycagateway/API/RevokeCertificate.cs b/digicert-certcentral-caplugin/API/RevokeCertificate.cs
similarity index 97%
rename from digicert-certcentral-anycagateway/API/RevokeCertificate.cs
rename to digicert-certcentral-caplugin/API/RevokeCertificate.cs
index 6d180cf..11cbe09 100644
--- a/digicert-certcentral-anycagateway/API/RevokeCertificate.cs
+++ b/digicert-certcentral-caplugin/API/RevokeCertificate.cs
@@ -7,7 +7,7 @@
using System.Threading.Tasks;
using System.Web;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class RevokeCertificateRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/StatusChanges.cs b/digicert-certcentral-caplugin/API/StatusChanges.cs
similarity index 88%
rename from digicert-certcentral-anycagateway/API/StatusChanges.cs
rename to digicert-certcentral-caplugin/API/StatusChanges.cs
index 0b9a2cf..c277224 100644
--- a/digicert-certcentral-anycagateway/API/StatusChanges.cs
+++ b/digicert-certcentral-caplugin/API/StatusChanges.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class StatusChangesRequest : CertCentralBaseRequest
{
@@ -27,6 +27,9 @@ public class StatusOrder
[JsonProperty("status")]
public string status { get; set; }
+
+ [JsonIgnore]
+ public string serialNum { get; set; }
}
public class StatusChangesResponse : CertCentralBaseResponse
diff --git a/digicert-certcentral-anycagateway/API/UpdateRequestStatus.cs b/digicert-certcentral-caplugin/API/UpdateRequestStatus.cs
similarity index 94%
rename from digicert-certcentral-anycagateway/API/UpdateRequestStatus.cs
rename to digicert-certcentral-caplugin/API/UpdateRequestStatus.cs
index 66113d2..93ab5c3 100644
--- a/digicert-certcentral-anycagateway/API/UpdateRequestStatus.cs
+++ b/digicert-certcentral-caplugin/API/UpdateRequestStatus.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class UpdateRequestStatusRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/API/ViewCertificateOrder.cs b/digicert-certcentral-caplugin/API/ViewCertificateOrder.cs
similarity index 95%
rename from digicert-certcentral-anycagateway/API/ViewCertificateOrder.cs
rename to digicert-certcentral-caplugin/API/ViewCertificateOrder.cs
index 3a423f0..a61f326 100644
--- a/digicert-certcentral-anycagateway/API/ViewCertificateOrder.cs
+++ b/digicert-certcentral-caplugin/API/ViewCertificateOrder.cs
@@ -1,4 +1,4 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Newtonsoft.Json;
using System;
@@ -7,7 +7,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.API
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.API
{
public class ViewCertificateOrderRequest : CertCentralBaseRequest
{
diff --git a/digicert-certcentral-anycagateway/CertCentralCAConnector.cs b/digicert-certcentral-caplugin/CertCentralCAPlugin.cs
similarity index 70%
rename from digicert-certcentral-anycagateway/CertCentralCAConnector.cs
rename to digicert-certcentral-caplugin/CertCentralCAPlugin.cs
index 2881854..a617eca 100644
--- a/digicert-certcentral-anycagateway/CertCentralCAConnector.cs
+++ b/digicert-certcentral-caplugin/CertCentralCAPlugin.cs
@@ -1,9 +1,12 @@
using Keyfactor.AnyGateway.Extensions;
using Keyfactor.Common;
-using Keyfactor.Extensions.CAGateway.DigiCert.API;
-using Keyfactor.Extensions.CAGateway.DigiCert.Client;
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Common.Exceptions;
+using Keyfactor.Extensions.CAPlugin.DigiCert.API;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Client;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Keyfactor.Logging;
+using Keyfactor.PKI.Enums;
+using Keyfactor.PKI.Enums.EJBCA;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -18,11 +21,11 @@
using static Keyfactor.PKI.PKIConstants.Microsoft;
-using CertCentralConstants = Keyfactor.Extensions.CAGateway.DigiCert.Constants;
+using CertCentralConstants = Keyfactor.Extensions.CAPlugin.DigiCert.Constants;
-namespace Keyfactor.Extensions.CAGateway.DigiCert
+namespace Keyfactor.Extensions.CAPlugin.DigiCert
{
- public class CertCentralCAConnector : ICAConnector
+ public class CertCentralCAPlugin : IAnyCAPlugin
{
private CertCentralConfig _config;
private readonly ILogger _logger;
@@ -30,17 +33,28 @@ public class CertCentralCAConnector : ICAConnector
private Dictionary DCVTokens { get; } = new Dictionary();
- public CertCentralCAConnector()
+ public CertCentralCAPlugin()
{
- _logger = LogHandler.GetClassLogger();
+ _logger = LogHandler.GetClassLogger();
}
- public void Initialize(ICAConnectorConfigProvider configProvider, ICertificateDataReader certificateDataReader)
+ public void Initialize(IAnyCAPluginConfigProvider configProvider, ICertificateDataReader certificateDataReader)
{
_certificateDataReader = certificateDataReader;
string rawConfig = JsonConvert.SerializeObject(configProvider.CAConnectionData);
_config = JsonConvert.DeserializeObject(rawConfig);
}
+ ///
+ /// Enroll for a certificate
+ ///
+ /// The CSR for the certificate request
+ /// The subject string
+ /// The list of SANs
+ /// Collection of product information and options. Includes both product-level config options as well as custom enrollment fields.
+ /// The format of the request
+ /// The type of enrollment (new, renew, reissue)
+ /// The result of the enrollment
+ ///
public async Task Enroll(string csr, string subject, Dictionary san, EnrollmentProductInfo productInfo, RequestFormat requestFormat, EnrollmentType enrollmentType)
{
_logger.MethodEntry(LogLevel.Trace);
@@ -48,7 +62,12 @@ public async Task Enroll(string csr, string subject, Dictionar
CertCentralCertType certType = CertCentralCertType.GetAllTypes(_config).FirstOrDefault(x => x.ProductCode.Equals(productInfo.ProductID));
OrderRequest orderRequest = new OrderRequest(certType);
- var days = (productInfo.ProductParameters.ContainsKey("LifetimeDays")) ? int.Parse(productInfo.ProductParameters["LifetimeDays"]) : 365;
+ //var days = (productInfo.ProductParameters.ContainsKey("LifetimeDays") && !st) ? int.Parse(productInfo.ProductParameters["LifetimeDays"]) : 365;
+ var days = 365;
+ if (productInfo.ProductParameters.ContainsKey("LifetimeDays") && !string.IsNullOrEmpty(productInfo.ProductParameters["LifetimeDays"]))
+ {
+ days = int.Parse(productInfo.ProductParameters["LifetimeDays"]);
+ }
int validityYears = 0;
DateTime? customExpirationDate = null;
switch (days)
@@ -95,7 +114,10 @@ public async Task Enroll(string csr, string subject, Dictionar
if (productInfo.ProductParameters.TryGetValue(CertCentralConstants.RequestAttributes.ORGANIZATION_NAME, out string orgName))
{
// If org name is provided as a parameter, it overrides whatever is in the CSR
- organization = orgName;
+ if (!string.IsNullOrEmpty(orgName))
+ {
+ organization = orgName;
+ }
}
string signatureHash = certType.signatureAlgorithm;
@@ -146,8 +168,11 @@ public async Task Enroll(string csr, string subject, Dictionar
}
// Get CA Cert ID (if present)
- string caCertId = (productInfo.ProductParameters.ContainsKey("CACertId")) ? (string)productInfo.ProductParameters["CACertId"] : null;
-
+ string caCertId = null;
+ if (productInfo.ProductParameters.ContainsKey("CACertId") && !string.IsNullOrEmpty(productInfo.ProductParameters["CACertId"]))
+ {
+ caCertId = (string)productInfo.ProductParameters["CACertId"];
+ }
// Set up request
orderRequest.Certificate.CommonName = commonName;
orderRequest.Certificate.CSR = csr;
@@ -203,30 +228,46 @@ public async Task Enroll(string csr, string subject, Dictionar
orderRequest.ValidityYears = validityYears;
}
- var renewWindow = (productInfo.ProductParameters.ContainsKey(CertCentralConstants.Config.RENEWAL_WINDOW)) ? int.Parse(productInfo.ProductParameters[CertCentralConstants.Config.RENEWAL_WINDOW]) : 90;
+ var renewWindow = 90;
+ if (productInfo.ProductParameters.ContainsKey(CertCentralConstants.Config.RENEWAL_WINDOW) && !string.IsNullOrEmpty(productInfo.ProductParameters[CertCentralConstants.Config.RENEWAL_WINDOW]))
+ {
+ renewWindow = int.Parse(productInfo.ProductParameters[CertCentralConstants.Config.RENEWAL_WINDOW]);
+ }
string priorCertSnString = null;
string priorCertReqID = null;
+
+ // Current gateway core leaves it up to the integration to determine if it is a renewal or a reissue
if (enrollmentType == EnrollmentType.RenewOrReissue)
{
//// Determine if we're going to do a renew or a reissue.
- //string priorCertSnString = productInfo.ProductParameters["PriorCertSN"];
- //_logger.LogTrace($"Attempting to retrieve the certificate with serial number {priorCertSnString}.");
- //byte[] priorCertSn = DataConversion.HexToBytes(priorCertSnString);
- //CAConnectorCertificate anyGatewayCertificate = _certificateDataReader.(priorCertSn);
- //if (anyGatewayCertificate == null)
- //{
- // throw new Exception($"No certificate with serial number '{priorCertSnString}' could be found.");
- //}
- enrollmentType = EnrollmentType.Renew;
+ priorCertSnString = productInfo.ProductParameters["PriorCertSN"];
+ _logger.LogTrace($"Attempting to retrieve the certificate with serial number {priorCertSnString}.");
+ var reqId = _certificateDataReader.GetRequestIDBySerialNumber(priorCertSnString).Result;
+ if (string.IsNullOrEmpty(reqId))
+ {
+ throw new Exception($"No certificate with serial number '{priorCertSnString}' could be found.");
+ }
+ var expDate = _certificateDataReader.GetExpirationDateByRequestId(reqId);
+
+ var renewCutoff = DateTime.Now.AddDays(renewWindow * -1);
+ if (expDate > renewCutoff)
+ {
+ _logger.LogTrace($"Certificate with serial number {priorCertSnString} is within renewal window");
+ enrollmentType = EnrollmentType.Renew;
+ }
+ else
+ {
+ _logger.LogTrace($"Certificate with serial number {priorCertSnString} is not within renewal window. Reissuing...");
+ enrollmentType = EnrollmentType.Reissue;
+ }
}
// Check if the order has more validity in it (multi-year cert). If so, do a reissue instead of a renew
if (enrollmentType == EnrollmentType.Renew)
{
// Get the old cert so we can properly construct the request.
- priorCertSnString = productInfo.ProductParameters["PriorCertSN"];
- _logger.LogTrace($"Attempting to retrieve the certificate with serial number {priorCertSnString}.");
+ _logger.LogTrace($"Checking for additional order validity.");
priorCertReqID = await _certificateDataReader.GetRequestIDBySerialNumber(priorCertSnString);
if (string.IsNullOrEmpty(priorCertReqID))
{
@@ -249,13 +290,20 @@ public async Task Enroll(string csr, string subject, Dictionar
if (certOrder.order_valid_till.HasValue && certOrder.order_valid_till.Value.AddDays(renewWindow * -1) > DateTime.UtcNow)
{
+ _logger.LogTrace($"Additional order validity found. Reissuing cert with new expiration.");
enrollmentType = EnrollmentType.Reissue;
}
+ else
+ {
+ _logger.LogTrace($"No additional order validity found. Renewing certificate.");
+ }
}
_logger.LogTrace("Making request to Enroll");
+ orderRequest.SkipApproval = true;
+
switch (enrollmentType)
{
case EnrollmentType.New:
@@ -272,6 +320,10 @@ public async Task Enroll(string csr, string subject, Dictionar
}
}
+ ///
+ /// Get the annotations for the CA Connector-level configuration fields
+ ///
+ ///
public Dictionary GetCAConnectorAnnotations()
{
return new Dictionary()
@@ -280,29 +332,44 @@ public Dictionary GetCAConnectorAnnotations()
{
Comments = "API Key for connecting to DigiCert",
Hidden = true,
- DefaultValue = ""
+ DefaultValue = "",
+ Type = "String"
},
[CertCentralConstants.Config.DIVISION_ID] = new PropertyConfigInfo()
{
Comments = "Division ID to use for retrieving product details (only if account is configured with per-divison product settings)",
Hidden = false,
- DefaultValue = ""
+ DefaultValue = "",
+ Type = "Number"
},
[CertCentralConstants.Config.REGION] = new PropertyConfigInfo()
{
Comments = "The geographic region that your DigiCert CertCentral account is in. Valid options are US and EU.",
Hidden = false,
- DefaultValue = "US"
+ DefaultValue = "US",
+ Type = "String"
},
[CertCentralConstants.Config.REVOKE_CERT] = new PropertyConfigInfo()
{
Comments = "Default DigiCert behavior on revocation requests is to revoke the entire order. If this value is changed to 'true', revocation requests will instead just revoke the individual certificate.",
Hidden = false,
- DefaultValue = "false"
+ DefaultValue = false,
+ Type = "Boolean"
+ },
+ [CertCentralConstants.Config.ENABLED] = new PropertyConfigInfo()
+ {
+ Comments = "Flag to Enable or Disable gateway functionality. Disabling is primarily used to allow creation of the CA prior to configuration information being available.",
+ Hidden = false,
+ DefaultValue = true,
+ Type = "Boolean"
}
};
}
+ ///
+ /// Get the list of valid product IDs from DigiCert
+ ///
+ ///
public List GetProductIds()
{
try
@@ -317,19 +384,27 @@ public List GetProductIds()
// If we couldn't get the types, return an empty comment.
if (productTypesResponse.Status != CertCentralBaseResponse.StatusType.SUCCESS)
{
- throw new Exception("Unable to retrieve product list");
+ _logger.LogError($"Unable to retrieve product list: {productTypesResponse.Errors[0]}");
+ return new List();
}
- return productTypesResponse.Products.Select(x => x.NameId).ToList(); }
+ return productTypesResponse.Products.Select(x => x.NameId).ToList();
+ }
catch (Exception ex)
{
// Swallow exceptions and return an empty string.
_logger.LogError($"Unable to retrieve product list: {ex.Message}");
- throw;
+ return new List();
}
}
- public async Task GetSingleRecord(string caRequestID)
+
+ ///
+ /// Retrieve a single record from DigiCert
+ ///
+ /// The gateway request ID of the record to retrieve, in the format 'orderID-certID'
+ ///
+ public async Task GetSingleRecord(string caRequestID)
{
_logger.MethodEntry(LogLevel.Trace);
// Split ca request id into order and cert id
@@ -348,13 +423,20 @@ public async Task GetSingleRecord(string caRequestID)
string certificate = null;
int status = GetCertificateStatusFromCA(certToCheck.status, orderId);
- if (status == (int)RequestDisposition.ISSUED || status == (int)RequestDisposition.REVOKED || status == (int)RequestDisposition.UNKNOWN)
+ if (status == (int)EndEntityStatus.GENERATED || status == (int)EndEntityStatus.REVOKED)
{
// We have a status where there may be a cert to download, try to download it
CertificateChainResponse certificateChainResponse = client.GetCertificateChain(new CertificateChainRequest(certId));
if (certificateChainResponse.Status == CertCentralBaseResponse.StatusType.SUCCESS)
{
- certificate = certificateChainResponse.Intermediates[0].PEM;
+ if (certificateChainResponse.Intermediates.Count > 0)
+ {
+ certificate = certificateChainResponse.Intermediates[0].PEM;
+ }
+ else
+ {
+ throw new Exception($"No PEM certificate returned for certificate {certId} in order {orderId}. This could be due to a certificate that provisioned via an alternative method, such as a physical token.");
+ }
}
else
{
@@ -362,7 +444,7 @@ public async Task GetSingleRecord(string caRequestID)
}
}
_logger.MethodExit(LogLevel.Trace);
- return new CAConnectorCertificate
+ return new AnyCAPluginCertificate
{
CARequestID = caRequestID,
Certificate = certificate,
@@ -372,6 +454,10 @@ public async Task GetSingleRecord(string caRequestID)
};
}
+ ///
+ /// Get the annotations for the product-level configuration fields
+ ///
+ ///
public Dictionary GetTemplateParameterAnnotations()
{
return new Dictionary()
@@ -380,32 +466,49 @@ public Dictionary GetTemplateParameterAnnotations()
{
Comments = "OPTIONAL: The number of days of validity to use when requesting certs. If not provided, default is 365.",
Hidden = false,
- DefaultValue = "365"
+ DefaultValue = 365,
+ Type = "Number"
},
[CertCentralConstants.Config.CA_CERT_ID] = new PropertyConfigInfo()
{
Comments = "OPTIONAL: ID of issuing CA to use by DigiCert. If not provided, the default for your account will be used.",
Hidden = false,
- DefaultValue = ""
+ DefaultValue = "",
+ Type = "String"
},
[CertCentralConstants.RequestAttributes.ORGANIZATION_NAME] = new PropertyConfigInfo()
{
Comments = "OPTIONAL: For requests that will not have a subject (such as ACME) you can use this field to provide the organization name. Value supplied here will override any CSR values, so do not include this field if you want the organization from the CSR to be used.",
Hidden = false,
- DefaultValue = ""
+ DefaultValue = "",
+ Type = "String"
},
[CertCentralConstants.Config.RENEWAL_WINDOW] = new PropertyConfigInfo()
{
Comments = "OPTIONAL: The number of days from certificate expiration that the gateway should do a renewal rather than a reissue. If not provided, default is 90.",
Hidden = false,
- DefaultValue = "90"
+ DefaultValue = 90,
+ Type = "Number"
}
};
}
+ ///
+ /// Verify connectivity with the DigiCert web service
+ ///
+ ///
+ ///
public async Task Ping()
{
_logger.MethodEntry(LogLevel.Trace);
+ if (!_config.Enabled)
+ {
+ _logger.LogWarning($"The CA is currently in the Disabled state. It must be Enabled to perform operations. Skipping connectivity test...");
+ _logger.MethodExit(LogLevel.Trace);
+ return;
+ }
+
+
try
{
CertCentralClient client = CertCentralClientUtilities.BuildCertCentralClient(_config);
@@ -428,6 +531,15 @@ public async Task Ping()
_logger.MethodExit(LogLevel.Trace);
}
+ ///
+ /// Revoke either a single certificate or an order, depending on your configuration settings
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public async Task Revoke(string caRequestID, string hexSerialNumber, uint revocationReason)
{
_logger.MethodEntry(LogLevel.Trace);
@@ -451,10 +563,12 @@ public async Task Revoke(string caRequestID, string hexSerialNumber, uint r
RevokeCertificateResponse revokeResponse;
if (_config.RevokeCertificateOnly.HasValue && _config.RevokeCertificateOnly.Value)
{
+ _logger.LogInformation($"Attempting to revoke certificate with CA Request Id {caRequestID} and serial number {hexSerialNumber}. RevokeCertificateOnly is true, so revoking single certificate.");
revokeResponse = client.RevokeCertificate(new RevokeCertificateRequest(certId) { comments = Conversions.RevokeReasonToString(revocationReason) });
}
else
{
+ _logger.LogInformation($"Attempting to revoke certificate with CA Request Id {caRequestID} and serial number {hexSerialNumber}. RevokeCertificateOnly is false, so revoking the entire order.");
revokeResponse = client.RevokeCertificate(new RevokeCertificateByOrderRequest(orderResponse.id) { comments = Conversions.RevokeReasonToString(revocationReason) });
}
@@ -474,10 +588,19 @@ public async Task Revoke(string caRequestID, string hexSerialNumber, uint r
_logger.LogError(errMsg);
throw new Exception(errMsg);
}
- return (int)RequestDisposition.REVOKED;
+ return (int)EndEntityStatus.REVOKED;
}
- public async Task Synchronize(BlockingCollection blockingBuffer, DateTime? lastSync, bool fullSync, CancellationToken cancelToken)
+ ///
+ /// Perform an inventory of DigiCert certs
+ ///
+ /// Buffer to return retrieved certs in
+ /// DateTime of the last sync performed
+ /// If true, return all certs from DigiCert. If false, only return certs that are new or changed status since the lastSync time.
+ ///
+ ///
+ ///
+ public async Task Synchronize(BlockingCollection blockingBuffer, DateTime? lastSync, bool fullSync, CancellationToken cancelToken)
{
_logger.MethodEntry(LogLevel.Trace);
@@ -486,7 +609,7 @@ public async Task Synchronize(BlockingCollection blockin
string lastSyncFormat = FormatSyncDate(lastSync);
string todaySyncFormat = FormatSyncDate(utcDate);
- List certs = new List();
+ List certs = new List();
List certsToSync = new List();
_logger.LogDebug("Attempting to create a CertCentral client");
@@ -495,9 +618,27 @@ public async Task Synchronize(BlockingCollection blockin
List skippedOrders = new List();
int certCount = 0;
+ string syncCAstring = string.Join(",", _config.SyncCAFilter ?? new List());
+ _logger.LogTrace($"Sync CAs: {syncCAstring}");
+ List caList = _config.SyncCAFilter ?? new List();
+ caList.ForEach(c => c.ToUpper());
+
+
if (fullSync)
{
- ListCertificateOrdersResponse ordersResponse = client.ListAllCertificateOrders();
+ bool ignoreExpired = false; int expiredWindow = 0;
+ if (_config.FilterExpiredOrders.HasValue && _config.FilterExpiredOrders.Value)
+ {
+ ignoreExpired = true;
+ if (_config.SyncExpirationDays.HasValue)
+ {
+ expiredWindow = _config.SyncExpirationDays.Value;
+ }
+ }
+ long time = DateTime.Now.Ticks;
+ long starttime = time;
+ _logger.LogDebug($"SYNC: Starting sync at time {time}");
+ ListCertificateOrdersResponse ordersResponse = client.ListAllCertificateOrders(ignoreExpired, expiredWindow);
if (ordersResponse.Status == CertCentralBaseResponse.StatusType.ERROR)
{
Error error = ordersResponse.Errors[0];
@@ -506,14 +647,21 @@ public async Task Synchronize(BlockingCollection blockin
}
else
{
+ _logger.LogDebug($"SYNC: Found {ordersResponse.orders.Count} records");
foreach (var orderDetails in ordersResponse.orders)
{
- List orderCerts = new List();
+ List orderCerts = new List();
try
{
cancelToken.ThrowIfCancellationRequested();
string caReqId = orderDetails.id + "-" + orderDetails.certificate.id;
- orderCerts = GetAllConnectorCertsForOrder(caReqId);
+ _logger.LogDebug($"SYNC: Retrieving certs for order id {orderDetails.id}");
+ orderCerts = GetAllConnectorCertsForOrder(caReqId, caList);
+ if (orderCerts == null || orderCerts.Count == 0)
+ {
+ continue;
+ }
+ _logger.LogDebug($"SYNC: Retrieved {orderCerts.Count} certs at time {DateTime.Now.Ticks}");
}
catch
{
@@ -529,6 +677,7 @@ public async Task Synchronize(BlockingCollection blockin
}
}
+ _logger.LogDebug($"SYNC: Complete after {DateTime.Now.Ticks - starttime} ticks");
}
}
else
@@ -545,12 +694,16 @@ public async Task Synchronize(BlockingCollection blockin
int orderCount = statusChangesResponse.orders.Count;
foreach (var order in statusChangesResponse.orders)
{
- List orderCerts = new List();
+ List orderCerts = new List();
try
{
cancelToken.ThrowIfCancellationRequested();
string caReqId = order.order_id + "-" + order.certificate_id;
- orderCerts = GetAllConnectorCertsForOrder(caReqId);
+ orderCerts = GetAllConnectorCertsForOrder(caReqId, caList);
+ if (orderCerts == null || orderCerts.Count > 0)
+ {
+ continue;
+ }
}
catch
{
@@ -582,9 +735,28 @@ public async Task Synchronize(BlockingCollection blockin
_logger.MethodExit(LogLevel.Trace);
}
+ ///
+ /// Validate CA Connection-level configuration fields
+ ///
+ ///
+ ///
public async Task ValidateCAConnectionInfo(Dictionary connectionInfo)
{
_logger.MethodEntry(LogLevel.Trace);
+ try
+ {
+ if (!(bool)connectionInfo[CertCentralConstants.Config.ENABLED])
+ {
+ _logger.LogWarning($"The CA is currently in the Disabled state. It must be Enabled to perform operations. Skipping validation...");
+ _logger.MethodExit(LogLevel.Trace);
+ return;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Exception: {LogHandler.FlattenException(ex)}");
+ }
+
List errors = new List();
_logger.LogTrace("Checking the API Key.");
@@ -616,7 +788,7 @@ public async Task ValidateCAConnectionInfo(Dictionary connection
if (domains.Status == CertCentralBaseResponse.StatusType.ERROR)
{
_logger.LogError($"Error from CertCentral client: {domains.Errors[0].message}");
- errors.Add("Error grabbing DigiCert domains");
+ errors.Add("Error grabbing DigiCert domains. See log file for details.");
}
_logger.MethodExit(LogLevel.Trace);
// We cannot proceed if there are any errors.
@@ -628,9 +800,17 @@ public async Task ValidateCAConnectionInfo(Dictionary connection
private void ThrowValidationException(List errors)
{
- throw new ArgumentException(string.Join("\n", errors));
+ string validationMsg = $"Validation errors:\n{string.Join("\n", errors)}";
+ throw new AnyCAValidationException(validationMsg);
}
+ ///
+ /// Validate product-level configuration fields
+ ///
+ ///
+ ///
+ ///
+ ///
public async Task ValidateProductInfo(EnrollmentProductInfo productInfo, Dictionary connectionInfo)
{
_logger.MethodEntry(LogLevel.Trace);
@@ -654,14 +834,14 @@ public async Task ValidateProductInfo(EnrollmentProductInfo productInfo, Diction
CertificateTypesResponse productIdResponse = client.GetAllCertificateTypes();
if (productIdResponse.Status != CertCentralBaseResponse.StatusType.SUCCESS)
{
- throw new Exception($"The product types could not be retrieved from the server. The following errors occurred: {string.Join(" ", productIdResponse.Errors.Select(x => x.message))}");
+ throw new AnyCAValidationException($"The product types could not be retrieved from the server. The following errors occurred: {string.Join(" ", productIdResponse.Errors.Select(x => x.message))}");
}
// Get product and check if it exists.
var product = productIdResponse.Products.FirstOrDefault(x => x.NameId.Equals(productId, StringComparison.InvariantCultureIgnoreCase));
if (product == null)
{
- throw new Exception($"The product ID '{productId}' does not exist. The following product IDs are valid: {string.Join(", ", productIdResponse.Products.Select(x => x.NameId))}");
+ throw new AnyCAValidationException($"The product ID '{productId}' does not exist. The following product IDs are valid: {string.Join(", ", productIdResponse.Products.Select(x => x.NameId))}");
}
// Get product ID details.
@@ -670,14 +850,20 @@ public async Task ValidateProductInfo(EnrollmentProductInfo productInfo, Diction
detailsRequest.ContainerId = null;
if (connectionInfo.ContainsKey(CertCentralConstants.Config.DIVISION_ID))
{
- int.TryParse((string)connectionInfo[CertCentralConstants.Config.DIVISION_ID], out int divId);
- detailsRequest.ContainerId = divId;
+ if (int.TryParse($"{connectionInfo[CertCentralConstants.Config.DIVISION_ID]}", out int divId))
+ {
+ detailsRequest.ContainerId = divId;
+ }
+ else
+ {
+ throw new AnyCAValidationException($"Unable to parse division ID '{connectionInfo[CertCentralConstants.Config.DIVISION_ID]}'. Check that this is a valid division ID.");
+ }
}
CertificateTypeDetailsResponse details = client.GetCertificateTypeDetails(detailsRequest);
if (details.Errors.Any())
{
- throw new Exception($"Validation of '{productId}' failed for the following reasons: {string.Join(" ", details.Errors.Select(x => x.message))}.");
+ throw new AnyCAValidationException($"Validation of '{productId}' failed for the following reasons: {string.Join(" ", details.Errors.Select(x => x.message))}.");
}
_logger.MethodExit(LogLevel.Trace);
}
@@ -702,7 +888,7 @@ private async Task NewCertificate(CertCentralClient client, Or
///
private async Task ExtractEnrollmentResult(CertCentralClient client, OrderResponse orderResponse, string commonName)
{
- int status = (int)RequestDisposition.UNKNOWN;
+ int status = 0;
string statusMessage = null;
string certificate = null;
string caRequestID = null;
@@ -711,7 +897,7 @@ private async Task ExtractEnrollmentResult(CertCentralClient c
{
_logger.LogError($"Error from CertCentral client: {orderResponse.Errors.First().message}");
- status = (int)RequestDisposition.FAILED;
+ status = (int)EndEntityStatus.FAILED;
statusMessage = orderResponse.Errors[0].message;
}
else if (orderResponse.Status == CertCentralBaseResponse.StatusType.SUCCESS)
@@ -771,12 +957,12 @@ private async Task ExtractEnrollmentResult(CertCentralClient c
caRequestID = orderResponse.OrderId.ToString();
if (updateStatusResponse.Errors.Any(x => x.code == "access_denied|invalid_approver"))
{
- status = (int)RequestDisposition.EXTERNAL_VALIDATION;
+ status = (int)EndEntityStatus.EXTERNALVALIDATION;
statusMessage = errors;
}
else
{
- status = (int)RequestDisposition.FAILED;
+ status = (int)EndEntityStatus.FAILED;
statusMessage = $"Approval of order '{orderResponse.OrderId}' failed. Check the gateway logs for more details.";
}
}
@@ -790,7 +976,7 @@ private async Task ExtractEnrollmentResult(CertCentralClient c
caRequestID = $"{order.id}-{order.certificate.id}";
try
{
- CAConnectorCertificate connCert = await GetSingleRecord($"{order.id}-{order.certificate.id}");
+ AnyCAPluginCertificate connCert = await GetSingleRecord($"{order.id}-{order.certificate.id}");
certificate = connCert.Certificate;
status = connCert.Status;
statusMessage = $"Post-submission approval of order {order.id} returned success";
@@ -798,7 +984,7 @@ private async Task ExtractEnrollmentResult(CertCentralClient c
catch (Exception getRecordEx)
{
_logger.LogWarning($"Unable to retrieve certificate {order.certificate.id} for order {order.id}: {getRecordEx.Message}");
- status = (int)RequestDisposition.UNKNOWN;
+ status = (int)EndEntityStatus.INPROCESS;
statusMessage = $"Post-submission approval of order {order.id} was successful, but pickup failed";
}
}
@@ -807,8 +993,7 @@ private async Task ExtractEnrollmentResult(CertCentralClient c
else
{
_logger.LogWarning("The request disposition is for this enrollment could not be determined.");
- status = (int)RequestDisposition.UNKNOWN;
- statusMessage = "The request disposition could not be determined.";
+ throw new Exception($"The request disposition is for this enrollment could not be determined.");
}
}
}
@@ -821,6 +1006,12 @@ private async Task ExtractEnrollmentResult(CertCentralClient c
};
}
+ ///
+ /// Convert DigiCert status string into a EndEntityStatus code
+ ///
+ ///
+ ///
+ ///
private int GetCertificateStatusFromCA(string status, int orderId)
{
switch (status)
@@ -828,32 +1019,43 @@ private int GetCertificateStatusFromCA(string status, int orderId)
case "issued":
case "approved":
case "expired":
- return (int)RequestDisposition.ISSUED;
+ return (int)EndEntityStatus.GENERATED;
case "processing":
case "reissue_pending":
case "pending": // Pending from DigiCert means it will be issued after validation
- return (int)RequestDisposition.EXTERNAL_VALIDATION;
+ case "waiting_pickup":
+ return (int)EndEntityStatus.EXTERNALVALIDATION;
case "denied":
- return (int)RequestDisposition.DENIED;
+ case "rejected":
+ case "canceled":
+ return (int)EndEntityStatus.FAILED;
case "revoked":
- return (int)RequestDisposition.REVOKED;
+ return (int)EndEntityStatus.REVOKED;
case "needs_approval": // This indicates that the request has to be approved through DigiCert, which is a misconfiguration
_logger.LogWarning($"Order {orderId} needs to be approved in the DigiCert portal prior to issuance");
- return (int)RequestDisposition.EXTERNAL_VALIDATION;
+ return (int)EndEntityStatus.EXTERNALVALIDATION;
default:
- _logger.LogWarning($"Order {orderId} has unexpected status {status}");
- return (int)RequestDisposition.UNKNOWN;
+ _logger.LogError($"Order {orderId} has unexpected status {status}");
+ throw new Exception($"Order {orderId} has unknown status {status}");
}
}
+ ///
+ /// Get the list of reissues for a given order
+ ///
+ ///
+ ///
+ ///
+ ///
private List GetReissues(CertCentralClient digiClient, int orderId)
{
_logger.LogTrace($"Getting Reissues for order {orderId}");
+ List reqIds = new List();
List reissueCerts = new List();
ListReissueResponse reissueResponse = digiClient.ListReissues(new ListReissueRequest(orderId));
if (reissueResponse.Status == CertCentralBaseResponse.StatusType.ERROR)
@@ -870,7 +1072,8 @@ private List GetReissues(CertCentralClient digiClient, int orderId)
{
order_id = orderId,
certificate_id = reissueCert.id,
- status = reissueCert.status
+ status = reissueCert.status,
+ serialNum = reissueCert.serial_number
};
reissueCerts.Add(reissueStatusOrder);
}
@@ -879,6 +1082,13 @@ private List GetReissues(CertCentralClient digiClient, int orderId)
return reissueCerts;
}
+ ///
+ /// Get the list of duplicate certs for a given order
+ ///
+ ///
+ ///
+ ///
+ ///
private List GetDuplicates(CertCentralClient digiClient, int orderId)
{
_logger.LogTrace($"Getting Duplicates for order {orderId}");
@@ -898,7 +1108,8 @@ private List GetDuplicates(CertCentralClient digiClient, int orderI
{
order_id = orderId,
certificate_id = dupeCert.id,
- status = dupeCert.status
+ status = dupeCert.status,
+ serialNum = dupeCert.serial_number
};
dupeCerts.Add(dupeStatusOrder);
}
@@ -977,6 +1188,11 @@ private async Task Reissue(CertCentralClient client, Enrollmen
return await ExtractEnrollmentResult(client, client.ReissueCertificate(reissueRequest), commonName);
}
+ ///
+ /// Verify that the given product ID is valid
+ ///
+ ///
+ ///
private void CheckProductExistence(string productId)
{
// Check that the product type is still valid.
@@ -1023,8 +1239,12 @@ string FormatSyncDate(DateTime? syncTime)
return date + "+" + time;
}
-
- private List GetAllConnectorCertsForOrder(string caRequestID)
+ ///
+ /// Get all of the certs for a given order, including reissues and duplicates, in CAConnectorCertificate form
+ ///
+ ///
+ ///
+ private List GetAllConnectorCertsForOrder(string caRequestID, List caFilterIds)
{
_logger.MethodEntry(LogLevel.Trace);
// Split ca request id into order and cert id
@@ -1037,40 +1257,60 @@ private List GetAllConnectorCertsForOrder(string caReque
CertCentralClient client = CertCentralClientUtilities.BuildCertCentralClient(_config);
ViewCertificateOrderResponse orderResponse = client.ViewCertificateOrder(new ViewCertificateOrderRequest((uint)orderId));
+ if (caFilterIds != null && caFilterIds.Count > 0 && !caFilterIds.Contains(orderResponse.certificate.ca_cert.Id.ToUpper()))
+ {
+ _logger.LogTrace($"Found order ID {orderId} that does not match SyncCAFilter. CA ID: {orderResponse.certificate.ca_cert.Id} Skipping...");
+ return null;
+ }
+
var orderCerts = GetAllCertsForOrder(orderId);
- List certList = new List();
+ List certList = new List();
foreach (var cert in orderCerts)
{
- string certificate = null;
- string caReqId = cert.order_id + "-" + cert.certificate_id;
- int status = GetCertificateStatusFromCA(cert.status, orderId);
- if (status == (int)RequestDisposition.ISSUED || status == (int)RequestDisposition.REVOKED || status == (int)RequestDisposition.UNKNOWN)
+ try
{
- // We have a status where there may be a cert to download, try to download it
- CertificateChainResponse certificateChainResponse = client.GetCertificateChain(new CertificateChainRequest(certId));
- if (certificateChainResponse.Status == CertCentralBaseResponse.StatusType.SUCCESS)
+ string certificate = null;
+ string caReqId = cert.order_id + "-" + cert.certificate_id;
+ int status = GetCertificateStatusFromCA(cert.status, orderId);
+ if (status == (int)EndEntityStatus.GENERATED || status == (int)EndEntityStatus.REVOKED)
{
- certificate = certificateChainResponse.Intermediates[0].PEM;
+ // We have a status where there may be a cert to download, try to download it
+ CertificateChainResponse certificateChainResponse = client.GetCertificateChain(new CertificateChainRequest(certId));
+ if (certificateChainResponse.Status == CertCentralBaseResponse.StatusType.SUCCESS)
+ {
+ certificate = certificateChainResponse.Intermediates[0].PEM;
+ }
+ else
+ {
+ throw new Exception($"Unexpected error downloading certificate {certId} for order {orderId}: {certificateChainResponse.Errors.FirstOrDefault()?.message}");
+ }
}
- else
+ var connCert = new AnyCAPluginCertificate
{
- _logger.LogWarning($"Unexpected error downloading certificate {certId} for order {orderId}: {certificateChainResponse.Errors.FirstOrDefault()?.message}");
- }
+ CARequestID = caReqId,
+ Certificate = certificate,
+ Status = status,
+ ProductID = orderResponse.product.name_id,
+ RevocationDate = GetRevocationDate(orderResponse)
+ };
+ certList.Add(connCert);
}
- var connCert = new CAConnectorCertificate
+ catch (Exception ex)
{
- CARequestID = caReqId,
- Certificate = certificate,
- Status = status,
- ProductID = orderResponse.product.name_id,
- RevocationDate = GetRevocationDate(orderResponse)
- };
- certList.Add(connCert);
+ _logger.LogWarning($"Error processing cert {cert.order_id}-{cert.certificate_id}: {ex.Message}. Skipping record.");
+ }
}
return certList;
}
+
+ ///
+ /// Get all of the certs for a given order, including reissues and duplicates, in StatusOrder form
+ ///
+ ///
+ ///
+ ///
private List GetAllCertsForOrder(int orderId)
{
CertCentralClient client = CertCentralClientUtilities.BuildCertCentralClient(_config);
@@ -1098,10 +1338,13 @@ private List GetAllCertsForOrder(int orderId)
{
order_id = orderId,
certificate_id = orderResponse.certificate.id,
- status = orderStatusString
+ status = orderStatusString,
+ serialNum = orderResponse.certificate.serial_number
+ };
+ List orderCerts = new List
+ {
+ primary
};
- List orderCerts = new List();
- orderCerts.Add(primary);
if (reissueCerts?.Count > 0)
{
orderCerts.AddRange(reissueCerts);
@@ -1110,7 +1353,33 @@ private List GetAllCertsForOrder(int orderId)
{
orderCerts.AddRange(dupeCerts);
}
- return orderCerts;
+ List retCerts = new List();
+ List reqIds = new List();
+ List serNums = new List();
+ foreach (var cert in orderCerts)
+ {
+ string req = $"{cert.order_id}-{cert.certificate_id}";
+
+ // Listing reissues/duplicates can also return the primary certificate. This check insures that only one copy of the primary certificate gets added to the sync list.
+ if (!reqIds.Contains(req))
+ {
+ // This is actually caused by an issue in the DigiCert API. For some orders (but not all), retrieving the reissued/duplicate certificates on an order
+ // instead just retrieves multiple copies of the primary certificate on that order. Since the gateway database must have unique certificates
+ // (serial number column is unique), we work around this by only syncing the primary cert in these cases. Other orders that correctly retrieve the
+ // reissued/duplicate certificates will pass this check.
+ if (!serNums.Contains(req))
+ {
+ reqIds.Add(req);
+ retCerts.Add(cert);
+ serNums.Add(cert.serialNum);
+ }
+ else
+ {
+ _logger.LogWarning($"Duplicate certificate serial numbers found. Only one will be synced. Order ID: {cert.order_id}");
+ }
+ }
+ }
+ return retCerts;
}
}
}
\ No newline at end of file
diff --git a/digicert-certcentral-anycagateway/CertCentralConfig.cs b/digicert-certcentral-caplugin/CertCentralConfig.cs
similarity index 51%
rename from digicert-certcentral-anycagateway/CertCentralConfig.cs
rename to digicert-certcentral-caplugin/CertCentralConfig.cs
index b29af9e..ed0320f 100644
--- a/digicert-certcentral-anycagateway/CertCentralConfig.cs
+++ b/digicert-certcentral-caplugin/CertCentralConfig.cs
@@ -4,13 +4,22 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert
+namespace Keyfactor.Extensions.CAPlugin.DigiCert
{
public class CertCentralConfig
{
+
+ public CertCentralConfig()
+ {
+ SyncCAFilter = new List();
+ }
public string APIKey { get; set; }
public string Region { get; set; } = "US";
public int? DivisionId { get; set; }
public bool? RevokeCertificateOnly { get; set; }
+ public bool Enabled { get; set; } = true;
+ public List SyncCAFilter { get; set; }
+ public bool? FilterExpiredOrders { get; set; }
+ public int? SyncExpirationDays { get; set; }
}
}
diff --git a/digicert-certcentral-anycagateway/Client/CertCentralClient.cs b/digicert-certcentral-caplugin/Client/CertCentralClient.cs
similarity index 98%
rename from digicert-certcentral-anycagateway/Client/CertCentralClient.cs
rename to digicert-certcentral-caplugin/Client/CertCentralClient.cs
index e02a790..c700a3c 100644
--- a/digicert-certcentral-anycagateway/Client/CertCentralClient.cs
+++ b/digicert-certcentral-caplugin/Client/CertCentralClient.cs
@@ -1,5 +1,5 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.API;
-using Keyfactor.Extensions.CAGateway.DigiCert.Models;
+using Keyfactor.Extensions.CAPlugin.DigiCert.API;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Models;
using Keyfactor.Logging;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
@@ -14,7 +14,7 @@
using static Keyfactor.PKI.X509.X509Utilities;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Client
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Client
{
public class CertCentralCredentials
{
@@ -473,7 +473,7 @@ public DownloadCertificateByFormatResponse DownloadCertificateByFormat(DownloadC
return dlCertificateRequestResponse;
}
- public ListCertificateOrdersResponse ListAllCertificateOrders()
+ public ListCertificateOrdersResponse ListAllCertificateOrders(bool ignoreExpired = false, int expiredWindow = 0)
{
int batch = 1000;
ListCertificateOrdersResponse totalResponse = new ListCertificateOrdersResponse();
@@ -483,7 +483,9 @@ public ListCertificateOrdersResponse ListAllCertificateOrders()
ListCertificateOrdersRequest request = new ListCertificateOrdersRequest()
{
limit = batch,
- offset = totalResponse.orders.Count
+ offset = totalResponse.orders.Count,
+ ignoreExpired = ignoreExpired,
+ expiredWindow = expiredWindow
};
CertCentralResponse response = Request(request, request.BuildParameters());
diff --git a/digicert-certcentral-anycagateway/Client/CertCentralClientUtilities.cs b/digicert-certcentral-caplugin/Client/CertCentralClientUtilities.cs
similarity index 93%
rename from digicert-certcentral-anycagateway/Client/CertCentralClientUtilities.cs
rename to digicert-certcentral-caplugin/Client/CertCentralClientUtilities.cs
index ab62f24..597bd0b 100644
--- a/digicert-certcentral-anycagateway/Client/CertCentralClientUtilities.cs
+++ b/digicert-certcentral-caplugin/Client/CertCentralClientUtilities.cs
@@ -7,7 +7,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Client
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Client
{
///
/// Static class containing some utility methods for the cert central client.
@@ -17,7 +17,7 @@ public static class CertCentralClientUtilities
///
/// Private instance of the logger.
///
- private static ILogger Logger => LogHandler.GetClassLogger();
+ private static ILogger Logger => LogHandler.GetClassLogger();
///
/// Uses the to build a DigiCert client.
diff --git a/digicert-certcentral-anycagateway/Client/Conversions.cs b/digicert-certcentral-caplugin/Client/Conversions.cs
similarity index 97%
rename from digicert-certcentral-anycagateway/Client/Conversions.cs
rename to digicert-certcentral-caplugin/Client/Conversions.cs
index 2d9cbe8..a53c0e9 100644
--- a/digicert-certcentral-anycagateway/Client/Conversions.cs
+++ b/digicert-certcentral-caplugin/Client/Conversions.cs
@@ -10,7 +10,7 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Client
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Client
{
public class Conversions
{
diff --git a/digicert-certcentral-anycagateway/Constants.cs b/digicert-certcentral-caplugin/Constants.cs
similarity index 91%
rename from digicert-certcentral-anycagateway/Constants.cs
rename to digicert-certcentral-caplugin/Constants.cs
index 9e7a527..44aa10b 100644
--- a/digicert-certcentral-anycagateway/Constants.cs
+++ b/digicert-certcentral-caplugin/Constants.cs
@@ -4,7 +4,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert
+namespace Keyfactor.Extensions.CAPlugin.DigiCert
{
public class Constants
{
@@ -26,6 +26,7 @@ public class Config
public const string CA_CERT_ID = "CACertId";
public const string RENEWAL_WINDOW = "RenewalWindowDays";
public const string REVOKE_CERT = "RevokeCertificateOnly";
+ public const string ENABLED = "Enabled";
}
public class RequestAttributes
diff --git a/digicert-certcentral-anycagateway/Models/CertCentralCertType.cs b/digicert-certcentral-caplugin/Models/CertCentralCertType.cs
similarity index 96%
rename from digicert-certcentral-anycagateway/Models/CertCentralCertType.cs
rename to digicert-certcentral-caplugin/Models/CertCentralCertType.cs
index f804aa5..ce3882d 100644
--- a/digicert-certcentral-anycagateway/Models/CertCentralCertType.cs
+++ b/digicert-certcentral-caplugin/Models/CertCentralCertType.cs
@@ -1,5 +1,5 @@
-using Keyfactor.Extensions.CAGateway.DigiCert.API;
-using Keyfactor.Extensions.CAGateway.DigiCert.Client;
+using Keyfactor.Extensions.CAPlugin.DigiCert.API;
+using Keyfactor.Extensions.CAPlugin.DigiCert.Client;
using Keyfactor.Logging;
using Microsoft.Extensions.Logging;
@@ -9,7 +9,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Models
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Models
{
public class CertCentralCertType
{
diff --git a/digicert-certcentral-anycagateway/Models/CertificateOrder.cs b/digicert-certcentral-caplugin/Models/CertificateOrder.cs
similarity index 96%
rename from digicert-certcentral-anycagateway/Models/CertificateOrder.cs
rename to digicert-certcentral-caplugin/Models/CertificateOrder.cs
index 6cd0569..a06f04c 100644
--- a/digicert-certcentral-anycagateway/Models/CertificateOrder.cs
+++ b/digicert-certcentral-caplugin/Models/CertificateOrder.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Models
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Models
{
public class CertificateOrder
{
diff --git a/digicert-certcentral-anycagateway/Models/Contact.cs b/digicert-certcentral-caplugin/Models/Contact.cs
similarity index 91%
rename from digicert-certcentral-anycagateway/Models/Contact.cs
rename to digicert-certcentral-caplugin/Models/Contact.cs
index 3106b92..82601b0 100644
--- a/digicert-certcentral-anycagateway/Models/Contact.cs
+++ b/digicert-certcentral-caplugin/Models/Contact.cs
@@ -7,7 +7,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Models
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Models
{
public class User : Contact
{
diff --git a/digicert-certcentral-anycagateway/Models/Error.cs b/digicert-certcentral-caplugin/Models/Error.cs
similarity index 88%
rename from digicert-certcentral-anycagateway/Models/Error.cs
rename to digicert-certcentral-caplugin/Models/Error.cs
index 1ebf365..e963096 100644
--- a/digicert-certcentral-anycagateway/Models/Error.cs
+++ b/digicert-certcentral-caplugin/Models/Error.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Models
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Models
{
public class Error
{
diff --git a/digicert-certcentral-anycagateway/Models/IdInformation.cs b/digicert-certcentral-caplugin/Models/IdInformation.cs
similarity index 87%
rename from digicert-certcentral-anycagateway/Models/IdInformation.cs
rename to digicert-certcentral-caplugin/Models/IdInformation.cs
index 90d91fc..506aff3 100644
--- a/digicert-certcentral-anycagateway/Models/IdInformation.cs
+++ b/digicert-certcentral-caplugin/Models/IdInformation.cs
@@ -7,7 +7,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Models
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Models
{
public class IdInformation
{
diff --git a/digicert-certcentral-anycagateway/Models/Product.cs b/digicert-certcentral-caplugin/Models/Product.cs
similarity index 91%
rename from digicert-certcentral-anycagateway/Models/Product.cs
rename to digicert-certcentral-caplugin/Models/Product.cs
index 901049c..d866b33 100644
--- a/digicert-certcentral-anycagateway/Models/Product.cs
+++ b/digicert-certcentral-caplugin/Models/Product.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Models
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Models
{
public class Product
{
diff --git a/digicert-certcentral-anycagateway/Models/RequestSummary.cs b/digicert-certcentral-caplugin/Models/RequestSummary.cs
similarity index 90%
rename from digicert-certcentral-anycagateway/Models/RequestSummary.cs
rename to digicert-certcentral-caplugin/Models/RequestSummary.cs
index aafc83e..1c015ba 100644
--- a/digicert-certcentral-anycagateway/Models/RequestSummary.cs
+++ b/digicert-certcentral-caplugin/Models/RequestSummary.cs
@@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert.Models
+namespace Keyfactor.Extensions.CAPlugin.DigiCert.Models
{
public class RequestSummary
{
diff --git a/digicert-certcentral-anycagateway/Utilities.cs b/digicert-certcentral-caplugin/Utilities.cs
similarity index 98%
rename from digicert-certcentral-anycagateway/Utilities.cs
rename to digicert-certcentral-caplugin/Utilities.cs
index a7e6f0f..ef6a598 100644
--- a/digicert-certcentral-anycagateway/Utilities.cs
+++ b/digicert-certcentral-caplugin/Utilities.cs
@@ -5,7 +5,7 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
-namespace Keyfactor.Extensions.CAGateway.DigiCert
+namespace Keyfactor.Extensions.CAPlugin.DigiCert
{
public static class Utilities
{
diff --git a/digicert-certcentral-caplugin/digicert-certcentral-caplugin.csproj b/digicert-certcentral-caplugin/digicert-certcentral-caplugin.csproj
new file mode 100644
index 0000000..c09a0b8
--- /dev/null
+++ b/digicert-certcentral-caplugin/digicert-certcentral-caplugin.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net6.0
+ Keyfactor.Extensions.CAPlugin.DigiCert
+ enable
+ disable
+ DigicertCAPlugin
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration-manifest.json b/integration-manifest.json
index e0ca91a..62f97bf 100644
--- a/integration-manifest.json
+++ b/integration-manifest.json
@@ -1,12 +1,12 @@
{
"$schema": "https://keyfactor.github.io/integration-manifest-schema.json",
- "integration_type": "ca-gateway",
- "name": "digicert-certcentral-anycagateway",
- "status": "prototype",
- "support_level": "community",
- "link_github": false,
- "update_catalog": false,
- "description": "DigiCert CertCentral plugin for the AnyCA Gateway framework",
- "gateway_framework": "1.0.0",
- "release_dir": "digicert-certcentral-anycagateway/bin/Release/net6.0"
+ "integration_type": "anyca-plugin",
+ "name": "DigiCert CertCentral AnyCA REST Gateway Plugin",
+ "status": "production",
+ "support_level": "kf-supported",
+ "link_github": true,
+ "update_catalog": true,
+ "description": "DigiCert CertCentral plugin for the AnyCA REST Gateway framework",
+ "gateway_framework": "24.2.0",
+ "release_dir": "digicert-certcentral-caplugin/bin/Release/net6.0"
}
diff --git a/readme_source.md b/readme_source.md
index 101b084..f436883 100644
--- a/readme_source.md
+++ b/readme_source.md
@@ -1,28 +1,61 @@
+
# Introduction
-This AnyGateway plug-in enables issuance, revocation, and synchronization of certificates from DigiCert's CertCentral offering.
+
+This AnyCA REST Gateway plug-in enables issuance, revocation, and synchronization of certificates from DigiCert's CertCentral offering.
# Prerequisites
-## Certificate Chain
+## Prerequisite: Certificate Chain
-In order to enroll for certificates the Keyfactor Command server must trust the trust chain. Once you create your Root and/or Subordinate CA, make sure to import the certificate chain into the AnyGateway and Command Server certificate store
+In order to request certificates from the Keyfactor AnyGateway, the Keyfactor Command server must trust the certificate chain of trust. To ensure trust is established, download your Root and/or Subordinate CA certificates from DigiCert and import them into the appropriate local certificate stores on the Keyfactor AnyGateway and Command servers. More information can be found in the [AnyCA Gateway REST Install Guide](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/Preparing.htm)
+## Installation
+1. Download latest successful build from [GitHub Releases](../../releases/latest)
-# Install
-* Download latest successful build from [GitHub Releases](../../releases/latest)
+2. Extract the .zip file, and from it, copy DigicertCAPlugin.dll and DigicertCAPlugin.deps.json to the 'C:\Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions' directory
-* Copy DigiCertCAGateway.dll and DigiCertCAGateway.deps.json to the Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions directory
+3. Within the 'C:\Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions\Connectors' folder, update the manifest.json file to contain the following:
-* Update the manifest.json file located in Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions\Connectors
- * If the manifest.json file or the Connectors folder do not exist, create them.
- ```json
+```json
{
- "extensions": {
- "Keyfactor.AnyGateway.Extensions.ICAConnector": {
- "DigiCertCAConnector": {
- "assemblypath": "../DigiCertCAGateway.dll",
- "TypeFullName": "Keyfactor.Extensions.CAGateway.DigiCert.CertCentralCAConnector"
- }
- }
- }
+ "extensions": {
+ "Keyfactor.AnyGateway.Extensions.IAnyCAPlugin": {
+ "CertCentralCAPlugin": {
+ "assemblypath": "../DigicertCAPlugin.dll",
+ "TypeFullName": "Keyfactor.Extensions.CAPlugin.DigiCert.CertCentralCAPlugin"
+ }
+ }
+ }
}
- ```
+```
+
+NOTE: If the Connectors folder and/or the manifest.json file do not exist, they must be manually created
+
+4. Restart the AnyCA Gateway service
+
+5. Navigate to the AnyCA Gateway REST portal and verify that the Gateway recognizes the DigiCert plugin by hovering over the ⓘ symbol to the right of the Gateway on the top left of the portal. CAPlugin Type should now be listed as CertCentralCA.
+
+
+## Configuration
+
+1. Follow the [official Keyfactor AnyCA Gateway REST documentation](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCA-Gateway.htm#Add_or_Edit_a_Certificate_Authority) to define a new Certificate Authority, using the following information to configure the CA Connection section:
+
+SETTING | REQUIRED? | DESCRIPTION
+--|--|--
+Enabled | Yes | Enables the DigiCert gateway functionality. Should almost always be set to 'true'
+APIKey | Yes | The API key the Gateway should use to communicate with the DigiCert API. Can be generated from the DigiCert portal.
+Region | No | The geographic region associated with your DigiCert account. Valid values are US and EU. Default if not provided is US.
+DivisionId | No | If your CertCentral account has multiple divisions AND uses any custom per-division product settings, provide a division ID for the gateway to use for product type lookups.
+RevokeCertificateOnly | No | If set to 'true', revoke operations will only revoke the individual certificate in question rather than the entire DigiCert order. Default if not provided is 'false'.
+SyncCAFilter | No | If you list one or more DigiCert issuing CA IDs here (comma-separated if more than one), the sync process will only return certs issued by one of those CAs. Leave this option empty to sync all certs from all CAs.
+FilterExpiredOrders | No | If set to 'true', syncing will not return certs that are expired more than a specified number of days. The number of days is specified by the SyncExpirationDays config option. Default value is 'false'.
+SyncExpirationDays | No | Only used if FilterExpiredOrders is 'true', otherwise ignored. Sets the number of days a cert has to be expired for the sync process to no longer sync it. For example, a value of 30 means sync will continue to return certs that have expired within the past 30 days, but not ones older than that. Default value is 0, meaning sync would not return any certs expired before the current day.
+
+2. After saving the CA configuration, Follow the [official AnyCA Gateway REST documentation](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCP-Gateway.htm#Certificate_Profile) to define one or more Certificate Profiles.
+3. Edit your newly configured CA, and you should now be able to modify the Templates tab. You need at least one template for each product type you wish to be able to enroll for. It is recommended to include the product type in the template name to make them easier to identify. Use the following information to configure the parameters for each template:
+
+SETTING | REQUIRED? | DESCRIPTION
+--|--|--
+LifetimeDays | No | The number of days of validity to use when requesting certs. Default if not provided is 365. NOTE FOR RENEWALS: If the value of LifetimeDays is evenly divisible by 365, the expiration day and month of the new cert will be set to the same values as the old cert if possible, to avoid renewal date drift.
+CACertId | No | The ID of the issuing CA to be used by DigiCert. If not specified, the default for your account will be used.
+Organization-Name | No | If specified, this value will override any organization name provided in the subject of the cert request on enrollment. Useful for requests (such as ACME) that contain no subject.
+RenewalWindowDays | No | The number of days from expiration that the gateway should do a reissue rather than a renewal. Default if not provided is 90, meaning any renewal request for certs that expire in more than 90 days will be treated as a reissue request.