diff --git a/.github/workflows/keyfactor-starter-workflow.yml b/.github/workflows/keyfactor-starter-workflow.yml
new file mode 100644
index 0000000..7b909a3
--- /dev/null
+++ b/.github/workflows/keyfactor-starter-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@v4
+ secrets:
+ token: ${{ secrets.V2BUILDTOKEN}}
+ gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }}
+ gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }}
+ scan_token: ${{ secrets.SAST_TOKEN }}
diff --git a/.gitignore b/.gitignore
index ce89292..8791d97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -416,3 +416,5 @@ FodyWeavers.xsd
*.msix
*.msm
*.msp
+.claude/settings.local.json
+.claude/settings.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..1470e43
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,22 @@
+# v2.0.0
+* Converted from AnyCA Gateway (DB) to AnyCA Gateway REST plugin architecture
+* Migrated from CAProxy.AnyGateway (BaseCAConnector) to IAnyCAPlugin interface
+* Fully async operations throughout (no more Task.Run().Result blocking)
+* Self-describing plugin configuration with annotations (no external template JSON files)
+* Built-in product registry with 80+ certificate products
+* Smart renewal vs. reissue logic with configurable renewal window
+* Uses CustomOrderId for stable order tracking
+* End-entity certificate extraction using X509Utilities.ExtractEndEntityCertificateContents
+* GetSingleRecord now downloads and returns the actual certificate
+* Connection validation with required field checks
+* Enable/disable toggle for CA connector lifecycle management
+* Removed Keyfactor API client dependency (no more direct template updates)
+
+# v1.1.1
+* SSL Store Api Changed Encoding Rules, needed to fix integration to match
+
+# v1.1.0
+* Added new AutoWWW field for single domain SSL Store products
+
+# v1.0.4
+* Original Release Version
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e4e1873
--- /dev/null
+++ b/README.md
@@ -0,0 +1,471 @@
+
+ SSL Store AnyCA Gateway REST Plugin
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Support
+
+ ·
+
+ Requirements
+
+ ·
+
+ Installation
+
+ ·
+
+ License
+
+ ·
+
+ Related Integrations
+
+
+
+
+The SSL Store AnyCA Gateway REST plugin extends the capabilities of the SSL Store Certificate Authority Service to Keyfactor Command via the Keyfactor AnyCA Gateway. SSL Store is a certificate reseller providing access to 80+ certificate products from vendors including DigiCert, Sectigo, RapidSSL, GeoTrust, and Comodo through a single REST API. The plugin represents a fully featured AnyCA Plugin with the following capabilities:
+
+* **CA Sync**:
+ * Download all certificates issued through SSL Store
+ * Full synchronization of all orders with paginated retrieval
+ * Automatic extraction of end-entity certificates from certificate chains
+ * Resilient retry logic (up to 5 retries) for large certificate inventories
+* **Certificate Enrollment**:
+ * Support for new certificate enrollment with CSR
+ * Intelligent renewal vs. reissue logic based on configurable renewal window
+ * Support for DV, OV, and EV certificate products
+ * Multi-domain (MDC/SAN) and wildcard certificate support
+ * Automatic domain validation with approver email verification
+ * 80+ pre-configured certificate products across DigiCert and Sectigo families
+* **Certificate Revocation**:
+ * Request revocation of previously issued certificates via SSL Store refund request API
+
+## Compatibility
+
+The SSL Store AnyCA Gateway REST plugin is compatible with the Keyfactor AnyCA Gateway REST 25.5 and later.
+
+## Support
+The SSL Store AnyCA Gateway REST plugin is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket with your Keyfactor representative. 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.
+
+## Requirements
+
+### SSL Store System Prerequisites
+
+Before configuring the AnyCA Gateway plugin, ensure the following prerequisites are met:
+
+1. **SSL Store Account**:
+ - Active SSL Store partner account with API access enabled
+ - Access to the SSL Store web-based API (WBAPI)
+ - SSL Store account configured and operational
+
+2. **API Credentials**:
+ - SSL Store Partner Code
+ - SSL Store Authentication Token
+ - These credentials must have permissions for:
+ - Certificate enrollment (new order submission)
+ - Certificate download
+ - Certificate revocation (refund request)
+ - Order query and status retrieval
+ - Email approver list retrieval
+
+3. **Network Connectivity**:
+ - Gateway server must have HTTPS access to the SSL Store API endpoint
+ - Production endpoint: `https://wbapi.thesslstore.com`
+ - Sandbox endpoint: `https://sandbox-wbapi.thesslstore.com`
+ - TLS 1.2 or higher must be supported
+
+### Obtaining Required Configuration Information
+
+#### 1. SSL Store Base URL
+
+The SSL Store Base URL is the root endpoint for the SSL Store REST API.
+
+**Available environments:**
+- Production: `https://wbapi.thesslstore.com`
+- Sandbox/Testing: `https://sandbox-wbapi.thesslstore.com`
+
+**To obtain your Base URL:**
+1. Log in to your SSL Store partner portal
+2. Determine whether you are using the production or sandbox environment
+3. Verify the URL is accessible from the Gateway server
+
+#### 2. API Authentication Credentials
+
+The Gateway authenticates to SSL Store using a Partner Code and Authentication Token.
+
+**Steps to obtain API credentials:**
+
+1. **Access SSL Store Partner Portal**:
+ - Log in to your SSL Store partner account
+ - Navigate to API settings
+
+2. **Obtain Credentials**:
+ - **Partner Code**: Your unique partner identifier assigned by SSL Store
+ - **Authentication Token**: A secret token for API authentication
+ - Store these credentials securely
+
+3. **Verify Permissions**:
+ - Ensure the API credentials have permissions for:
+ - Order creation (`/rest/order/neworder`)
+ - Order reissue (`/rest/order/reissue`)
+ - Order query (`/rest/order/query`)
+ - Order status (`/rest/order/status`)
+ - Certificate download (`/rest/order/download`)
+ - Revocation/refund (`/rest/order/refundrequest`)
+ - Email approver list (`/rest/order/approverlist`)
+
+#### 3. Supported Certificate Products
+
+The plugin supports 80+ certificate products from multiple vendors. Products are organized by validation type and vendor:
+
+**DigiCert Products:**
+
+| Product Code | Description | Validation |
+|-------------|-------------|------------|
+| `digi_securesite_flex` | DigiCert Secure Site | OV |
+| `digi_securesite_flex-EO` | DigiCert Secure Site (Enterprise Org) | OV |
+| `digi_securesite_ev_flex` | DigiCert Secure Site EV | EV |
+| `digi_securesite_ev_flex-EO` | DigiCert Secure Site EV (Enterprise Org) | EV |
+| `digi_securesite_pro_flex` | DigiCert Secure Site Pro | OV |
+| `digi_securesite_pro_flex-EO` | DigiCert Secure Site Pro (Enterprise Org) | OV |
+| `digi_securesite_pro_ev_flex` | DigiCert Secure Site Pro EV | EV |
+| `digi_securesite_pro_ev_flex-EO` | DigiCert Secure Site Pro EV (Enterprise Org) | EV |
+| `digi_sslwebserver_flex` | DigiCert SSL Web Server | OV |
+| `digi_sslwebserver_flex-EO` | DigiCert SSL Web Server (Enterprise Org) | OV |
+| `digi_sslwebserver_ev_flex` | DigiCert SSL Web Server EV | EV |
+| `digi_sslwebserver_ev_flex-EO` | DigiCert SSL Web Server EV (Enterprise Org) | EV |
+| `digi_truebizid_flex` | DigiCert TrueBizID | OV |
+| `digi_truebizid_flex-EO` | DigiCert TrueBizID (Enterprise Org) | OV |
+| `digi_truebizid_ev_flex` | DigiCert TrueBizID EV | EV |
+| `digi_truebizid_ev_flex-EO` | DigiCert TrueBizID EV (Enterprise Org) | EV |
+| `digi_ssl_basic` | DigiCert Basic SSL | OV |
+| `digi_ssl_basic-EO` | DigiCert Basic SSL (Enterprise Org) | OV |
+| `digi_ssl_ev_basic` | DigiCert Basic SSL EV | EV |
+| `digi_ssl_ev_basic-EO` | DigiCert Basic SSL EV (Enterprise Org) | EV |
+| `digi_rapidssl` | RapidSSL | DV |
+| `digi_rapidssl_wc` | RapidSSL Wildcard | DV |
+| `digi_ssl_dv_geotrust_flex` | GeoTrust DV SSL | DV |
+| `digi_ssl123_flex` | GeoTrust SSL123 | DV |
+| `digi_quickssl_md` | DigiCert QuickSSL Multi-Domain | DV |
+| `digi_client_premium` | DigiCert Client Premium | Client |
+| `digi_csc` | DigiCert Code Signing | Code Signing |
+| `digi_csc_ev` | DigiCert EV Code Signing | EV Code Signing |
+| `digi_doc_signing_ind_500` | DigiCert Document Signing Individual 500 | Document Signing |
+| `digi_doc_signing_ind_2000` | DigiCert Document Signing Individual 2000 | Document Signing |
+| `digi_doc_signing_org_2000` | DigiCert Document Signing Organization 2000 | Document Signing |
+| `digi_doc_signing_org_5000` | DigiCert Document Signing Organization 5000 | Document Signing |
+
+**Sectigo/Comodo Products:**
+
+| Product Code | Description | Validation |
+|-------------|-------------|------------|
+| `positivessl` | Positive SSL | DV |
+| `positivesslwildcard` | Positive SSL Wildcard | DV |
+| `positivemdcssl` | Positive SSL Multi-Domain | DV |
+| `positivemdcwildcard` | Positive SSL MDC Wildcard | DV |
+| `positiveevssl` | Positive EV SSL | EV |
+| `positiveevmdc` | Positive EV Multi-Domain | EV |
+| `sectigossl` | Sectigo SSL | DV |
+| `sectigowildcard` | Sectigo Wildcard | DV |
+| `sectigoovssl` | Sectigo OV SSL | OV |
+| `sectigoovwildcard` | Sectigo OV Wildcard | OV |
+| `sectigoevssl` | Sectigo EV SSL | EV |
+| `sectigodvucc` | Sectigo DV UCC | DV |
+| `sectigouccwildcard` | Sectigo UCC Wildcard | DV |
+| `sectigomdc` | Sectigo Multi-Domain | OV |
+| `sectigomdcwildcard` | Sectigo MDC Wildcard | OV |
+| `sectigoevmdc` | Sectigo EV Multi-Domain | EV |
+| `comodopremiumssl` | Comodo Premium SSL | OV |
+| `comodopremiumwildcard` | Comodo Premium Wildcard | OV |
+| `comodossl` | Comodo SSL | OV |
+| `comodoevssl` | Comodo EV SSL | EV |
+| `comodomdc` | Comodo Multi-Domain | OV |
+| `comodomdcwildcard` | Comodo MDC Wildcard | OV |
+| `comodoevmdc` | Comodo EV Multi-Domain | EV |
+| `comodoucc` | Comodo UCC | OV |
+| `comodouccwildcard` | Comodo UCC Wildcard | OV |
+| `comodowildcard` | Comodo Wildcard | OV |
+| `comodocsc` | Comodo Code Signing | Code Signing |
+| `comodoevcsc` | Comodo EV Code Signing | EV Code Signing |
+| `comododvucc` | Comodo DV UCC | DV |
+| `comodopciscan` | Comodo PCI Scan | Scanning |
+| `instantssl` | InstantSSL | OV |
+| `instantsslpro` | InstantSSL Pro | OV |
+| `enterprisepro` | Enterprise Pro SSL | OV |
+| `enterpriseprowc` | Enterprise Pro Wildcard | OV |
+| `enterpriseproev` | Enterprise Pro EV | EV |
+| `enterpriseproevmdc` | Enterprise Pro EV Multi-Domain | EV |
+| `enterprisessl` | Enterprise SSL | OV |
+| `essentialssl` | Essential SSL | DV |
+| `essentialwildcard` | Essential Wildcard | DV |
+| `elitessl` | Elite SSL | OV |
+
+**Note:** Products with the `-EO` suffix are Enterprise Organization variants that use a pre-configured DigiCert organization instead of requiring organization details during enrollment. These products require only a Validity Period and Organization ID.
+
+#### 4. Certificate Validity Configuration
+
+Certificate validity is specified in days during enrollment and automatically converted to months for the SSL Store API:
+
+| Days | Months |
+|------|--------|
+| 90 | 3 |
+| 180 | 6 |
+| 365 | 12 |
+| 730 | 24 |
+| 1095 | 36 |
+
+#### 5. Renewal vs. Reissue Logic
+
+The plugin uses a configurable **Renewal Window** (default: 30 days) to determine behavior during certificate renewal:
+
+- If the existing order is **within** the renewal window (i.e., expiring within N days), the plugin performs a **renewal** (new order linked to the original)
+- If the existing order is **outside** the renewal window (still has significant life remaining), the plugin performs a **reissue** on the same order
+
+## Installation
+
+1. Install the AnyCA Gateway REST per the [official Keyfactor documentation](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/InstallIntroduction.htm).
+
+2. On the server hosting the AnyCA Gateway REST, download and unzip the latest [SSL Store AnyCA Gateway REST plugin](https://github.com/Keyfactor/sslstore-caplugin/releases/latest) from GitHub.
+
+3. Copy the unzipped directory (usually called `net6.0` or `net8.0`) to the Extensions directory:
+
+
+ ```shell
+ Depending on your AnyCA Gateway REST version, copy the unzipped directory to one of the following locations:
+ Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions
+ Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net8.0\Extensions
+ ```
+
+ > The directory containing the SSL Store AnyCA Gateway REST plugin DLLs (`net6.0` or `net8.0`) can be named anything, as long as it is unique within the `Extensions` directory.
+
+4. Restart the AnyCA Gateway REST service.
+
+5. Navigate to the AnyCA Gateway REST portal and verify that the Gateway recognizes the SSL Store plugin by hovering over the ⓘ symbol to the right of the Gateway on the top left of the portal.
+
+## Configuration
+
+1. Follow the [official AnyCA Gateway REST documentation](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCA-Gateway.htm) to define a new Certificate Authority, and use the notes below to configure the **Gateway Registration** and **CA Connection** tabs:
+
+ * **Gateway Registration**
+
+ ### CA Connection Configuration
+
+ When registering the SSL Store CA in the AnyCA Gateway, you'll need to provide the following configuration parameters:
+
+ | Parameter | Description | Required | Default |
+ |-----------|-------------|----------|---------|
+ | **SSLStoreURL** | Full URL to the SSL Store API endpoint | Yes | `https://sandbox-wbapi.thesslstore.com` |
+ | **PartnerCode** | Partner Code obtained from SSL Store | Yes | |
+ | **AuthToken** | Authentication Token obtained from SSL Store | Yes | |
+ | **PageSize** | Number of records per page during synchronization | No | `100` |
+ | **Enabled** | Flag to Enable or Disable the CA connector | No | `true` |
+ | **RenewalWindow** | Days before order expiry to trigger renewal vs. reissue | No | `30` |
+
+ ### Gateway Registration Notes
+
+ - Each defined Certificate Authority in the AnyCA Gateway REST can support one SSL Store API endpoint
+ - If you have multiple SSL Store environments (production/sandbox), define separate Certificate Authorities for each
+ - Each CA configuration will manifest in Command as a separate CA entry
+ - The plugin uses REST API authentication with Partner Code and Authentication Token
+ - The plugin automatically handles:
+ - Product discovery (80+ products)
+ - Certificate status mapping (Active, Pending, Cancelled)
+ - End-entity certificate extraction from certificate chains
+ - Paginated order synchronization with retry logic
+
+ ### Security Considerations
+
+ 1. **Credential Storage**: The AuthToken field is configured as a secret/hidden field and should be stored securely
+ 2. **Network Security**: Ensure TLS/SSL is properly configured for all API communications
+ 3. **Least Privilege**: Request API credentials with minimal required permissions
+ 4. **Audit Logging**: Enable comprehensive logging in both the Gateway and SSL Store for security monitoring
+ 5. **Credential Rotation**: Regularly rotate API credentials according to your security policy
+ 6. **Sandbox Testing**: Use the sandbox endpoint (`https://sandbox-wbapi.thesslstore.com`) for initial configuration and testing before switching to production
+
+ ### CA Connection Fields
+
+ Populate using the configuration fields collected in the [requirements](#requirements) section.
+
+ * **SSLStoreURL** - The base URL for the SSL Store API endpoint. Use `https://wbapi.thesslstore.com` for production or `https://sandbox-wbapi.thesslstore.com` for testing.
+ * **PartnerCode** - The Partner Code obtained from your SSL Store partner account.
+ * **AuthToken** - The Authentication Token obtained from your SSL Store partner account.
+ * **PageSize** - Number of records to retrieve per page during certificate synchronization. Default is 100.
+ * **Enabled** - Flag to enable or disable the CA connector. Set to `true` to enable.
+ * **RenewalWindow** - Number of days before an order's expiration date to trigger a renewal (new order) instead of a reissue (same order). Default is 30 days.
+
+ * **CA Connection**
+
+ Populate using the configuration fields collected in the [requirements](#requirements) section.
+
+ * **SSLStoreURL** - The Base URL for the SSL Store API endpoint (e.g. https://sandbox-wbapi.thesslstore.com).
+ * **PartnerCode** - The Partner Code obtained from SSL Store.
+ * **AuthToken** - The Authentication Token obtained from SSL Store.
+ * **PageSize** - The number of records to return per page during synchronization.
+ * **Enabled** - Flag to Enable or Disable the CA connector.
+ * **RenewalWindow** - Number of days before order expiry to trigger a renewal instead of a reissue.
+
+2. ### Template (Product) Configuration
+
+ After adding the CA to the Gateway, certificate templates are automatically discovered from the plugin's built-in product registry. Each template may require different enrollment fields depending on the product type and validation level.
+
+ **Enrollment fields vary by product type. The following categories exist:**
+
+ #### DV Products (Minimal Fields)
+
+ Products like `positivessl`, `sectigossl`, `sectigowildcard`:
+
+ | Parameter | Description | Required |
+ |-----------|-------------|----------|
+ | **Admin Contact - Email** | Administrative contact email | Yes |
+ | **Approver Email** | Domain validation approver email | Yes |
+ | **Validity Period (In Days)** | Certificate validity in days | Yes |
+
+ #### OV Products (Organization Fields)
+
+ Products like `sectigoovssl`, `comodopremiumssl`, `instantssl`:
+
+ | Parameter | Description | Required |
+ |-----------|-------------|----------|
+ | **Admin Contact - Email** | Administrative contact email | Yes |
+ | **Approver Email** | Domain validation approver email | Yes |
+ | **Validity Period (In Days)** | Certificate validity in days | Yes |
+ | **Organization Name** | Organization name | Yes |
+ | **Organization Address** | Organization street address | Yes |
+ | **Organization State/Province** | Organization state or province | Yes |
+ | **Organization Postal Code** | Organization postal/zip code | Yes |
+ | **Organization Country** | Two-letter country code (e.g. US) | Yes |
+ | **Organization Phone** | Organization phone number | Yes |
+
+ #### DigiCert OV Flex Products
+
+ Products like `digi_securesite_flex`, `digi_sslwebserver_flex`, `digi_truebizid_flex`:
+
+ | Parameter | Description | Required |
+ |-----------|-------------|----------|
+ | **Admin Contact - First Name** | Administrative contact first name | Yes |
+ | **Admin Contact - Last Name** | Administrative contact last name | Yes |
+ | **Admin Contact - Phone** | Administrative contact phone | Yes |
+ | **Admin Contact - Email** | Administrative contact email | Yes |
+ | **Approver Email** | Domain validation approver email | Yes |
+ | **Validity Period (In Days)** | Certificate validity in days | Yes |
+ | **Organization Name** | Organization name | Yes |
+ | **Organization Address** | Organization street address | Yes |
+ | **Organization City** | Organization city | Yes |
+ | **Organization State/Province** | Organization state or province | Yes |
+ | **Organization Postal Code** | Organization postal/zip code | Yes |
+ | **Organization Country** | Two-letter country code | Yes |
+ | **Organization Phone** | Organization phone number | Yes |
+
+ #### DigiCert EV Flex Products
+
+ Products like `digi_securesite_ev_flex`, `digi_ssl_ev_basic`, `digi_truebizid_ev_flex`:
+
+ Same as DigiCert OV Flex, plus:
+
+ | Parameter | Description | Required |
+ |-----------|-------------|----------|
+ | **Admin Contact - Title** | Administrative contact job title | Yes |
+
+ #### Enterprise Organization (-EO) Products
+
+ Products like `digi_securesite_flex-EO`, `digi_sslwebserver_ev_flex-EO`:
+
+ | Parameter | Description | Required |
+ |-----------|-------------|----------|
+ | **Validity Period (In Days)** | Certificate validity in days | Yes |
+ | **Organization ID** | DigiCert Organization ID | Yes |
+
+ #### EV Products with Jurisdiction
+
+ Products like `enterpriseproev`, `positiveevssl`, `positiveevmdc`:
+
+ Same as OV Products, plus:
+
+ | Parameter | Description | Required |
+ |-----------|-------------|----------|
+ | **Organization Jurisdiction Country** | Jurisdiction country code for EV validation | Yes |
+
+ ### Domain Validation - Approver Emails
+
+ The plugin validates approver emails against SSL Store's approved list for each domain before enrollment:
+
+ - **DigiCert products**: Exactly one approver email is required and must be from the approved list
+ - **Sectigo/Comodo products**: At least one approver email must be from the approved list
+ - Emails are validated per-domain for multi-domain certificates
+
+ ### Important Notes
+
+ - Product IDs are automatically registered from the plugin's built-in product registry
+ - The `Validity Period (In Days)` is automatically converted to months for the SSL Store API
+ - For `-EO` (Enterprise Organization) products, the Organization ID dropdown is populated from your DigiCert account's active organizations
+ - DNS names (SANs) are extracted from the Keyfactor enrollment request; they do not need to be provided as a separate enrollment field
+ - The Common Name (CN) is extracted from the CSR subject
+
+3. Follow the [official Keyfactor documentation](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCA-Keyfactor.htm) to add each defined Certificate Authority to Keyfactor Command and import the newly defined Certificate Templates.
+
+4. In Keyfactor Command (v12.3+), for each imported Certificate Template, follow the [official documentation](https://software.keyfactor.com/Core-OnPrem/Current/Content/ReferenceGuide/Configuring%20Template%20Options.htm) to define enrollment fields for each of the following parameters:
+
+ * **Approver Email** - Comma-separated approver email address(es) for domain validation.
+ * **Validity Period (In Days)** - Certificate validity period in days (e.g. 90, 365, 730).
+ * **Admin Contact - First Name** - Administrative contact first name.
+ * **Admin Contact - Last Name** - Administrative contact last name.
+ * **Admin Contact - Phone** - Administrative contact phone number.
+ * **Admin Contact - Email** - Administrative contact email address.
+ * **Admin Contact - Title** - Administrative contact job title.
+ * **Admin Contact - Organization Name** - Administrative contact organization name.
+ * **Admin Contact - Address** - Administrative contact street address.
+ * **Admin Contact - City** - Administrative contact city.
+ * **Admin Contact - Region** - Administrative contact state/province/region.
+ * **Admin Contact - Postal Code** - Administrative contact postal/zip code.
+ * **Admin Contact - Country** - Administrative contact two-letter country code (e.g. US).
+ * **Technical Contact - First Name** - Technical contact first name.
+ * **Technical Contact - Last Name** - Technical contact last name.
+ * **Technical Contact - Phone** - Technical contact phone number.
+ * **Technical Contact - Email** - Technical contact email address.
+ * **Technical Contact - Organization Name** - Technical contact organization name.
+ * **Technical Contact - Address** - Technical contact street address.
+ * **Technical Contact - City** - Technical contact city.
+ * **Technical Contact - Region** - Technical contact state/province/region.
+ * **Technical Contact - Postal Code** - Technical contact postal/zip code.
+ * **Technical Contact - Country** - Technical contact two-letter country code (e.g. US).
+ * **Organization Name** - Organization name for the certificate.
+ * **Organization Address** - Organization street address.
+ * **Organization City** - Organization city.
+ * **Organization Region** - Organization state/province/region.
+ * **Organization State/Province** - Organization state or province.
+ * **Organization Postal Code** - Organization postal/zip code.
+ * **Organization Country** - Organization two-letter country code (e.g. US).
+ * **Organization Phone** - Organization phone number.
+ * **Organization Jurisdiction Country** - Jurisdiction country code for EV certificates.
+ * **Organization ID** - DigiCert organization ID for EO (Enterprise Organization) products.
+ * **Server Count** - Number of server licenses for the certificate.
+ * **Web Server Type** - Web server type (e.g. apacheopenssl, iis, tomcat, Other).
+ * **Signature Hash Algorithm** - Signature hash algorithm (PREFER_SHA2, REQUIRE_SHA2, PREFER_SHA1).
+ * **File Auth Domain Validation** - Use file-based domain validation (True/False).
+ * **CName Auth Domain Validation** - Use CNAME-based domain validation (True/False).
+ * **Is CU Order?** - Is this a CU (Customer) order (True/False).
+ * **Is Renewal Order?** - Is this a renewal order (True/False).
+ * **Is Trial Order?** - Is this a trial order (True/False).
+
+
+
+## License
+
+Apache License 2.0, see [LICENSE](LICENSE).
+
+## Related Integrations
+
+See all [Keyfactor Any CA Gateways (REST)](https://github.com/orgs/Keyfactor/repositories?q=anycagateway).
\ No newline at end of file
diff --git a/SslStoreCaProxy.slnx b/SslStoreCaProxy.slnx
new file mode 100644
index 0000000..ba17d13
--- /dev/null
+++ b/SslStoreCaProxy.slnx
@@ -0,0 +1,3 @@
+
+
+
diff --git a/SslStoreCaProxy/Client/Models/AddSan.cs b/SslStoreCaProxy/Client/Models/AddSan.cs
new file mode 100644
index 0000000..733d3ed
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/AddSan.cs
@@ -0,0 +1,10 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class AddSan : IAddSan
+ {
+ public string OldValue { get; set; }
+ public string NewValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/AdminContact.cs b/SslStoreCaProxy/Client/Models/AdminContact.cs
new file mode 100644
index 0000000..c0d6fa9
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/AdminContact.cs
@@ -0,0 +1,23 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class AdminContact : IAdminContact
+ {
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ public string SubjectFirstName { get; set; }
+ public string SubjectLastName { get; set; }
+ public string Phone { get; set; }
+ public string Fax { get; set; }
+ public string Email { get; set; }
+ public string Title { get; set; }
+ public string OrganizationName { get; set; }
+ public string AddressLine1 { get; set; }
+ public string AddressLine2 { get; set; }
+ public string City { get; set; }
+ public string Region { get; set; }
+ public string PostalCode { get; set; }
+ public string Country { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/AuthRequest.cs b/SslStoreCaProxy/Client/Models/AuthRequest.cs
new file mode 100644
index 0000000..2405499
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/AuthRequest.cs
@@ -0,0 +1,22 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class AuthRequest : IAuthRequest
+ {
+ public string PartnerCode { get; set; }
+ public string AuthToken { get; set; }
+ public string ReplayToken { get; set; }
+ public string UserAgent { get; set; }
+
+ [JsonProperty("TokenID")] public string TokenId { get; set; }
+
+ public string TokenCode { get; set; }
+
+ [JsonProperty("IPAddress")] public string IpAddress { get; set; }
+
+ public bool IsUsedForTokenSystem { get; set; }
+ public string Token { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/AuthResponse.cs b/SslStoreCaProxy/Client/Models/AuthResponse.cs
new file mode 100644
index 0000000..77a95be
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/AuthResponse.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class AuthResponse : IAuthResponse
+ {
+ [JsonProperty("isError")] public bool IsError { get; set; }
+
+ public List Message { get; set; }
+ public string Timestamp { get; set; }
+ public string ReplayToken { get; set; }
+ public string InvokingPartnerCode { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/AuthenticationStatus.cs b/SslStoreCaProxy/Client/Models/AuthenticationStatus.cs
new file mode 100644
index 0000000..8f4651c
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/AuthenticationStatus.cs
@@ -0,0 +1,11 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class AuthenticationStatus : IAuthenticationStatus
+ {
+ public string AuthenticationStep { get; set; }
+ public string Status { get; set; }
+ public string LastUpdated { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/Certificate.cs b/SslStoreCaProxy/Client/Models/Certificate.cs
new file mode 100644
index 0000000..1a99e81
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/Certificate.cs
@@ -0,0 +1,8 @@
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class Certificate
+ {
+ public string FileContent { get; set; }
+ public string FileName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/DeleteSan.cs b/SslStoreCaProxy/Client/Models/DeleteSan.cs
new file mode 100644
index 0000000..4bc6ff4
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/DeleteSan.cs
@@ -0,0 +1,8 @@
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class DeleteSan
+ {
+ public string OldValue { get; set; }
+ public string NewValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/DomainAuthVettingStatus.cs b/SslStoreCaProxy/Client/Models/DomainAuthVettingStatus.cs
new file mode 100644
index 0000000..8bb0335
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/DomainAuthVettingStatus.cs
@@ -0,0 +1,27 @@
+using System;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class DomainAuthVettingStatus : IDomainAuthVettingStatus
+ {
+ [JsonProperty("domain")] public string Domain { get; set; }
+
+ [JsonProperty("dcvMethod")] public string DcvMethod { get; set; }
+
+ [JsonProperty("dcvStatus")] public string DcvStatus { get; set; }
+
+ public string FileName { get; set; }
+ public string FileContents { get; set; }
+
+ [JsonProperty("DNSName")] public string DnsName { get; set; }
+
+ [JsonProperty("DNSEntry")] public string DnsEntry { get; set; }
+
+ public string PollStatus { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public DateTime LastPollDate { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/DownloadCertificateRequest.cs b/SslStoreCaProxy/Client/Models/DownloadCertificateRequest.cs
new file mode 100644
index 0000000..76b3439
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/DownloadCertificateRequest.cs
@@ -0,0 +1,12 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class DownloadCertificateRequest : IDownloadCertificateRequest
+ {
+ public AuthRequest AuthRequest { get; set; }
+ [JsonProperty("TheSSLStoreOrderID", NullValueHandling = NullValueHandling.Ignore)] public string TheSslStoreOrderId { get; set; }
+ [JsonProperty("CustomOrderID", NullValueHandling = NullValueHandling.Ignore)] public string CustomOrderId { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/DownloadCertificateResponse.cs b/SslStoreCaProxy/Client/Models/DownloadCertificateResponse.cs
new file mode 100644
index 0000000..de4093b
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/DownloadCertificateResponse.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class DownloadCertificateResponse : IDownloadCertificateResponse
+ {
+ public AuthResponse AuthResponse { get; set; }
+ public string CertificateEndDate { get; set; }
+
+ [JsonProperty("CertificateEndDateInUTC")]
+ public string CertificateEndDateInUtc { get; set; }
+
+ public string CertificateStartDate { get; set; }
+
+ [JsonProperty("CertificateStartDateInUTC")]
+ public string CertificateStartDateInUtc { get; set; }
+
+ public string CertificateStatus { get; set; }
+ public List Certificates { get; set; }
+ [JsonProperty("PartnerOrderID")] public string PartnerOrderId { get; set; }
+ [JsonProperty("TheSSLStoreOrderID")] public object TheSslStoreOrderId { get; set; }
+ public string ValidationStatus { get; set; }
+ [JsonProperty("VendorOrderID")] public object VendorOrderId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/EditSan.cs b/SslStoreCaProxy/Client/Models/EditSan.cs
new file mode 100644
index 0000000..184c01c
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/EditSan.cs
@@ -0,0 +1,8 @@
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class EditSan
+ {
+ public string OldValue { get; set; }
+ public string NewValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/EmailApproverRequest.cs b/SslStoreCaProxy/Client/Models/EmailApproverRequest.cs
new file mode 100644
index 0000000..98eeeae
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/EmailApproverRequest.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class EmailApproverRequest : IEmailApproverRequest
+ {
+ public AuthRequest AuthRequest { get; set; }
+ public string ProductCode { get; set; }
+ public string DomainName { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/EmailApproverResponse.cs b/SslStoreCaProxy/Client/Models/EmailApproverResponse.cs
new file mode 100644
index 0000000..4c48104
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/EmailApproverResponse.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class EmailApproverResponse : IEmailApproverResponse
+ {
+ public List ApproverEmailList { get; set; }
+ public AuthResponse AuthResponse { get; set; }
+ [JsonProperty("baseDomainName", NullValueHandling = NullValueHandling.Ignore)]
+ public string BaseDomainName { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/EnrollmentField.cs b/SslStoreCaProxy/Client/Models/EnrollmentField.cs
new file mode 100644
index 0000000..580b8a8
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/EnrollmentField.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class EnrollmentField : IEnrollmentField
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public List Options { get; set; } = new List();
+ public int DataType { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/NewOrderRequest.cs b/SslStoreCaProxy/Client/Models/NewOrderRequest.cs
new file mode 100644
index 0000000..7877e6d
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/NewOrderRequest.cs
@@ -0,0 +1,96 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class NewOrderRequest : INewOrderRequest
+ {
+ public AuthRequest AuthRequest { get; set; }
+
+ [JsonProperty("CustomOrderID", NullValueHandling = NullValueHandling.Ignore)]
+ public string CustomOrderId { get; set; }
+
+ public string ProductCode { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string ExtraProductCodes { get; set; }
+
+ public OrganizationInfo OrganizationInfo { get; set; }
+
+ [JsonProperty("TSSOrganizationId", NullValueHandling = NullValueHandling.Ignore)]
+ public long TssOrganizationId { get; set; }
+
+ public long ValidityPeriod { get; set; }
+
+ public long ServerCount { get; set; }
+
+ [JsonProperty("CSR")] public string Csr { get; set; }
+
+ public string DomainName { get; set; }
+
+ public string WebServerType { get; set; }
+
+ [JsonProperty("DNSNames", NullValueHandling = NullValueHandling.Ignore)]
+ public List DnsNames { get; set; }
+
+ [JsonProperty("isCUOrder")] public bool IsCuOrder { get; set; }
+
+ [JsonProperty("AutoWWW", NullValueHandling = NullValueHandling.Ignore,DefaultValueHandling =DefaultValueHandling.Ignore)]
+ public bool? AutoWWW { get; set; }
+
+ [JsonProperty("isRenewalOrder")] public bool IsRenewalOrder { get; set; }
+
+ public string SpecialInstructions { get; set; }
+
+ [JsonProperty("RelatedTheSSLStoreOrderID", NullValueHandling = NullValueHandling.Ignore)]
+ public string RelatedTheSslStoreOrderId { get; set; }
+
+ [JsonProperty("isTrialOrder")] public bool IsTrialOrder { get; set; }
+
+ public AdminContact AdminContact { get; set; }
+
+ public TechnicalContact TechnicalContact { get; set; }
+
+ public string ApproverEmail { get; set; }
+
+ [JsonProperty("ReserveSANCount", NullValueHandling = NullValueHandling.Ignore)]
+ public long ReserveSanCount { get; set; }
+
+ public bool AddInstallationSupport { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string EmailLanguageCode { get; set; }
+
+ [JsonProperty("FileAuthDVIndicator")] public bool? FileAuthDvIndicator { get; set; }
+
+ [JsonProperty("CNAMEAuthDVIndicator", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? CnameAuthDvIndicator { get; set; }
+
+ [JsonProperty("HTTPSFileAuthDVIndicator", NullValueHandling = NullValueHandling.Ignore)]
+ public bool HttpsFileAuthDvIndicator { get; set; }
+
+ public string SignatureHashAlgorithm { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public bool CertTransparencyIndicator { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public long RenewalDays { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string DateTimeCulture { get; set; }
+
+ [JsonProperty("CSRUniqueValue", NullValueHandling = NullValueHandling.Ignore)]
+ public string CsrUniqueValue { get; set; }
+
+ [JsonProperty("isPKCS10", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsPkcs10 { get; set; }
+
+ [JsonProperty("WildcardReserveSANCount", NullValueHandling = NullValueHandling.Ignore)]
+ public long WildcardReserveSanCount { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string ProvisioningMethod { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/NewOrderResponse.cs b/SslStoreCaProxy/Client/Models/NewOrderResponse.cs
new file mode 100644
index 0000000..b664105
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/NewOrderResponse.cs
@@ -0,0 +1,75 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class NewOrderResponse : INewOrderResponse
+ {
+ public AdminContact AdminContact { get; set; }
+ public string ApproverEmail { get; set; }
+ public string AuthFileContent { get; set; }
+ public string AuthFileName { get; set; }
+ public AuthResponse AuthResponse { get; set; }
+ [JsonProperty("CNAMEAuthName")] public string CnameAuthName { get; set; }
+ [JsonProperty("CNAMEAuthValue")] public string CnameAuthValue { get; set; }
+ public string CertificateEndDate { get; set; }
+
+ [JsonProperty("CertificateEndDateInUTC")]
+ public string CertificateEndDateInUtc { get; set; }
+
+ public string CertificateStartDate { get; set; }
+
+ [JsonProperty("CertificateStartDateInUTC")]
+ public string CertificateStartDateInUtc { get; set; }
+
+ public string CommonName { get; set; }
+ public string Country { get; set; }
+ [JsonProperty("CustomOrderID")] public string CustomOrderId { get; set; }
+ [JsonProperty("CustomerID")] public int CustomerId { get; set; }
+ public string CustomerLoginName { get; set; }
+ public string CustomerPassword { get; set; }
+ [JsonProperty("DNSNames")] public string DnsNames { get; set; }
+ [JsonProperty("DUNS")] public string Duns { get; set; }
+ public string Locality { get; set; }
+ public string OrderAmount { get; set; }
+ public string OrderExpiryDate { get; set; }
+ [JsonProperty("OrderExpiryDateInUTC")] public string OrderExpiryDateInUtc { get; set; }
+ public OrderStatus OrderStatus { get; set; }
+ public string Organization { get; set; }
+ public string OrganizationAddress { get; set; }
+ public string OrganizationPhone { get; set; }
+ public string OrganizationPostalcode { get; set; }
+ public string OrganizationalUnit { get; set; }
+ [JsonProperty("PartnerOrderID")] public string PartnerOrderId { get; set; }
+ public string PollDate { get; set; }
+ [JsonProperty("PollDateInUTC")] public string PollDateInUtc { get; set; }
+ public string PollStatus { get; set; }
+ public string ProductCode { get; set; }
+ public string ProductName { get; set; }
+ public string PurchaseDate { get; set; }
+ [JsonProperty("PurchaseDateInUTC")] public string PurchaseDateInUtc { get; set; }
+ [JsonProperty("RefundRequestID")] public string RefundRequestId { get; set; }
+ public string ReissueSuccessCode { get; set; }
+ [JsonProperty("SANCount")] public int SanCount { get; set; }
+ public string SerialNumber { get; set; }
+ public int ServerCount { get; set; }
+ public string SignatureEncryptionAlgorithm { get; set; }
+ public string SignatureHashAlgorithm { get; set; }
+ public string SiteSealurl { get; set; }
+ public string State { get; set; }
+ public string SubVendorName { get; set; }
+ [JsonProperty("TSSOrganizationId")] public int TssOrganizationId { get; set; }
+ public TechnicalContact TechnicalContact { get; set; }
+ [JsonProperty("TheSSLStoreOrderID")] public string TheSslStoreOrderId { get; set; }
+ public string TinyOrderLink { get; set; }
+ public string Token { get; set; }
+ public string TokenCode { get; set; }
+ [JsonProperty("TokenID")] public string TokenId { get; set; }
+ public int Validity { get; set; }
+ public string VendorName { get; set; }
+ [JsonProperty("VendorOrderID")] public string VendorOrderId { get; set; }
+ public string WebServerType { get; set; }
+ [JsonProperty("isRefundApproved")] public bool IsRefundApproved { get; set; }
+ [JsonProperty("wildcardSANCount")] public int WildcardSanCount { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/OrderNote.cs b/SslStoreCaProxy/Client/Models/OrderNote.cs
new file mode 100644
index 0000000..1ae23f6
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrderNote.cs
@@ -0,0 +1,10 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrderNote : IOrderNote
+ {
+ public string Comments { get; set; }
+ public string DateAdded { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/OrderStatus.cs b/SslStoreCaProxy/Client/Models/OrderStatus.cs
new file mode 100644
index 0000000..068f970
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrderStatus.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrderStatus : IOrderStatus
+ {
+ [JsonProperty("isTinyOrder")] public bool IsTinyOrder { get; set; }
+
+ [JsonProperty("isTinyOrderClaimed")] public bool IsTinyOrderClaimed { get; set; }
+
+ public string MajorStatus { get; set; }
+ public string MinorStatus { get; set; }
+ public List AuthenticationStatuses { get; set; }
+ public string AuthenticationComments { get; set; }
+ public List OrderNotes { get; set; }
+ public List DomainAuthVettingStatus { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/OrderStatusRequest.cs b/SslStoreCaProxy/Client/Models/OrderStatusRequest.cs
new file mode 100644
index 0000000..f69cc76
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrderStatusRequest.cs
@@ -0,0 +1,12 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrderStatusRequest : IOrderStatusRequest
+ {
+ public AuthRequest AuthRequest { get; set; }
+ [JsonProperty("TheSSLStoreOrderID", NullValueHandling = NullValueHandling.Ignore)] public string TheSslStoreOrderId { get; set; }
+ [JsonProperty("CustomOrderID", NullValueHandling = NullValueHandling.Ignore)] public string CustomOrderId { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/OrderStatusResponse.cs b/SslStoreCaProxy/Client/Models/OrderStatusResponse.cs
new file mode 100644
index 0000000..7a23f52
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrderStatusResponse.cs
@@ -0,0 +1,68 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrderStatusResponse : IOrderStatusResponse
+ {
+ public AuthResponse AuthResponse { get; set; }
+ [JsonProperty("PartnerOrderID")] public string PartnerOrderId { get; set; }
+ [JsonProperty("CustomOrderID")] public string CustomOrderId { get; set; }
+ [JsonProperty("TheSSLStoreOrderID")] public string TheSslStoreOrderId { get; set; }
+ [JsonProperty("VendorOrderID")] public string VendorOrderId { get; set; }
+ [JsonProperty("RefundRequestID")] public string RefundRequestId { get; set; }
+ [JsonProperty("isRefundApproved")] public bool IsRefundApproved { get; set; }
+ public string TinyOrderLink { get; set; }
+ public OrderStatus OrderStatus { get; set; }
+ public string OrderAmount { get; set; }
+ public string PurchaseDate { get; set; }
+ public string CertificateStartDate { get; set; }
+ public string CertificateEndDate { get; set; }
+ public string CommonName { get; set; }
+ [JsonProperty("DNSNames")] public string DnsNames { get; set; }
+ [JsonProperty("SANCount")] public long SanCount { get; set; }
+ public long ServerCount { get; set; }
+ public long Validity { get; set; }
+ public string Organization { get; set; }
+ public string OrganizationalUnit { get; set; }
+ public string State { get; set; }
+ public string Country { get; set; }
+ public string Locality { get; set; }
+ public string OrganizationPhone { get; set; }
+ public string OrganizationAddress { get; set; }
+ public string OrganizationPostalcode { get; set; }
+ [JsonProperty("DUNS")] public string Duns { get; set; }
+ public string WebServerType { get; set; }
+ public string ApproverEmail { get; set; }
+ public string ProductName { get; set; }
+ public AdminContact AdminContact { get; set; }
+ public TechnicalContact TechnicalContact { get; set; }
+ public string ReissueSuccessCode { get; set; }
+ public string AuthFileName { get; set; }
+ public string AuthFileContent { get; set; }
+ public string PollStatus { get; set; }
+ public string PollDate { get; set; }
+ public string CustomerLoginName { get; set; }
+ public string CustomerPassword { get; set; }
+ [JsonProperty("CustomerID")] public long CustomerId { get; set; }
+ [JsonProperty("TokenID")] public string TokenId { get; set; }
+ public string TokenCode { get; set; }
+ public string SiteSealurl { get; set; }
+ [JsonProperty("CNAMEAuthName")] public string CnameAuthName { get; set; }
+ [JsonProperty("CNAMEAuthValue")] public string CnameAuthValue { get; set; }
+ public string SignatureEncryptionAlgorithm { get; set; }
+ public string SignatureHashAlgorithm { get; set; }
+ public string VendorName { get; set; }
+ public string SubVendorName { get; set; }
+ public string Token { get; set; }
+
+ [JsonProperty("CertificateStartDateInUTC")]
+ public string CertificateStartDateInUtc { get; set; }
+
+ [JsonProperty("CertificateEndDateInUTC")]
+ public string CertificateEndDateInUtc { get; set; }
+
+ [JsonProperty("PurchaseDateInUTC")] public string PurchaseDateInUtc { get; set; }
+ [JsonProperty("PollDateInUTC")] public string PollDateInUtc { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/Organization.cs b/SslStoreCaProxy/Client/Models/Organization.cs
new file mode 100644
index 0000000..7b6410d
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/Organization.cs
@@ -0,0 +1,25 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class Organization : IOrganization
+ {
+ public string Address { get; set; }
+ public string Address2 { get; set; }
+ public object ApproversContact { get; set; }
+ public string AssumedName { get; set; }
+ public string City { get; set; }
+ public string Country { get; set; }
+ public string Name { get; set; }
+ public OrganizationContact OrganizationContact { get; set; }
+ [JsonProperty("Organization_Phone", NullValueHandling = NullValueHandling.Ignore)]
+ public string OrganizationPhone { get; set; }
+ public string State { get; set; }
+ public string Zip { get; set; }
+ public string Status { get; set; }
+ [JsonProperty("TSSOrganizationId", NullValueHandling = NullValueHandling.Ignore)]
+ public int TssOrganizationId { get; set; }
+ public int VendorOrganizationId { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/OrganizationAddress.cs b/SslStoreCaProxy/Client/Models/OrganizationAddress.cs
new file mode 100644
index 0000000..d2d6b95
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrganizationAddress.cs
@@ -0,0 +1,18 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrganizationAddress : IOrganizationAddress
+ {
+ public string AddressLine1 { get; set; }
+ public string AddressLine2 { get; set; }
+ public string AddressLine3 { get; set; }
+ public string City { get; set; }
+ public string Region { get; set; }
+ public string PostalCode { get; set; }
+ public string Country { get; set; }
+ public string Phone { get; set; }
+ public string Fax { get; set; }
+ public string LocalityName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/OrganizationContact.cs b/SslStoreCaProxy/Client/Models/OrganizationContact.cs
new file mode 100644
index 0000000..61e953c
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrganizationContact.cs
@@ -0,0 +1,18 @@
+
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrganizationContact : IOrganizationContact
+ {
+ public string Email { get; set; }
+ public string Firstname { get; set; }
+ public string JobTitle { get; set; }
+ public string Lastname { get; set; }
+ public string Phone { get; set; }
+ [JsonProperty("Phone_Extension", NullValueHandling = NullValueHandling.Ignore)]
+ public string PhoneExtension { get; set; }
+ public string Username { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/OrganizationInfo.cs b/SslStoreCaProxy/Client/Models/OrganizationInfo.cs
new file mode 100644
index 0000000..ef76148
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrganizationInfo.cs
@@ -0,0 +1,21 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrganizationInfo : IOrganizationInfo
+ {
+ public string OrganizationName { get; set; }
+
+ [JsonProperty("DUNS")] public string Duns { get; set; }
+
+ public string Division { get; set; }
+ public string IncorporatingAgency { get; set; }
+ public string RegistrationNumber { get; set; }
+ public string JurisdictionCity { get; set; }
+ public string JurisdictionRegion { get; set; }
+ public string JurisdictionCountry { get; set; }
+ public string AssumedName { get; set; }
+ public OrganizationAddress OrganizationAddress { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/OrganizationListRequest.cs b/SslStoreCaProxy/Client/Models/OrganizationListRequest.cs
new file mode 100644
index 0000000..16dffb3
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrganizationListRequest.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrganizationListRequest : IOrganizationListRequest
+ {
+ public string PartnerCode { get; set; }
+ public string AuthToken { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/OrganizationResponse.cs b/SslStoreCaProxy/Client/Models/OrganizationResponse.cs
new file mode 100644
index 0000000..6ef3038
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/OrganizationResponse.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class OrganizationResponse : IOrganizationResponse
+ {
+ public AuthResponse AuthResponse { get; set; }
+ public List OrganizationList { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/QueryOrderRequest.cs b/SslStoreCaProxy/Client/Models/QueryOrderRequest.cs
new file mode 100644
index 0000000..dd90de4
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/QueryOrderRequest.cs
@@ -0,0 +1,41 @@
+using System;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class QueryOrderRequest : IQueryOrderRequest
+ {
+ public AuthRequest AuthRequest { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public DateTime? StartDate { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public DateTime? EndDate { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public DateTime? CertificateExpireToDate { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public DateTime? CertificateExpireFromDate { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string DomainName { get; set; }
+
+ [JsonProperty("SubUserID", NullValueHandling = NullValueHandling.Ignore)]
+ public string SubUserId { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string ProductCode { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string DateTimeCulture { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public long PageNumber { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public long PageSize { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/ReIssueRequest.cs b/SslStoreCaProxy/Client/Models/ReIssueRequest.cs
new file mode 100644
index 0000000..d1aaf66
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/ReIssueRequest.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class ReIssueRequest : IReIssueRequest
+ {
+ public AuthRequest AuthRequest { get; set; }
+ [JsonProperty("TheSSLStoreOrderID")] public string TheSslStoreOrderId { get; set; }
+ [JsonProperty("CustomOrderID", NullValueHandling = NullValueHandling.Ignore)] public string CustomOrderId { get; set; }
+ [JsonProperty("CSR")] public string Csr { get; set; }
+ public string WebServerType { get; set; }
+ [JsonProperty("DNSNames")] public List DnsNames { get; set; }
+ [JsonProperty("isRenewalOrder")] public bool IsRenewalOrder { get; set; }
+ public string SpecialInstructions { get; set; }
+ [JsonProperty("EditSAN")] public List EditSan { get; set; }
+ [JsonProperty("DeleteSAN")] public List DeleteSan { get; set; }
+ [JsonProperty("AddSAN")] public List AddSan { get; set; }
+ [JsonProperty("isWildCard")] public bool IsWildCard { get; set; }
+ public string ReissueEmail { get; set; }
+ public bool PreferEnrollmentLink { get; set; }
+ public string SignatureHashAlgorithm { get; set; }
+ [JsonProperty("FileAuthDVIndicator")] public bool FileAuthDvIndicator { get; set; }
+
+ [JsonProperty("HTTPSFileAuthDVIndicator")]
+ public bool HttpsFileAuthDvIndicator { get; set; }
+
+ [JsonProperty("CNAMEAuthDVIndicator")] public bool CNameAuthDvIndicator { get; set; }
+ public string ApproverEmails { get; set; }
+ public string DateTimeCulture { get; set; }
+ [JsonProperty("CSRUniqueValue")] public string CsrUniqueValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/RevokeOrderRequest.cs b/SslStoreCaProxy/Client/Models/RevokeOrderRequest.cs
new file mode 100644
index 0000000..8faf24b
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/RevokeOrderRequest.cs
@@ -0,0 +1,13 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class RevokeOrderRequest : IRevokeOrderRequest
+ {
+ public AuthRequest AuthRequest { get; set; }
+ [JsonProperty("TheSSLStoreOrderID", NullValueHandling = NullValueHandling.Ignore)] public string TheSslStoreOrderId { get; set; }
+ [JsonProperty("CustomOrderID", NullValueHandling = NullValueHandling.Ignore)] public string CustomOrderId { get; set; }
+ public string SerialNumber { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/RevokeOrderResponse.cs b/SslStoreCaProxy/Client/Models/RevokeOrderResponse.cs
new file mode 100644
index 0000000..95abb0b
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/RevokeOrderResponse.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class RevokeOrderResponse : IRevokeOrderResponse
+ {
+ public string InvokingPartnerCode { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public List Message { get; set; }
+
+ public object ReplayToken { get; set; }
+ public string Timestamp { get; set; }
+ [JsonProperty("isError")] public bool IsError { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/TechnicalContact.cs b/SslStoreCaProxy/Client/Models/TechnicalContact.cs
new file mode 100644
index 0000000..53c96f4
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/TechnicalContact.cs
@@ -0,0 +1,23 @@
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class TechnicalContact : ITechnicalContact
+ {
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ public string SubjectFirstName { get; set; }
+ public string SubjectLastName { get; set; }
+ public string Phone { get; set; }
+ public string Fax { get; set; }
+ public string Email { get; set; }
+ public string Title { get; set; }
+ public string OrganizationName { get; set; }
+ public string AddressLine1 { get; set; }
+ public string AddressLine2 { get; set; }
+ public string City { get; set; }
+ public string Region { get; set; }
+ public string PostalCode { get; set; }
+ public string Country { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/Template.cs b/SslStoreCaProxy/Client/Models/Template.cs
new file mode 100644
index 0000000..b89e361
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/Template.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class Template : ITemplate
+ {
+ public int Id { get; set; }
+ public string CommonName { get; set; }
+ public string TemplateName { get; set; }
+ public string Oid { get; set; }
+ public string KeySize { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string KeyType { get; set; }
+ public string ForestRoot { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public string FriendlyName { get; set; }
+ public string KeyRetention { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+ public int? KeyRetentionDays { get; set; }
+ public bool KeyArchival { get; set; }
+ public List EnrollmentFields { get; set; }
+ public int AllowedEnrollmentTypes { get; set; }
+ public List TemplateRegexes { get; set; }
+ public bool UseAllowedRequesters { get; set; }
+ public List AllowedRequesters { get; set; }
+ public string DisplayName { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/Models/TemplateNewOrderRequest.cs b/SslStoreCaProxy/Client/Models/TemplateNewOrderRequest.cs
new file mode 100644
index 0000000..5e03d61
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/TemplateNewOrderRequest.cs
@@ -0,0 +1,270 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class TemplateNewOrderRequest : ITemplateNewOrderRequest
+ {
+ public TemplateAuthRequest AuthRequest { get; set; }
+ public ProductCode ProductCode { get; set; }
+ public TemplateOrganizationInfo OrganizationInfo { get; set; }
+ public ValidityPeriod ValidityPeriod { get; set; }
+ public ServerCount ServerCount { get; set; }
+ [JsonProperty("CSR")] public Csr Csr { get; set; }
+ public DomainName DomainName { get; set; }
+ public WebServerType WebServerType { get; set; }
+ [JsonProperty("DNSNames")] public DnsNames DnsNames { get; set; }
+ [JsonProperty("isCUOrder")] public IsCuOrder IsCuOrder { get; set; }
+ [JsonProperty("AutoWWW", NullValueHandling = NullValueHandling.Ignore)] public AutoWWW AutoWWW { get; set; }
+ [JsonProperty("isRenewalOrder")] public IsRenewalOrder IsRenewalOrder { get; set; }
+ [JsonProperty("isTrialOrder")] public IsTrialOrder IsTrialOrder { get; set; }
+ public TemplateAdminContact AdminContact { get; set; }
+ public TemplateTechnicalContact TechnicalContact { get; set; }
+ public ApproverEmail ApproverEmail { get; set; }
+ [JsonProperty("FileAuthDVIndicator")] public FileAuthDvIndicator FileAuthDvIndicator { get; set; }
+ [JsonProperty("CNAMEAuthDVIndicator")] public CNameAuthDvIndicator CNameAuthDvIndicator { get; set; }
+ public SignatureHashAlgorithm SignatureHashAlgorithm { get; set; }
+ }
+
+ public class FieldData
+ {
+ public List RequiredForProducts { get; set; }
+ public string EnrollmentFieldMapping { get; set; }
+ public bool Array { get; set; }
+ }
+
+ public class PartnerCode
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class AuthToken
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("AuthRequest")]
+ public class TemplateAuthRequest
+ {
+ public PartnerCode PartnerCode { get; set; }
+ public AuthToken AuthToken { get; set; }
+ }
+
+ public class ProductCode
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class OrganizationName
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class RegistrationNumber
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class JurisdictionCountry
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class AddressLine1
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class Region
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class PostalCode
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class LocalityName
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("OrganizationAddress")]
+ public class TemplateOrganizationAddress
+ {
+ public AddressLine1 AddressLine1 { get; set; }
+ public Region Region { get; set; }
+ public PostalCode PostalCode { get; set; }
+ public LocalityName LocalityName { get; set; }
+ public Country Country { get; set; }
+ public Phone Phone { get; set; }
+
+ }
+
+ [JsonObject("OrganizationInfo")]
+ public class TemplateOrganizationInfo
+ {
+ public OrganizationName OrganizationName { get; set; }
+ public RegistrationNumber RegistrationNumber { get; set; }
+ public JurisdictionCountry JurisdictionCountry { get; set; }
+ public TemplateOrganizationAddress OrganizationAddress { get; set; }
+ }
+
+ public class ValidityPeriod
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class ServerCount
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("CSR")]
+ public class Csr
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class DomainName
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class WebServerType
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("DNSNames")]
+ public class DnsNames
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("isCUOrder")]
+ public class IsCuOrder
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("AutoWWW",ItemNullValueHandling =NullValueHandling.Ignore)]
+ public class AutoWWW
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("isRenewalOrder")]
+ public class IsRenewalOrder
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("isTrialOrder")]
+ public class IsTrialOrder
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class FirstName
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class LastName
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class Phone
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class Email
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class Title
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class City
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class Country
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("AdminContact")]
+ public class TemplateAdminContact
+ {
+ public FirstName FirstName { get; set; }
+ public LastName LastName { get; set; }
+ public Phone Phone { get; set; }
+ public Email Email { get; set; }
+ public Title Title { get; set; }
+ public OrganizationName OrganizationName { get; set; }
+ public AddressLine1 AddressLine1 { get; set; }
+ public City City { get; set; }
+ public Region Region { get; set; }
+ public PostalCode PostalCode { get; set; }
+ public Country Country { get; set; }
+ }
+
+ public class SubjectFirstName
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class SubjectLastName
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("TechnicalContact")]
+ public class TemplateTechnicalContact
+ {
+ public FirstName FirstName { get; set; }
+ public LastName LastName { get; set; }
+ public SubjectFirstName SubjectFirstName { get; set; }
+ public SubjectLastName SubjectLastName { get; set; }
+ public Phone Phone { get; set; }
+ public Email Email { get; set; }
+ public Title Title { get; set; }
+ public AddressLine1 AddressLine1 { get; set; }
+ public City City { get; set; }
+ public Region Region { get; set; }
+ public PostalCode PostalCode { get; set; }
+ public Country Country { get; set; }
+ }
+
+ public class ApproverEmail
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("FileAuthDVIndicator")]
+ public class FileAuthDvIndicator
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ [JsonObject("CNAMEAuthDVIndicator")]
+ public class CNameAuthDvIndicator
+ {
+ public FieldData FieldData { get; set; }
+ }
+
+ public class SignatureHashAlgorithm
+ {
+ public FieldData FieldData { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Client/Models/TemplateRegex.cs b/SslStoreCaProxy/Client/Models/TemplateRegex.cs
new file mode 100644
index 0000000..27eaab6
--- /dev/null
+++ b/SslStoreCaProxy/Client/Models/TemplateRegex.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+
+namespace Keyfactor.AnyGateway.SslStore.Client.Models
+{
+ public class TemplateRegex : ITemplateRegex
+ {
+ public int TemplateId { get; set; }
+ public string SubjectPart { get; set; }
+ public string Regex { get; set; }
+ public string Error { get; set; }
+ }
+}
diff --git a/SslStoreCaProxy/Client/SslStoreClient.cs b/SslStoreCaProxy/Client/SslStoreClient.cs
new file mode 100644
index 0000000..3d8b721
--- /dev/null
+++ b/SslStoreCaProxy/Client/SslStoreClient.cs
@@ -0,0 +1,246 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.Extensions;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+using Keyfactor.AnyGateway.SslStore.Exceptions;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Keyfactor.Logging;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Keyfactor.AnyGateway.SslStore.Client
+{
+ public sealed class SslStoreClient : ISslStoreClient
+ {
+ private static readonly ILogger _logger = LogHandler.GetClassLogger();
+
+ // Use an explicit JsonSerializer to ensure [JsonProperty] attributes are respected,
+ // regardless of the host application's global JsonConvert.DefaultSettings.
+ private static readonly JsonSerializer _serializer = new JsonSerializer
+ {
+ ContractResolver = new DefaultContractResolver(),
+ NullValueHandling = NullValueHandling.Ignore
+ };
+
+ private static string Serialize(object obj)
+ {
+ var sb = new StringBuilder();
+ using (var sw = new System.IO.StringWriter(sb))
+ using (var jw = new JsonTextWriter(sw))
+ {
+ _serializer.Serialize(jw, obj);
+ }
+ return sb.ToString();
+ }
+
+ public SslStoreClient(IAnyCAPluginConfigProvider config)
+ {
+ if (config.CAConnectionData.ContainsKey(Constants.SslStoreUrl))
+ {
+ BaseUrl = new Uri(config.CAConnectionData[Constants.SslStoreUrl].ToString());
+ RestClient = ConfigureRestClient();
+ }
+ }
+
+ private Uri BaseUrl { get; }
+ private HttpClient RestClient { get; }
+ private int PageSize { get; } = 100;
+
+ public async Task SubmitNewOrderRequestAsync(NewOrderRequest newOrderRequest)
+ {
+ using (var resp = await RestClient.PostAsync("/rest/order/neworder", new StringContent(
+ Serialize(newOrderRequest), Encoding.UTF8, "application/json")))
+ {
+ _logger.LogTrace(Serialize(newOrderRequest));
+ resp.EnsureSuccessStatusCode();
+ var enrollmentResponse =
+ JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync());
+ return enrollmentResponse;
+ }
+ }
+
+ public async Task SubmitEmailApproverRequestAsync(EmailApproverRequest newApproverRequest)
+ {
+ using (var resp = await RestClient.PostAsync("/rest/order/approverlist", new StringContent(
+ Serialize(newApproverRequest), Encoding.UTF8, "application/json")))
+ {
+ _logger.LogTrace(Serialize(newApproverRequest));
+ resp.EnsureSuccessStatusCode();
+ var enrollmentResponse =
+ JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync());
+ return enrollmentResponse;
+ }
+ }
+
+ public async Task SubmitReIssueRequestAsync(ReIssueRequest reIssueOrderRequest)
+ {
+ using (var resp = await RestClient.PostAsync("/rest/order/reissue", new StringContent(
+ Serialize(reIssueOrderRequest), Encoding.UTF8, "application/json")))
+ {
+ var orderStatusResponse =
+ JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync());
+ return orderStatusResponse;
+ }
+ }
+
+ public async Task SubmitRenewRequestAsync(NewOrderRequest renewOrderRequest)
+ {
+ using (var resp = await RestClient.PostAsync("/rest/order/neworder", new StringContent(
+ Serialize(renewOrderRequest), Encoding.UTF8, "application/json")))
+ {
+ _logger.LogTrace(Serialize(renewOrderRequest));
+ resp.EnsureSuccessStatusCode();
+ var enrollmentResponse =
+ JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync());
+ return enrollmentResponse;
+ }
+ }
+
+ public async Task SubmitDownloadCertificateAsync(
+ DownloadCertificateRequest downloadOrderRequest)
+ {
+ using (var resp = await RestClient.PostAsync("/rest/order/download", new StringContent(
+ Serialize(downloadOrderRequest), Encoding.UTF8, "application/json")))
+ {
+ _logger.LogTrace(Serialize(downloadOrderRequest));
+ resp.EnsureSuccessStatusCode();
+ var downloadOrderResponse =
+ JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync());
+ return downloadOrderResponse;
+ }
+ }
+
+ public async Task SubmitQueryOrderRequestAsync(BlockingCollection bc, CancellationToken ct,
+ RequestManager requestManager)
+ {
+ _logger.MethodEntry();
+ try
+ {
+ var itemsProcessed = 0;
+ var pageCounter = 0;
+ var isComplete = false;
+ var retryCount = 0;
+ do
+ {
+ pageCounter++;
+ var queryOrderRequest = requestManager.GetQueryOrderRequest(PageSize, pageCounter);
+ var batchItemsProcessed = 0;
+ using (var resp = await RestClient.PostAsync("/rest/order/query", new StringContent(
+ Serialize(queryOrderRequest), Encoding.UTF8, "application/json")))
+ {
+ if (!resp.IsSuccessStatusCode)
+ {
+ var responseMessage = resp.Content.ReadAsStringAsync().Result;
+ _logger.LogError(
+ $"Failed Request to SslStore. Retrying request. Status Code {resp.StatusCode} | Message: {responseMessage}");
+ retryCount++;
+ if (retryCount > 5)
+ throw new RetryCountExceededException(
+ $"5 consecutive failures to {resp.RequestMessage.RequestUri}");
+
+ continue;
+ }
+
+ var batchResponse =
+ JsonConvert.DeserializeObject>(
+ await resp.Content.ReadAsStringAsync());
+
+ _logger.LogTrace($"Order List JSON {Serialize(batchResponse)}");
+
+ var batchCount = batchResponse.Count;
+
+ _logger.LogTrace($"Processing {batchCount} items in batch");
+ do
+ {
+ var r = batchResponse[batchItemsProcessed];
+ if (bc.TryAdd(r, 10, ct))
+ {
+ _logger.LogTrace($"Added Certificate ID {r.TheSslStoreOrderId} to Queue for processing");
+ batchItemsProcessed++;
+ itemsProcessed++;
+ _logger.LogTrace($"Processed {batchItemsProcessed} of {batchCount}");
+ _logger.LogTrace($"Total Items Processed: {itemsProcessed}");
+ }
+ else
+ {
+ _logger.LogTrace($"Adding {r} blocked. Retry");
+ }
+ } while (batchItemsProcessed < batchCount);
+ }
+
+ if (batchItemsProcessed < PageSize)
+ isComplete = true;
+ } while (!isComplete);
+
+ bc.CompleteAdding();
+ }
+ catch (OperationCanceledException cancelEx)
+ {
+ _logger.LogWarning($"Synchronize method was cancelled. Message: {cancelEx.Message}");
+ bc.CompleteAdding();
+ _logger.MethodExit();
+ throw;
+ }
+ catch (RetryCountExceededException retryEx)
+ {
+ _logger.LogError($"Retries Failed: {retryEx.Message}");
+ _logger.MethodExit();
+ }
+ catch (HttpRequestException ex)
+ {
+ _logger.LogError($"HttpRequest Failed: {ex.Message}");
+ _logger.MethodExit();
+ }
+
+ _logger.MethodExit();
+ }
+
+ public async Task SubmitRevokeCertificateAsync(RevokeOrderRequest revokeOrderRequest)
+ {
+ using (var resp = await RestClient.PostAsync("/rest/order/refundrequest", new StringContent(
+ Serialize(revokeOrderRequest), Encoding.UTF8, "application/json")))
+ {
+ var revocationResponse =
+ JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync());
+ return revocationResponse;
+ }
+ }
+
+ public async Task SubmitOrderStatusRequestAsync(OrderStatusRequest orderStatusRequest)
+ {
+ using (var resp = await RestClient.PostAsync("/rest/order/status", new StringContent(
+ Serialize(orderStatusRequest), Encoding.UTF8, "application/json")))
+ {
+ var orderStatusResponse =
+ JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync());
+ return orderStatusResponse;
+ }
+ }
+
+ public async Task SubmitOrganizationListAsync(OrganizationListRequest organizationListRequest)
+ {
+ using (var resp = await RestClient.PostAsync("/rest/digicert/organizationlist", new StringContent(
+ Serialize(organizationListRequest), Encoding.UTF8, "application/json")))
+ {
+ var organizationListResponse =
+ JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync());
+ return organizationListResponse;
+ }
+ }
+
+ private HttpClient ConfigureRestClient()
+ {
+ var clientHandler = new HttpClientHandler();
+ var returnClient = new HttpClient(clientHandler, true) { BaseAddress = BaseUrl };
+ returnClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+ return returnClient;
+ }
+ }
+}
diff --git a/SslStoreCaProxy/Constants.cs b/SslStoreCaProxy/Constants.cs
new file mode 100644
index 0000000..36caa82
--- /dev/null
+++ b/SslStoreCaProxy/Constants.cs
@@ -0,0 +1,11 @@
+namespace Keyfactor.AnyGateway.SslStore
+{
+ internal class Constants
+ {
+ public static string SslStoreUrl = "SSLStoreURL";
+ public static string PartnerCode = "PartnerCode";
+ public static string AuthToken = "AuthToken";
+ public static string PageSize = "PageSize";
+ public static int DefaultPageSize = 100;
+ }
+}
diff --git a/SslStoreCaProxy/Exceptions/RetryCountExceededException.cs b/SslStoreCaProxy/Exceptions/RetryCountExceededException.cs
new file mode 100644
index 0000000..125c666
--- /dev/null
+++ b/SslStoreCaProxy/Exceptions/RetryCountExceededException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Keyfactor.AnyGateway.SslStore.Exceptions
+{
+ public class RetryCountExceededException : Exception
+ {
+ public RetryCountExceededException(string message) : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IAddSan.cs b/SslStoreCaProxy/Interfaces/IAddSan.cs
new file mode 100644
index 0000000..6bf7277
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IAddSan.cs
@@ -0,0 +1,8 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IAddSan
+ {
+ string OldValue { get; set; }
+ string NewValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IAdminContact.cs b/SslStoreCaProxy/Interfaces/IAdminContact.cs
new file mode 100644
index 0000000..deb6573
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IAdminContact.cs
@@ -0,0 +1,21 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IAdminContact
+ {
+ string FirstName { get; set; }
+ string LastName { get; set; }
+ string SubjectFirstName { get; set; }
+ string SubjectLastName { get; set; }
+ string Phone { get; set; }
+ string Fax { get; set; }
+ string Email { get; set; }
+ string Title { get; set; }
+ string OrganizationName { get; set; }
+ string AddressLine1 { get; set; }
+ string AddressLine2 { get; set; }
+ string City { get; set; }
+ string Region { get; set; }
+ string PostalCode { get; set; }
+ string Country { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IAuthRequest.cs b/SslStoreCaProxy/Interfaces/IAuthRequest.cs
new file mode 100644
index 0000000..17d4526
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IAuthRequest.cs
@@ -0,0 +1,15 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IAuthRequest
+ {
+ string PartnerCode { get; set; }
+ string AuthToken { get; set; }
+ string ReplayToken { get; set; }
+ string UserAgent { get; set; }
+ string TokenId { get; set; }
+ string TokenCode { get; set; }
+ string IpAddress { get; set; }
+ bool IsUsedForTokenSystem { get; set; }
+ string Token { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IAuthResponse.cs b/SslStoreCaProxy/Interfaces/IAuthResponse.cs
new file mode 100644
index 0000000..161a3ee
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IAuthResponse.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IAuthResponse
+ {
+ bool IsError { get; set; }
+ List Message { get; set; }
+ string Timestamp { get; set; }
+ string ReplayToken { get; set; }
+ string InvokingPartnerCode { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IAuthenticationStatus.cs b/SslStoreCaProxy/Interfaces/IAuthenticationStatus.cs
new file mode 100644
index 0000000..d4c66f9
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IAuthenticationStatus.cs
@@ -0,0 +1,9 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IAuthenticationStatus
+ {
+ string AuthenticationStep { get; set; }
+ string Status { get; set; }
+ string LastUpdated { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IDomainAuthVettingStatus.cs b/SslStoreCaProxy/Interfaces/IDomainAuthVettingStatus.cs
new file mode 100644
index 0000000..a6dcdfc
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IDomainAuthVettingStatus.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IDomainAuthVettingStatus
+ {
+ string Domain { get; set; }
+ string DcvMethod { get; set; }
+ string DcvStatus { get; set; }
+ string FileName { get; set; }
+ string FileContents { get; set; }
+ string DnsName { get; set; }
+ string DnsEntry { get; set; }
+ string PollStatus { get; set; }
+ DateTime LastPollDate { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IDownloadCertificateRequest.cs b/SslStoreCaProxy/Interfaces/IDownloadCertificateRequest.cs
new file mode 100644
index 0000000..af9f698
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IDownloadCertificateRequest.cs
@@ -0,0 +1,10 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IDownloadCertificateRequest
+ {
+ AuthRequest AuthRequest { get; set; }
+ string TheSslStoreOrderId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IDownloadCertificateResponse.cs b/SslStoreCaProxy/Interfaces/IDownloadCertificateResponse.cs
new file mode 100644
index 0000000..88e8872
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IDownloadCertificateResponse.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IDownloadCertificateResponse
+ {
+ AuthResponse AuthResponse { get; set; }
+ string CertificateEndDate { get; set; }
+ string CertificateEndDateInUtc { get; set; }
+ string CertificateStartDate { get; set; }
+ string CertificateStartDateInUtc { get; set; }
+ string CertificateStatus { get; set; }
+ List Certificates { get; set; }
+ string PartnerOrderId { get; set; }
+ object TheSslStoreOrderId { get; set; }
+ string ValidationStatus { get; set; }
+ object VendorOrderId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IEmailApproverRequest.cs b/SslStoreCaProxy/Interfaces/IEmailApproverRequest.cs
new file mode 100644
index 0000000..722f83e
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IEmailApproverRequest.cs
@@ -0,0 +1,11 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IEmailApproverRequest
+ {
+ AuthRequest AuthRequest { get; set; }
+ string ProductCode { get; set; }
+ string DomainName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IEmailApproverResponse.cs b/SslStoreCaProxy/Interfaces/IEmailApproverResponse.cs
new file mode 100644
index 0000000..a0d353a
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IEmailApproverResponse.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IEmailApproverResponse
+ {
+ List ApproverEmailList { get; set; }
+ AuthResponse AuthResponse { get; set; }
+ string BaseDomainName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IEnrollmentField.cs b/SslStoreCaProxy/Interfaces/IEnrollmentField.cs
new file mode 100644
index 0000000..ff377d7
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IEnrollmentField.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IEnrollmentField
+ {
+ int Id { get; set; }
+ string Name { get; set; }
+ List Options { get; set; }
+ int DataType { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/INewOrderRequest.cs b/SslStoreCaProxy/Interfaces/INewOrderRequest.cs
new file mode 100644
index 0000000..59e87fb
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/INewOrderRequest.cs
@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface INewOrderRequest
+ {
+ AuthRequest AuthRequest { get; set; }
+ string CustomOrderId { get; set; }
+ string ProductCode { get; set; }
+ string ExtraProductCodes { get; set; }
+ OrganizationInfo OrganizationInfo { get; set; }
+ long TssOrganizationId { get; set; }
+ long ValidityPeriod { get; set; }
+ long ServerCount { get; set; }
+ string Csr { get; set; }
+ string DomainName { get; set; }
+ string WebServerType { get; set; }
+ List DnsNames { get; set; }
+ bool IsCuOrder { get; set; }
+ bool? AutoWWW { get; set; }
+ bool IsRenewalOrder { get; set; }
+ string SpecialInstructions { get; set; }
+ string RelatedTheSslStoreOrderId { get; set; }
+ bool IsTrialOrder { get; set; }
+ AdminContact AdminContact { get; set; }
+ TechnicalContact TechnicalContact { get; set; }
+ string ApproverEmail { get; set; }
+ long ReserveSanCount { get; set; }
+ bool AddInstallationSupport { get; set; }
+ string EmailLanguageCode { get; set; }
+ bool? FileAuthDvIndicator { get; set; }
+ bool? CnameAuthDvIndicator { get; set; }
+ bool HttpsFileAuthDvIndicator { get; set; }
+ string SignatureHashAlgorithm { get; set; }
+ bool CertTransparencyIndicator { get; set; }
+ long RenewalDays { get; set; }
+ string DateTimeCulture { get; set; }
+ string CsrUniqueValue { get; set; }
+ bool IsPkcs10 { get; set; }
+ long WildcardReserveSanCount { get; set; }
+ string ProvisioningMethod { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/INewOrderResponse.cs b/SslStoreCaProxy/Interfaces/INewOrderResponse.cs
new file mode 100644
index 0000000..ec1f3aa
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/INewOrderResponse.cs
@@ -0,0 +1,68 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface INewOrderResponse
+ {
+ AdminContact AdminContact { get; set; }
+ string ApproverEmail { get; set; }
+ string AuthFileContent { get; set; }
+ string AuthFileName { get; set; }
+ AuthResponse AuthResponse { get; set; }
+ string CnameAuthName { get; set; }
+ string CnameAuthValue { get; set; }
+ string CertificateEndDate { get; set; }
+ string CertificateEndDateInUtc { get; set; }
+ string CertificateStartDate { get; set; }
+ string CertificateStartDateInUtc { get; set; }
+ string CommonName { get; set; }
+ string Country { get; set; }
+ string CustomOrderId { get; set; }
+ int CustomerId { get; set; }
+ string CustomerLoginName { get; set; }
+ string CustomerPassword { get; set; }
+ string DnsNames { get; set; }
+ string Duns { get; set; }
+ string Locality { get; set; }
+ string OrderAmount { get; set; }
+ string OrderExpiryDate { get; set; }
+ string OrderExpiryDateInUtc { get; set; }
+ OrderStatus OrderStatus { get; set; }
+ string Organization { get; set; }
+ string OrganizationAddress { get; set; }
+ string OrganizationPhone { get; set; }
+ string OrganizationPostalcode { get; set; }
+ string OrganizationalUnit { get; set; }
+ string PartnerOrderId { get; set; }
+ string PollDate { get; set; }
+ string PollDateInUtc { get; set; }
+ string PollStatus { get; set; }
+ string ProductCode { get; set; }
+ string ProductName { get; set; }
+ string PurchaseDate { get; set; }
+ string PurchaseDateInUtc { get; set; }
+ string RefundRequestId { get; set; }
+ string ReissueSuccessCode { get; set; }
+ int SanCount { get; set; }
+ string SerialNumber { get; set; }
+ int ServerCount { get; set; }
+ string SignatureEncryptionAlgorithm { get; set; }
+ string SignatureHashAlgorithm { get; set; }
+ string SiteSealurl { get; set; }
+ string State { get; set; }
+ string SubVendorName { get; set; }
+ int TssOrganizationId { get; set; }
+ TechnicalContact TechnicalContact { get; set; }
+ string TheSslStoreOrderId { get; set; }
+ string TinyOrderLink { get; set; }
+ string Token { get; set; }
+ string TokenCode { get; set; }
+ string TokenId { get; set; }
+ int Validity { get; set; }
+ string VendorName { get; set; }
+ string VendorOrderId { get; set; }
+ string WebServerType { get; set; }
+ bool IsRefundApproved { get; set; }
+ int WildcardSanCount { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrderNote.cs b/SslStoreCaProxy/Interfaces/IOrderNote.cs
new file mode 100644
index 0000000..2d7c65b
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrderNote.cs
@@ -0,0 +1,8 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrderNote
+ {
+ string Comments { get; set; }
+ string DateAdded { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrderStatus.cs b/SslStoreCaProxy/Interfaces/IOrderStatus.cs
new file mode 100644
index 0000000..e4d55df
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrderStatus.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrderStatus
+ {
+ bool IsTinyOrder { get; set; }
+ bool IsTinyOrderClaimed { get; set; }
+ string MajorStatus { get; set; }
+ string MinorStatus { get; set; }
+ List AuthenticationStatuses { get; set; }
+ string AuthenticationComments { get; set; }
+ List OrderNotes { get; set; }
+ List DomainAuthVettingStatus { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrderStatusRequest.cs b/SslStoreCaProxy/Interfaces/IOrderStatusRequest.cs
new file mode 100644
index 0000000..fcd8e43
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrderStatusRequest.cs
@@ -0,0 +1,10 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrderStatusRequest
+ {
+ AuthRequest AuthRequest { get; set; }
+ string TheSslStoreOrderId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrderStatusResponse.cs b/SslStoreCaProxy/Interfaces/IOrderStatusResponse.cs
new file mode 100644
index 0000000..883455e
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrderStatusResponse.cs
@@ -0,0 +1,62 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrderStatusResponse
+ {
+ AuthResponse AuthResponse { get; set; }
+ string PartnerOrderId { get; set; }
+ string CustomOrderId { get; set; }
+ string TheSslStoreOrderId { get; set; }
+ string VendorOrderId { get; set; }
+ string RefundRequestId { get; set; }
+ bool IsRefundApproved { get; set; }
+ string TinyOrderLink { get; set; }
+ OrderStatus OrderStatus { get; set; }
+ string OrderAmount { get; set; }
+ string PurchaseDate { get; set; }
+ string CertificateStartDate { get; set; }
+ string CertificateEndDate { get; set; }
+ string CommonName { get; set; }
+ string DnsNames { get; set; }
+ long SanCount { get; set; }
+ long ServerCount { get; set; }
+ long Validity { get; set; }
+ string Organization { get; set; }
+ string OrganizationalUnit { get; set; }
+ string State { get; set; }
+ string Country { get; set; }
+ string Locality { get; set; }
+ string OrganizationPhone { get; set; }
+ string OrganizationAddress { get; set; }
+ string OrganizationPostalcode { get; set; }
+ string Duns { get; set; }
+ string WebServerType { get; set; }
+ string ApproverEmail { get; set; }
+ string ProductName { get; set; }
+ AdminContact AdminContact { get; set; }
+ TechnicalContact TechnicalContact { get; set; }
+ string ReissueSuccessCode { get; set; }
+ string AuthFileName { get; set; }
+ string AuthFileContent { get; set; }
+ string PollStatus { get; set; }
+ string PollDate { get; set; }
+ string CustomerLoginName { get; set; }
+ string CustomerPassword { get; set; }
+ long CustomerId { get; set; }
+ string TokenId { get; set; }
+ string TokenCode { get; set; }
+ string SiteSealurl { get; set; }
+ string CnameAuthName { get; set; }
+ string CnameAuthValue { get; set; }
+ string SignatureEncryptionAlgorithm { get; set; }
+ string SignatureHashAlgorithm { get; set; }
+ string VendorName { get; set; }
+ string SubVendorName { get; set; }
+ string Token { get; set; }
+ string CertificateStartDateInUtc { get; set; }
+ string CertificateEndDateInUtc { get; set; }
+ string PurchaseDateInUtc { get; set; }
+ string PollDateInUtc { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrganization.cs b/SslStoreCaProxy/Interfaces/IOrganization.cs
new file mode 100644
index 0000000..6f369d2
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrganization.cs
@@ -0,0 +1,22 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrganization
+ {
+ string Address { get; set; }
+ string Address2 { get; set; }
+ object ApproversContact { get; set; }
+ string AssumedName { get; set; }
+ string City { get; set; }
+ string Country { get; set; }
+ string Name { get; set; }
+ OrganizationContact OrganizationContact { get; set; }
+ string OrganizationPhone { get; set; }
+ string State { get; set; }
+ string Zip { get; set; }
+ string Status { get; set; }
+ int TssOrganizationId { get; set; }
+ int VendorOrganizationId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrganizationAddress.cs b/SslStoreCaProxy/Interfaces/IOrganizationAddress.cs
new file mode 100644
index 0000000..e1831a9
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrganizationAddress.cs
@@ -0,0 +1,16 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrganizationAddress
+ {
+ string AddressLine1 { get; set; }
+ string AddressLine2 { get; set; }
+ string AddressLine3 { get; set; }
+ string City { get; set; }
+ string Region { get; set; }
+ string PostalCode { get; set; }
+ string Country { get; set; }
+ string Phone { get; set; }
+ string Fax { get; set; }
+ string LocalityName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrganizationContact.cs b/SslStoreCaProxy/Interfaces/IOrganizationContact.cs
new file mode 100644
index 0000000..b283d21
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrganizationContact.cs
@@ -0,0 +1,13 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrganizationContact
+ {
+ string Email { get; set; }
+ string Firstname { get; set; }
+ string JobTitle { get; set; }
+ string Lastname { get; set; }
+ string Phone { get; set; }
+ string PhoneExtension { get; set; }
+ string Username { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrganizationInfo.cs b/SslStoreCaProxy/Interfaces/IOrganizationInfo.cs
new file mode 100644
index 0000000..1ffe4d3
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrganizationInfo.cs
@@ -0,0 +1,18 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrganizationInfo
+ {
+ string OrganizationName { get; set; }
+ string Duns { get; set; }
+ string Division { get; set; }
+ string IncorporatingAgency { get; set; }
+ string RegistrationNumber { get; set; }
+ string JurisdictionCity { get; set; }
+ string JurisdictionRegion { get; set; }
+ string JurisdictionCountry { get; set; }
+ string AssumedName { get; set; }
+ OrganizationAddress OrganizationAddress { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrganizationListRequest.cs b/SslStoreCaProxy/Interfaces/IOrganizationListRequest.cs
new file mode 100644
index 0000000..1e98104
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrganizationListRequest.cs
@@ -0,0 +1,8 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrganizationListRequest
+ {
+ string PartnerCode { get; set; }
+ string AuthToken { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IOrganizationResponse.cs b/SslStoreCaProxy/Interfaces/IOrganizationResponse.cs
new file mode 100644
index 0000000..489a96a
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IOrganizationResponse.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IOrganizationResponse
+ {
+ AuthResponse AuthResponse { get; set; }
+ List OrganizationList { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IQueryOrderRequest.cs b/SslStoreCaProxy/Interfaces/IQueryOrderRequest.cs
new file mode 100644
index 0000000..5042278
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IQueryOrderRequest.cs
@@ -0,0 +1,20 @@
+using System;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IQueryOrderRequest
+ {
+ AuthRequest AuthRequest { get; set; }
+ DateTime? StartDate { get; set; }
+ DateTime? EndDate { get; set; }
+ DateTime? CertificateExpireToDate { get; set; }
+ DateTime? CertificateExpireFromDate { get; set; }
+ string DomainName { get; set; }
+ string SubUserId { get; set; }
+ string ProductCode { get; set; }
+ string DateTimeCulture { get; set; }
+ long PageNumber { get; set; }
+ long PageSize { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IReIssueRequest.cs b/SslStoreCaProxy/Interfaces/IReIssueRequest.cs
new file mode 100644
index 0000000..72fde8b
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IReIssueRequest.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IReIssueRequest
+ {
+ AuthRequest AuthRequest { get; set; }
+ string TheSslStoreOrderId { get; set; }
+ string Csr { get; set; }
+ string WebServerType { get; set; }
+ List DnsNames { get; set; }
+ bool IsRenewalOrder { get; set; }
+ string SpecialInstructions { get; set; }
+ List EditSan { get; set; }
+ List DeleteSan { get; set; }
+ List AddSan { get; set; }
+ bool IsWildCard { get; set; }
+ string ReissueEmail { get; set; }
+ bool PreferEnrollmentLink { get; set; }
+ string SignatureHashAlgorithm { get; set; }
+ bool FileAuthDvIndicator { get; set; }
+ bool HttpsFileAuthDvIndicator { get; set; }
+ bool CNameAuthDvIndicator { get; set; }
+ string ApproverEmails { get; set; }
+ string DateTimeCulture { get; set; }
+ string CsrUniqueValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IRequestManager.cs b/SslStoreCaProxy/Interfaces/IRequestManager.cs
new file mode 100644
index 0000000..a4bb581
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IRequestManager.cs
@@ -0,0 +1,23 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.Extensions;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IRequestManager
+ {
+ NewOrderRequest GetEnrollmentRequest(string csr, string subject, Dictionary san,
+ EnrollmentProductInfo productInfo, IAnyCAPluginConfigProvider configProvider, bool isRenewalOrder);
+
+ AuthRequest GetAuthRequest();
+ ReIssueRequest GetReIssueRequest(INewOrderResponse orderData, string csr, bool isRenewal);
+ AdminContact GetAdminContact(EnrollmentProductInfo productInfo);
+ TechnicalContact GetTechnicalContact(EnrollmentProductInfo productInfo);
+ DownloadCertificateRequest GetCertificateRequest(string theSslStoreOrderId);
+ RevokeOrderRequest GetRevokeOrderRequest(string theSslStoreOrderId);
+ int GetClientPageSize(IAnyCAPluginConfigProvider config);
+ QueryOrderRequest GetQueryOrderRequest(int pageSize, int pageNumber);
+ OrderStatusRequest GetOrderStatusRequest(string theSslStoreId);
+ int MapReturnStatus(string sslStoreStatus);
+ }
+}
diff --git a/SslStoreCaProxy/Interfaces/IRevokeOrderRequest.cs b/SslStoreCaProxy/Interfaces/IRevokeOrderRequest.cs
new file mode 100644
index 0000000..58ed409
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IRevokeOrderRequest.cs
@@ -0,0 +1,11 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IRevokeOrderRequest
+ {
+ AuthRequest AuthRequest { get; set; }
+ string TheSslStoreOrderId { get; set; }
+ string SerialNumber { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/IRevokeOrderResponse.cs b/SslStoreCaProxy/Interfaces/IRevokeOrderResponse.cs
new file mode 100644
index 0000000..3a9b422
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/IRevokeOrderResponse.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface IRevokeOrderResponse
+ {
+ string InvokingPartnerCode { get; set; }
+ List Message { get; set; }
+ object ReplayToken { get; set; }
+ string Timestamp { get; set; }
+ bool IsError { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/ISslStoreClient.cs b/SslStoreCaProxy/Interfaces/ISslStoreClient.cs
new file mode 100644
index 0000000..fd5cf0a
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/ISslStoreClient.cs
@@ -0,0 +1,30 @@
+using System.Collections.Concurrent;
+using System.Threading;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface ISslStoreClient
+ {
+ Task SubmitNewOrderRequestAsync(NewOrderRequest newOrderRequest);
+
+ Task SubmitReIssueRequestAsync(ReIssueRequest reIssueOrderRequest);
+
+ Task SubmitRenewRequestAsync(NewOrderRequest renewOrderRequest);
+
+ Task SubmitDownloadCertificateAsync(
+ DownloadCertificateRequest downloadOrderRequest);
+
+ Task SubmitQueryOrderRequestAsync(BlockingCollection bc, CancellationToken ct,
+ RequestManager requestManager);
+
+ Task SubmitRevokeCertificateAsync(RevokeOrderRequest revokeOrderRequest);
+
+ Task SubmitOrganizationListAsync(OrganizationListRequest organizationListRequest);
+
+ Task SubmitOrderStatusRequestAsync(OrderStatusRequest orderStatusRequest);
+
+ Task SubmitEmailApproverRequestAsync(EmailApproverRequest newApproverRequest);
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/ITechnicalContact.cs b/SslStoreCaProxy/Interfaces/ITechnicalContact.cs
new file mode 100644
index 0000000..0c8a4f5
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/ITechnicalContact.cs
@@ -0,0 +1,21 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface ITechnicalContact
+ {
+ string FirstName { get; set; }
+ string LastName { get; set; }
+ string SubjectFirstName { get; set; }
+ string SubjectLastName { get; set; }
+ string Phone { get; set; }
+ string Fax { get; set; }
+ string Email { get; set; }
+ string Title { get; set; }
+ string OrganizationName { get; set; }
+ string AddressLine1 { get; set; }
+ string AddressLine2 { get; set; }
+ string City { get; set; }
+ string Region { get; set; }
+ string PostalCode { get; set; }
+ string Country { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/ITemplate.cs b/SslStoreCaProxy/Interfaces/ITemplate.cs
new file mode 100644
index 0000000..1fbbeb5
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/ITemplate.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface ITemplate
+ {
+ int Id { get; set; }
+ string CommonName { get; set; }
+ string TemplateName { get; set; }
+ string Oid { get; set; }
+ string KeySize { get; set; }
+ string KeyType { get; set; }
+ string ForestRoot { get; set; }
+ string FriendlyName { get; set; }
+ string KeyRetention { get; set; }
+ int? KeyRetentionDays { get; set; }
+ bool KeyArchival { get; set; }
+ List EnrollmentFields { get; set; }
+ int AllowedEnrollmentTypes { get; set; }
+ List TemplateRegexes { get; set; }
+ bool UseAllowedRequesters { get; set; }
+ List AllowedRequesters { get; set; }
+ string DisplayName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/ITemplateNewOrderRequest.cs b/SslStoreCaProxy/Interfaces/ITemplateNewOrderRequest.cs
new file mode 100644
index 0000000..bc46601
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/ITemplateNewOrderRequest.cs
@@ -0,0 +1,27 @@
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface ITemplateNewOrderRequest
+ {
+ TemplateAuthRequest AuthRequest { get; set; }
+ ProductCode ProductCode { get; set; }
+ TemplateOrganizationInfo OrganizationInfo { get; set; }
+ ValidityPeriod ValidityPeriod { get; set; }
+ ServerCount ServerCount { get; set; }
+ Csr Csr { get; set; }
+ DomainName DomainName { get; set; }
+ WebServerType WebServerType { get; set; }
+ DnsNames DnsNames { get; set; }
+ AutoWWW AutoWWW { get; set; }
+ IsCuOrder IsCuOrder { get; set; }
+ IsRenewalOrder IsRenewalOrder { get; set; }
+ IsTrialOrder IsTrialOrder { get; set; }
+ TemplateAdminContact AdminContact { get; set; }
+ TemplateTechnicalContact TechnicalContact { get; set; }
+ ApproverEmail ApproverEmail { get; set; }
+ FileAuthDvIndicator FileAuthDvIndicator { get; set; }
+ CNameAuthDvIndicator CNameAuthDvIndicator { get; set; }
+ SignatureHashAlgorithm SignatureHashAlgorithm { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/Interfaces/ITemplateRegex.cs b/SslStoreCaProxy/Interfaces/ITemplateRegex.cs
new file mode 100644
index 0000000..66f2dbe
--- /dev/null
+++ b/SslStoreCaProxy/Interfaces/ITemplateRegex.cs
@@ -0,0 +1,10 @@
+namespace Keyfactor.AnyGateway.SslStore.Interfaces
+{
+ public interface ITemplateRegex
+ {
+ int TemplateId { get; set; }
+ string SubjectPart { get; set; }
+ string Regex { get; set; }
+ string Error { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SslStoreCaProxy/ProductDefinitions.cs b/SslStoreCaProxy/ProductDefinitions.cs
new file mode 100644
index 0000000..331fbd3
--- /dev/null
+++ b/SslStoreCaProxy/ProductDefinitions.cs
@@ -0,0 +1,476 @@
+using System.Collections.Generic;
+using System.Linq;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+
+namespace Keyfactor.AnyGateway.SslStore
+{
+ ///
+ /// Static registry of all SSL Store products and their enrollment fields.
+ /// Template-level properties (Id, OID, KeySize, etc.) are configured in Keyfactor Command.
+ ///
+ public static class ProductDefinitions
+ {
+ private static int _nextFieldId = 1;
+
+ #region Shared Option Lists
+
+ private static readonly List ValidityStandard = new List { "12", "24", "36", "48", "60" };
+ private static readonly List ValidityExtended = new List { "6", "12", "24", "36", "48", "60" };
+ private static readonly List ValidityDigicert = new List { "12", "24", "36", "48", "60", "72" };
+
+ private static readonly List BoolOptions = new List { "False", "True" };
+
+ private static readonly List CountryCodes = new List
+ {
+ "US","AF","AX","AL","DZ","AS","AD","AO","AI","AQ","AG","AR","AM","AW","AU","AT","AZ",
+ "BS","BH","BD","BB","BY","BE","BZ","BJ","BM","BT","BO","BQ","BA","BW","BV","BR","IO",
+ "BN","BG","BF","BI","CV","KH","CM","CA","KY","CF","TD","CL","CN","CX","CC","CO","KM",
+ "CG","CD","CK","CR","CI","HR","CU","CW","CY","CZ","DK","DJ","DM","DO","EC","EG","SV",
+ "GQ","ER","EE","SZ","ET","FK","FO","FJ","FI","FR","GF","PF","TF","GA","GM","GE","DE",
+ "GH","GI","GR","GL","GD","GP","GU","GT","GG","GN","GW","GY","HT","HM","VA","HN","HK",
+ "HU","IS","IN","ID","IR","IQ","IE","IM","IL","IT","JM","JP","JE","JO","KZ","KE","KI",
+ "KP","KR","KW","KG","LA","LV","LB","LS","LR","LY","LI","LT","LU","MO","MG","MW","MY",
+ "MV","ML","MT","MH","MQ","MR","MU","YT","MX","FM","MD","MC","MN","ME","MS","MA","MZ",
+ "MM","NA","NR","NP","NL","NC","NZ","NI","NE","NG","NU","NF","MK","MP","NO","OM","PK",
+ "PW","PS","PA","PG","PY","PE","PH","PN","PL","PT","PR","QA","RE","RO","RU","RW","BL",
+ "SH","KN","LC","MF","PM","VC","WS","SM","ST","SA","SN","RS","SC","SL","SG","SX","SK",
+ "SI","SB","SO","ZA","GS","SS","ES","LK","SD","SR","SJ","SE","CH","SY","TW","TJ","TZ",
+ "TH","TL","TG","TK","TO","TT","TN","TR","TM","TC","TV","UG","UA","AE","GB","UM","UY",
+ "UZ","VU","VE","VN","VG","VI","WF","EH","YE","ZM","ZW"
+ };
+
+ private static readonly List SignatureHashAlgorithms = new List
+ {
+ "PREFER_SHA2", "REQUIRE_SHA2", "PREFER_SHA1", ""
+ };
+
+ private static readonly List WebServerTypes = new List
+ {
+ "aol","apachessl","apacheraven","apachessleay","iis","iis4","iis5","c2net","Ibmhttp",
+ "Ibminternet","Iplanet","Dominogo4625","Dominogo4626","Domino","Netscape",
+ "NetscapeFastTrack","zeusv3","Other","apacheopenssl","apache2","apacheapachessl",
+ "cobaltseries","covalentserver","cpanel","ensim","hsphere","ipswitch","plesk","tomcat",
+ "WebLogic","website","webstar","sapwebserver","webten","redhat","reven","r3ssl","quid",
+ "oracle","javawebserver","cisco3000","citrix"
+ };
+
+ #endregion
+
+ #region Enrollment Field Builders
+
+ private static EnrollmentField TextField(string name)
+ {
+ return new EnrollmentField { Id = _nextFieldId++, Name = name, Options = new List { "" }, DataType = 1 };
+ }
+
+ private static EnrollmentField DropdownField(string name, List options)
+ {
+ return new EnrollmentField { Id = _nextFieldId++, Name = name, Options = new List(options), DataType = 2 };
+ }
+
+ // Group 1: Legacy Full (36 fields) - comodossl, comodoevssl, etc.
+ private static List LegacyFullFields(List validity)
+ {
+ return new List
+ {
+ DropdownField("Is CU Order?", BoolOptions),
+ DropdownField("Is Renewal Order?", BoolOptions),
+ DropdownField("Is Trial Order?", BoolOptions),
+ TextField("Admin Contact - First Name"),
+ TextField("Admin Contact - Last Name"),
+ TextField("Admin Contact - Phone"),
+ TextField("Admin Contact - Email"),
+ TextField("Admin Contact - Organization Name"),
+ TextField("Admin Contact - Address"),
+ TextField("Admin Contact - City"),
+ TextField("Admin Contact - Region"),
+ TextField("Admin Contact - Postal Code"),
+ DropdownField("Admin Contact - Country", CountryCodes),
+ TextField("Technical Contact - First Name"),
+ TextField("Technical Contact - Last Name"),
+ TextField("Technical Contact - Phone"),
+ TextField("Technical Contact - Email"),
+ TextField("Technical Contact - Organization Name"),
+ TextField("Technical Contact - Address"),
+ TextField("Technical Contact - City"),
+ TextField("Technical Contact - Region"),
+ TextField("Technical Contact - Postal Code"),
+ DropdownField("Technical Contact - Country", CountryCodes),
+ TextField("Approver Email"),
+ DropdownField("File Auth Domain Validation", BoolOptions),
+ DropdownField("CName Auth Domain Validation", BoolOptions),
+ DropdownField("Signature Hash Algorithm", SignatureHashAlgorithms),
+ DropdownField("Web Server Type", WebServerTypes),
+ TextField("Server Count"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization Region"),
+ TextField("Organization Postal Code"),
+ TextField("Organization Country"),
+ TextField("Organization Phone")
+ };
+ }
+
+ // Group 1b: Legacy Full + DNS Names + Jurisdiction (38 fields) - digi_quickssl_md
+ private static List LegacyFullDnsJurisdictionFields(List validity)
+ {
+ var fields = new List();
+ fields.AddRange(LegacyFullFields(validity));
+ // Insert Jurisdiction Country before Organization Phone (at the end)
+ fields.Insert(fields.Count - 1, DropdownField("Organization Jurisdiction Country", CountryCodes));
+ return fields;
+ }
+
+ // Group 2: EO Minimal (3 fields) - digi_*-EO products, digi_securesite_pro_flex
+ private static List EoMinimalFields()
+ {
+ return new List
+ {
+
+ TextField("Validity Period (In Days)"),
+ DropdownField("Organization ID", new List())
+ };
+ }
+
+ // Group 3: Sectigo/Comodo OV (9 fields) - instantssl, comodopremiumssl, etc.
+ private static List SectigoOvFields()
+ {
+ return new List
+ {
+ TextField("Admin Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization State/Province"),
+ TextField("Organization Postal Code"),
+ DropdownField("Organization Country", CountryCodes),
+ TextField("Organization Phone")
+ };
+ }
+
+ // Group 4: DigiCert OV Flex (14 fields) - digi_securesite_flex, digi_sslwebserver_flex, etc.
+ private static List DigiCertOvFlexFields()
+ {
+ return new List
+ {
+
+ TextField("Admin Contact - First Name"),
+ TextField("Admin Contact - Last Name"),
+ TextField("Admin Contact - Phone"),
+ TextField("Admin Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization City"),
+ TextField("Organization State/Province"),
+ TextField("Organization Postal Code"),
+ DropdownField("Organization Country", CountryCodes),
+ TextField("Organization Phone")
+ };
+ }
+
+ // Group 5: DV Minimal (3 fields) - positivessl, sectigossl, etc.
+ private static List DvMinimalFields()
+ {
+ return new List
+ {
+ TextField("Admin Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)")
+ };
+ }
+
+ // Group 6: DigiCert EV Flex (15 fields) - digi_securesite_ev_flex, digi_ssl_ev_basic, etc.
+ private static List DigiCertEvFlexFields(string regionFieldName = "Organization State/Province")
+ {
+ return new List
+ {
+
+ TextField("Admin Contact - First Name"),
+ TextField("Admin Contact - Last Name"),
+ TextField("Admin Contact - Phone"),
+ TextField("Admin Contact - Email"),
+ TextField("Admin Contact - Title"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization City"),
+ TextField(regionFieldName),
+ TextField("Organization Postal Code"),
+ DropdownField("Organization Country", CountryCodes),
+ TextField("Organization Phone")
+ };
+ }
+
+ // Group 7: DV MDC (4 fields) - positivemdcssl, sectigodvucc, etc.
+ private static List DvMdcFields()
+ {
+ return new List
+ {
+
+ TextField("Admin Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)")
+ };
+ }
+
+ // Group 8: DigiCert DV RapidSSL (3 fields) - digi_rapidssl, digi_rapidssl_wc
+ private static List DigiCertDvRapidSslFields()
+ {
+ return new List
+ {
+ TextField("Technical Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)")
+ };
+ }
+
+ // Group 9: DigiCert DV GeoTrust/SSL123 (4 fields) - digi_ssl_dv_geotrust_flex, digi_ssl123_flex
+ private static List DigiCertDvGeoTrustFields()
+ {
+ return new List
+ {
+
+ TextField("Technical Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)")
+ };
+ }
+
+ // Group 10: EV with Jurisdiction (10 fields) - enterpriseproev, positiveevssl
+ private static List EvJurisdictionFields()
+ {
+ return new List
+ {
+ TextField("Admin Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization State/Province"),
+ TextField("Organization Postal Code"),
+ DropdownField("Organization Country", CountryCodes),
+ DropdownField("Organization Jurisdiction Country", CountryCodes),
+ TextField("Organization Phone")
+ };
+ }
+
+ // Group 11: EV MDC with Jurisdiction (11 fields) - positiveevmdc, sectigoevmdc
+ private static List EvMdcJurisdictionFields()
+ {
+ return new List
+ {
+
+ TextField("Admin Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization State/Province"),
+ TextField("Organization Postal Code"),
+ DropdownField("Organization Country", CountryCodes),
+ DropdownField("Organization Jurisdiction Country", CountryCodes),
+ TextField("Organization Phone")
+ };
+ }
+
+ // Group 11b: EV MDC with Jurisdiction (Country before Jurisdiction) - enterpriseproevmdc
+ private static List EvMdcJurisdictionAltFields()
+ {
+ return new List
+ {
+
+ TextField("Admin Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization State/Province"),
+ TextField("Organization Postal Code"),
+ DropdownField("Organization Jurisdiction Country", CountryCodes),
+ DropdownField("Organization Country", CountryCodes),
+ TextField("Organization Phone")
+ };
+ }
+
+ // Group 12: OV MDC (10 fields) - sectigomdc, sectigomdcwildcard
+ private static List OvMdcFields()
+ {
+ return new List
+ {
+
+ TextField("Admin Contact - Email"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization State/Province"),
+ TextField("Organization Postal Code"),
+ DropdownField("Organization Country", CountryCodes),
+ TextField("Organization Phone")
+ };
+ }
+
+ // Group 15: Enterprise Pro OV (9 fields) - enterprisepro (Admin First Name instead of Email)
+ private static List EnterpriseProOvFields()
+ {
+ return new List
+ {
+ TextField("Admin Contact - First Name"),
+ TextField("Approver Email"),
+ TextField("Validity Period (In Days)"),
+ TextField("Organization Name"),
+ TextField("Organization Address"),
+ TextField("Organization State/Province"),
+ TextField("Organization Postal Code"),
+ DropdownField("Organization Country", CountryCodes),
+ TextField("Organization Phone")
+ };
+ }
+
+ #endregion
+
+ #region Product Registry
+
+ private static readonly Dictionary> _products = BuildRegistry();
+
+ private static Dictionary> BuildRegistry()
+ {
+ var registry = new Dictionary>();
+
+ // --- Group 1: Legacy Full (validity: 6,12,24,36,48,60) ---
+ foreach (var code in new[]
+ {
+ "comododvucc", "comodoevcsc", "comodoevmdc", "comodoevssl", "comodomdc",
+ "comodomdcwildcard", "comodopciscan", "comodossl", "comodoucc",
+ "comodouccwildcard", "comodowildcard", "digi_client_premium", "digi_csc",
+ "digi_csc_ev", "digi_doc_signing_ind_2000", "digi_doc_signing_ind_500",
+ "digi_doc_signing_org_2000", "digi_doc_signing_org_5000",
+ "elitessl", "enterprisessl", "essentialssl", "essentialwildcard",
+ "hackerprooftm", "hgpcicontrolscan", "pacbasic", "pacpro", "pacenterprise"
+ })
+ {
+ registry[code] = LegacyFullFields(ValidityExtended);
+ }
+
+ // comodocsc: Legacy Full but with standard validity (12,24,36,48,60)
+ registry["comodocsc"] = LegacyFullFields(ValidityStandard);
+
+ // --- Group 1b: Legacy Full + DNS + Jurisdiction ---
+ registry["digi_quickssl_md"] = LegacyFullDnsJurisdictionFields(ValidityExtended);
+
+ // --- Group 2: EO Minimal ---
+ foreach (var code in new[]
+ {
+ "digi_securesite_ev_flex-EO", "digi_securesite_flex-EO",
+ "digi_securesite_pro_ev_flex-EO", "digi_securesite_pro_flex",
+ "digi_securesite_pro_flex-EO", "digi_ssl_basic-EO", "digi_ssl_ev_basic-EO",
+ "digi_sslwebserver_ev_flex-EO", "digi_sslwebserver_flex-EO",
+ "digi_truebizid_ev_flex-EO", "digi_truebizid_flex-EO"
+ })
+ {
+ registry[code] = EoMinimalFields();
+ }
+
+ // --- Group 3: Sectigo/Comodo OV ---
+ foreach (var code in new[]
+ {
+ "comodopremiumssl", "comodopremiumwildcard", "enterpriseprowc",
+ "instantssl", "instantsslpro", "sectigoovssl", "sectigoovwildcard"
+ })
+ {
+ registry[code] = SectigoOvFields();
+ }
+
+ // --- Group 4: DigiCert OV Flex ---
+ foreach (var code in new[]
+ {
+ "digi_securesite_flex", "digi_ssl_basic",
+ "digi_sslwebserver_flex", "digi_truebizid_flex"
+ })
+ {
+ registry[code] = DigiCertOvFlexFields();
+ }
+
+ // --- Group 5: DV Minimal ---
+ foreach (var code in new[]
+ {
+ "positivessl", "positivesslwildcard", "sectigoevssl",
+ "sectigossl", "sectigowildcard"
+ })
+ {
+ registry[code] = DvMinimalFields();
+ }
+
+ // --- Group 6: DigiCert EV Flex (State/Province) ---
+ foreach (var code in new[]
+ {
+ "digi_securesite_ev_flex", "digi_ssl_ev_basic",
+ "digi_sslwebserver_ev_flex", "digi_truebizid_ev_flex"
+ })
+ {
+ registry[code] = DigiCertEvFlexFields();
+ }
+
+ // Group 6b: DigiCert EV Flex with "Organization Region" instead of "State/Province"
+ registry["digi_securesite_pro_ev_flex"] = DigiCertEvFlexFields("Organization Region");
+
+ // --- Group 7: DV MDC ---
+ foreach (var code in new[]
+ {
+ "positivemdcssl", "positivemdcwildcard", "sectigodvucc", "sectigouccwildcard"
+ })
+ {
+ registry[code] = DvMdcFields();
+ }
+
+ // --- Group 8: DigiCert DV RapidSSL ---
+ registry["digi_rapidssl"] = DigiCertDvRapidSslFields();
+ registry["digi_rapidssl_wc"] = DigiCertDvRapidSslFields();
+
+ // --- Group 9: DigiCert DV GeoTrust/SSL123 ---
+ registry["digi_ssl_dv_geotrust_flex"] = DigiCertDvGeoTrustFields();
+ registry["digi_ssl123_flex"] = DigiCertDvGeoTrustFields();
+
+ // --- Group 10: EV with Jurisdiction ---
+ registry["enterpriseproev"] = EvJurisdictionFields();
+ registry["positiveevssl"] = EvJurisdictionFields();
+
+ // --- Group 11: EV MDC with Jurisdiction ---
+ registry["positiveevmdc"] = EvMdcJurisdictionFields();
+ registry["sectigoevmdc"] = EvMdcJurisdictionFields();
+
+ // --- Group 11b: EV MDC Jurisdiction (alt field order) ---
+ registry["enterpriseproevmdc"] = EvMdcJurisdictionAltFields();
+
+ // --- Group 12: OV MDC ---
+ registry["sectigomdc"] = OvMdcFields();
+ registry["sectigomdcwildcard"] = OvMdcFields();
+
+ // --- Group 15: Enterprise Pro OV ---
+ registry["enterprisepro"] = EnterpriseProOvFields();
+
+ return registry;
+ }
+
+ #endregion
+
+ #region Public API
+
+ public static List GetProductIds()
+ {
+ return _products.Keys.ToList();
+ }
+
+ public static List GetEnrollmentFields(string productCode)
+ {
+ return _products.TryGetValue(productCode, out var fields) ? fields : null;
+ }
+
+ #endregion
+ }
+}
diff --git a/SslStoreCaProxy/RequestManager.cs b/SslStoreCaProxy/RequestManager.cs
new file mode 100644
index 0000000..30718f8
--- /dev/null
+++ b/SslStoreCaProxy/RequestManager.cs
@@ -0,0 +1,392 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Text.RegularExpressions;
+using Keyfactor.AnyGateway.Extensions;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Keyfactor.Logging;
+using Keyfactor.PKI.Enums.EJBCA;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+
+namespace Keyfactor.AnyGateway.SslStore
+{
+ public class RequestManager : IRequestManager
+ {
+ private static readonly ILogger _logger = LogHandler.GetClassLogger();
+ private readonly SslStoreCaProxy _sslStoreCaProxy;
+
+ public RequestManager(SslStoreCaProxy sslStoreCaProxy)
+ {
+ _sslStoreCaProxy = sslStoreCaProxy;
+ }
+
+ public NewOrderRequest GetEnrollmentRequest(string csr, string subject, Dictionary san,
+ EnrollmentProductInfo productInfo, IAnyCAPluginConfigProvider configProvider, bool isRenewalOrder)
+ {
+ var pemCsr = ConvertCsrToPem(csr);
+ return BuildNewOrderRequest(productInfo, pemCsr, subject, san, isRenewalOrder);
+ }
+
+ private string ConvertCsrToPem(string csr)
+ {
+ try
+ {
+ var csrBytes = Convert.FromBase64String(csr);
+ var base64 = Convert.ToBase64String(csrBytes);
+ var sb = new StringBuilder();
+ sb.AppendLine("-----BEGIN CERTIFICATE REQUEST-----");
+ for (int i = 0; i < base64.Length; i += 64)
+ {
+ sb.AppendLine(base64.Substring(i, Math.Min(64, base64.Length - i)));
+ }
+ sb.AppendLine("-----END CERTIFICATE REQUEST-----");
+ return sb.ToString();
+ }
+ catch
+ {
+ return csr;
+ }
+ }
+
+ public EmailApproverRequest GetEmailApproverListRequest(string productId, string productName)
+ {
+ return new EmailApproverRequest()
+ {
+ AuthRequest = GetAuthRequest(),
+ ProductCode = productId,
+ DomainName = productName
+ };
+ }
+
+ public OrganizationListRequest GetOrganizationListRequest()
+ {
+ return new OrganizationListRequest()
+ {
+ PartnerCode = _sslStoreCaProxy.PartnerCode,
+ AuthToken = _sslStoreCaProxy.AuthenticationToken
+ };
+ }
+
+ public AuthRequest GetAuthRequest()
+ {
+ return new AuthRequest
+ {
+ PartnerCode = _sslStoreCaProxy.PartnerCode,
+ AuthToken = _sslStoreCaProxy.AuthenticationToken
+ };
+ }
+
+ public ReIssueRequest GetReIssueRequest(INewOrderResponse orderData, string csr, bool isRenewal)
+ {
+ return new ReIssueRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ TheSslStoreOrderId = orderData.TheSslStoreOrderId,
+ CustomOrderId = Guid.NewGuid().ToString(),
+ Csr = csr,
+ IsRenewalOrder = isRenewal,
+ IsWildCard = orderData.ProductCode.Contains("wc") || orderData.ProductCode.Contains("wildcard"),
+ ReissueEmail = orderData.AdminContact.Email,
+ ApproverEmails = orderData.ApproverEmail,
+ PreferEnrollmentLink = false,
+ FileAuthDvIndicator = orderData.OrderStatus.DomainAuthVettingStatus == null ? false : orderData.OrderStatus.DomainAuthVettingStatus.Exists(x => x.FileName != null),
+ CNameAuthDvIndicator = orderData.OrderStatus.DomainAuthVettingStatus == null ? false : orderData.OrderStatus.DomainAuthVettingStatus.Exists(x => x.DnsName != null),
+ WebServerType = orderData.WebServerType
+ };
+ }
+
+ public AdminContact GetAdminContact(EnrollmentProductInfo productInfo)
+ {
+ return new AdminContact
+ {
+ FirstName = productInfo.ProductParameters["Admin Contact - First Name"],
+ LastName = productInfo.ProductParameters["Admin Contact - Last Name"],
+ Phone = productInfo.ProductParameters["Admin Contact - Phone"],
+ Email = productInfo.ProductParameters["Admin Contact - Email"],
+ OrganizationName = productInfo.ProductParameters["Admin Contact - Organization Name"],
+ AddressLine1 = productInfo.ProductParameters["Admin Contact - Address"],
+ City = productInfo.ProductParameters["Admin Contact - City"],
+ Region = productInfo.ProductParameters["Admin Contact - Region"],
+ PostalCode = productInfo.ProductParameters["Admin Contact - Postal Code"],
+ Country = productInfo.ProductParameters["Admin Contact - Country"]
+ };
+ }
+
+ public TechnicalContact GetTechnicalContact(EnrollmentProductInfo productInfo)
+ {
+ return new TechnicalContact
+ {
+ FirstName = productInfo.ProductParameters["Technical Contact - First Name"],
+ LastName = productInfo.ProductParameters["Technical Contact - Last Name"],
+ Phone = productInfo.ProductParameters["Technical Contact - Phone"],
+ Email = productInfo.ProductParameters["Technical Contact - Email"],
+ OrganizationName = productInfo.ProductParameters["Technical Contact - Organization Name"],
+ AddressLine1 = productInfo.ProductParameters["Technical Contact - Address"],
+ City = productInfo.ProductParameters["Technical Contact - City"],
+ Region = productInfo.ProductParameters["Technical Contact - Region"],
+ PostalCode = productInfo.ProductParameters["Technical Contact - Postal Code"],
+ Country = productInfo.ProductParameters["Technical Contact - Country"]
+ };
+ }
+
+ public DownloadCertificateRequest GetCertificateRequest(string customOrderId)
+ {
+ return new DownloadCertificateRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ CustomOrderId = customOrderId
+ };
+ }
+
+ public DownloadCertificateRequest GetCertificateRequestBySslStoreId(string theSslStoreOrderId)
+ {
+ return new DownloadCertificateRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ TheSslStoreOrderId = theSslStoreOrderId
+ };
+ }
+
+ public RevokeOrderRequest GetRevokeOrderRequest(string customOrderId)
+ {
+ return new RevokeOrderRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ CustomOrderId = customOrderId
+ };
+ }
+
+ public RevokeOrderRequest GetRevokeOrderRequestBySslStoreId(string theSslStoreOrderId)
+ {
+ return new RevokeOrderRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ TheSslStoreOrderId = theSslStoreOrderId
+ };
+ }
+
+ public int GetClientPageSize(IAnyCAPluginConfigProvider config)
+ {
+ if (config.CAConnectionData.ContainsKey(Constants.PageSize))
+ return int.Parse(config.CAConnectionData[Constants.PageSize].ToString());
+ return Constants.DefaultPageSize;
+ }
+
+ public QueryOrderRequest GetQueryOrderRequest(int pageSize, int pageNumber)
+ {
+ return new QueryOrderRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ PageSize = pageSize,
+ PageNumber = pageNumber
+ };
+ }
+
+ public OrderStatusRequest GetOrderStatusRequest(string customOrderId)
+ {
+ return new OrderStatusRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ CustomOrderId = customOrderId
+ };
+ }
+
+ public OrderStatusRequest GetOrderStatusRequestBySslStoreId(string theSslStoreOrderId)
+ {
+ return new OrderStatusRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ TheSslStoreOrderId = theSslStoreOrderId
+ };
+ }
+
+ public int MapReturnStatus(string sslStoreStatus)
+ {
+ switch (sslStoreStatus)
+ {
+ case "Active":
+ return (int)EndEntityStatus.GENERATED;
+ case "Initial":
+ case "Pending":
+ return (int)EndEntityStatus.EXTERNALVALIDATION;
+ case "Cancelled":
+ return (int)EndEntityStatus.REVOKED;
+ default:
+ return (int)EndEntityStatus.NEW;
+ }
+ }
+
+ public NewOrderRequest GetRenewalRequest(INewOrderResponse orderData, string csr)
+ {
+ return new NewOrderRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ CustomOrderId = Guid.NewGuid().ToString(),
+ RelatedTheSslStoreOrderId = orderData.TheSslStoreOrderId,
+ ProductCode = orderData.ProductCode,
+ AdminContact = GetAdminContact(orderData),
+ TechnicalContact = GetTechnicalContact(orderData),
+ ApproverEmail = orderData.ApproverEmail,
+ SignatureHashAlgorithm = orderData.SignatureHashAlgorithm,
+ WebServerType = orderData.WebServerType,
+ ValidityPeriod = orderData.Validity,
+ ServerCount = orderData.ServerCount,
+ IsRenewalOrder = true,
+ FileAuthDvIndicator = orderData.OrderStatus?.DomainAuthVettingStatus?.Exists(x => x.FileName != null),
+ CnameAuthDvIndicator = orderData.OrderStatus?.DomainAuthVettingStatus?.Exists(x => x.DnsName != null),
+ Csr = csr
+ };
+ }
+
+ public AdminContact GetAdminContact(INewOrderResponse productInfo)
+ {
+ return new AdminContact
+ {
+ FirstName = productInfo.AdminContact.FirstName,
+ LastName = productInfo.AdminContact.LastName,
+ Phone = productInfo.AdminContact.Phone,
+ Email = productInfo.AdminContact.Email
+ };
+ }
+
+ public TechnicalContact GetTechnicalContact(INewOrderResponse productInfo)
+ {
+ return new TechnicalContact
+ {
+ FirstName = productInfo.AdminContact.FirstName,
+ LastName = productInfo.AdminContact.LastName,
+ Phone = productInfo.AdminContact.Phone,
+ Email = productInfo.AdminContact.Email
+ };
+ }
+
+ private NewOrderRequest BuildNewOrderRequest(EnrollmentProductInfo productInfo,
+ string csr, string subject, Dictionary san, bool isRenewal)
+ {
+ var p = productInfo.ProductParameters;
+
+ // Extract domain name from CSR subject CN
+ var domainName = subject?.Split(',')
+ .Select(part => part.Trim())
+ .Where(part => part.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
+ .Select(part => part.Substring(3))
+ .FirstOrDefault() ?? "";
+
+ // Extract DNS SANs from Keyfactor san parameter
+ var dnsNames = san != null && san.ContainsKey("dnsname") ? san["dnsname"].ToList() : new List();
+
+ return new NewOrderRequest
+ {
+ AuthRequest = GetAuthRequest(),
+ ProductCode = productInfo.ProductID.Replace("-EO", ""),
+ CustomOrderId = Guid.NewGuid().ToString(),
+ TssOrganizationId = p.ContainsKey("Organization ID") ? long.TryParse(ExtractOrgId(p["Organization ID"]), out var orgId) ? orgId : 0 : 0,
+ OrganizationInfo = new OrganizationInfo
+ {
+ OrganizationName = GetParam(p, "Organization Name"),
+ JurisdictionCountry = GetParam(p, "Organization Jurisdiction Country"),
+ OrganizationAddress = new OrganizationAddress
+ {
+ AddressLine1 = GetParam(p, "Organization Address"),
+ Region = GetParam(p, "Organization Region") ?? GetParam(p, "Organization State/Province"),
+ PostalCode = GetParam(p, "Organization Postal Code"),
+ Country = GetParam(p, "Organization Country"),
+ Phone = GetParam(p, "Organization Phone"),
+ LocalityName = GetParam(p, "Organization City")
+ }
+ },
+ ValidityPeriod = ConvertDaysToMonths(productInfo),
+ ServerCount = 1,
+ Csr = csr,
+ DomainName = domainName,
+ WebServerType = GetParam(p, "Web Server Type") ?? "Other",
+ DnsNames = dnsNames,
+ IsCuOrder = false,
+ IsRenewalOrder = isRenewal,
+ IsTrialOrder = false,
+ AdminContact = new AdminContact
+ {
+ FirstName = GetParam(p, "Admin Contact - First Name"),
+ LastName = GetParam(p, "Admin Contact - Last Name"),
+ Phone = GetParam(p, "Admin Contact - Phone"),
+ Email = GetParam(p, "Admin Contact - Email"),
+ Title = GetParam(p, "Admin Contact - Title"),
+ OrganizationName = GetParam(p, "Admin Contact - Organization Name"),
+ AddressLine1 = GetParam(p, "Admin Contact - Address"),
+ City = GetParam(p, "Admin Contact - City"),
+ Region = GetParam(p, "Admin Contact - Region"),
+ PostalCode = GetParam(p, "Admin Contact - Postal Code"),
+ Country = GetParam(p, "Admin Contact - Country")
+ },
+ TechnicalContact = new TechnicalContact
+ {
+ FirstName = GetParam(p, "Technical Contact - First Name"),
+ LastName = GetParam(p, "Technical Contact - Last Name"),
+ Phone = GetParam(p, "Technical Contact - Phone"),
+ Email = GetParam(p, "Technical Contact - Email"),
+ Title = GetParam(p, "Technical Contact - Title"),
+ OrganizationName = GetParam(p, "Technical Contact - Organization Name"),
+ AddressLine1 = GetParam(p, "Technical Contact - Address"),
+ City = GetParam(p, "Technical Contact - City"),
+ Region = GetParam(p, "Technical Contact - Region"),
+ PostalCode = GetParam(p, "Technical Contact - Postal Code"),
+ Country = GetParam(p, "Technical Contact - Country")
+ },
+ ApproverEmail = GetParam(p, "Approver Email"),
+ FileAuthDvIndicator = false,
+ CnameAuthDvIndicator = false,
+ SignatureHashAlgorithm = GetParam(p, "Signature Hash Algorithm") ?? "PREFER_SHA2"
+ };
+ }
+
+ private static string GetParam(Dictionary parameters, string key)
+ {
+ return parameters.ContainsKey(key) ? parameters[key] : null;
+ }
+
+ public string GetCertificateContent(List certificates, string commonName)
+ {
+ foreach (var c in certificates)
+ {
+ var cert = new X509Certificate2(Encoding.UTF8.GetBytes(c.FileContent));
+ if (cert.SubjectName.Name != null && cert.SubjectName.Name.Contains(commonName)) return c.FileContent;
+ }
+
+ return "";
+ }
+
+ private long ConvertDaysToMonths(EnrollmentProductInfo productInfo)
+ {
+ if (productInfo.ProductParameters.ContainsKey("Validity Period (In Days)") &&
+ long.TryParse(productInfo.ProductParameters["Validity Period (In Days)"], out var days))
+ {
+ // Round to nearest standard month value (30.44 days/month average)
+ // SSL Store only accepts specific values like 1, 3, 6, 12, 24, 36, etc.
+ var months = Math.Max(1, (long)Math.Round(days / 30.44));
+ _logger.LogTrace($"Validity conversion: {days} days -> {months} months");
+ return months;
+ }
+
+ _logger.LogWarning("Validity Period (In Days) not found or invalid, defaulting to 12 months");
+ return 12;
+ }
+
+ private string ExtractOrgId(string organization)
+ {
+ if (organization != null)
+ {
+ Regex pattern = new Regex(@"(\([^0-9]*\d+[^0-9]*\))");
+ Match match = pattern.Match(organization);
+ return match.Value.Replace("(", "").Replace(")", "");
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/SslStoreCaProxy/SslStoreCAPluginConfig.cs b/SslStoreCaProxy/SslStoreCAPluginConfig.cs
new file mode 100644
index 0000000..2c375a9
--- /dev/null
+++ b/SslStoreCaProxy/SslStoreCAPluginConfig.cs
@@ -0,0 +1,373 @@
+using Keyfactor.AnyGateway.Extensions;
+using System.Collections.Generic;
+
+namespace Keyfactor.AnyGateway.SslStore
+{
+ public class SslStoreCAPluginConfig
+ {
+ public const int DefaultPageSize = 100;
+
+ public class ConfigConstants
+ {
+ public static string SSLStoreURL = "SSLStoreURL";
+ public static string PartnerCode = "PartnerCode";
+ public static string AuthToken = "AuthToken";
+ public static string PageSize = "PageSize";
+ public static string Enabled = "Enabled";
+ public static string RenewalWindow = "RenewalWindow";
+ }
+
+ public class Config
+ {
+ public string SSLStoreURL { get; set; }
+ public string PartnerCode { get; set; }
+ public string AuthToken { get; set; }
+ public int PageSize { get; set; } = DefaultPageSize;
+ public bool Enabled { get; set; }
+ public int RenewalWindow { get; set; } = 30;
+ }
+
+ public static Dictionary GetPluginAnnotations()
+ {
+ return new Dictionary()
+ {
+ [ConfigConstants.SSLStoreURL] = new PropertyConfigInfo()
+ {
+ Comments = "The Base URL for the SSL Store API endpoint (e.g. https://sandbox-wbapi.thesslstore.com).",
+ Hidden = false,
+ DefaultValue = "https://sandbox-wbapi.thesslstore.com",
+ Type = "String"
+ },
+ [ConfigConstants.PartnerCode] = new PropertyConfigInfo()
+ {
+ Comments = "The Partner Code obtained from SSL Store.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ [ConfigConstants.AuthToken] = new PropertyConfigInfo()
+ {
+ Comments = "The Authentication Token obtained from SSL Store.",
+ Hidden = true,
+ DefaultValue = "",
+ Type = "Secret"
+ },
+ [ConfigConstants.PageSize] = new PropertyConfigInfo()
+ {
+ Comments = "The number of records to return per page during synchronization.",
+ Hidden = false,
+ DefaultValue = DefaultPageSize,
+ Type = "Number"
+ },
+ [ConfigConstants.Enabled] = new PropertyConfigInfo()
+ {
+ Comments = "Flag to Enable or Disable the CA connector.",
+ Hidden = false,
+ DefaultValue = true,
+ Type = "Bool"
+ },
+ [ConfigConstants.RenewalWindow] = new PropertyConfigInfo()
+ {
+ Comments = "Number of days before order expiry to trigger a renewal instead of a reissue.",
+ Hidden = false,
+ DefaultValue = 30,
+ Type = "Number"
+ }
+ };
+ }
+
+ public static Dictionary GetTemplateParameterAnnotations()
+ {
+ return new Dictionary()
+ {
+ ["Approver Email"] = new PropertyConfigInfo()
+ {
+ Comments = "Comma-separated approver email address(es) for domain validation.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Validity Period (In Days)"] = new PropertyConfigInfo()
+ {
+ Comments = "Certificate validity period in days (e.g. 90, 365, 730).",
+ Hidden = false,
+ DefaultValue = "365",
+ Type = "String"
+ },
+ ["Admin Contact - First Name"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact first name.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Last Name"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact last name.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Phone"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact phone number.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Email"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact email address.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Title"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact job title.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Organization Name"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact organization name.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Address"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact street address.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - City"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact city.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Region"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact state/province/region.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Postal Code"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact postal/zip code.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Admin Contact - Country"] = new PropertyConfigInfo()
+ {
+ Comments = "Administrative contact two-letter country code (e.g. US).",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - First Name"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact first name.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - Last Name"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact last name.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - Phone"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact phone number.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - Email"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact email address.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - Organization Name"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact organization name.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - Address"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact street address.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - City"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact city.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - Region"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact state/province/region.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - Postal Code"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact postal/zip code.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Technical Contact - Country"] = new PropertyConfigInfo()
+ {
+ Comments = "Technical contact two-letter country code (e.g. US).",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization Name"] = new PropertyConfigInfo()
+ {
+ Comments = "Organization name for the certificate.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization Address"] = new PropertyConfigInfo()
+ {
+ Comments = "Organization street address.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization City"] = new PropertyConfigInfo()
+ {
+ Comments = "Organization city.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization Region"] = new PropertyConfigInfo()
+ {
+ Comments = "Organization state/province/region.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization State/Province"] = new PropertyConfigInfo()
+ {
+ Comments = "Organization state or province.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization Postal Code"] = new PropertyConfigInfo()
+ {
+ Comments = "Organization postal/zip code.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization Country"] = new PropertyConfigInfo()
+ {
+ Comments = "Organization two-letter country code (e.g. US).",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization Phone"] = new PropertyConfigInfo()
+ {
+ Comments = "Organization phone number.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization Jurisdiction Country"] = new PropertyConfigInfo()
+ {
+ Comments = "Jurisdiction country code for EV certificates.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Organization ID"] = new PropertyConfigInfo()
+ {
+ Comments = "DigiCert organization ID for EO (Enterprise Organization) products.",
+ Hidden = false,
+ DefaultValue = "",
+ Type = "String"
+ },
+ ["Server Count"] = new PropertyConfigInfo()
+ {
+ Comments = "Number of server licenses for the certificate.",
+ Hidden = false,
+ DefaultValue = "1",
+ Type = "String"
+ },
+ ["Web Server Type"] = new PropertyConfigInfo()
+ {
+ Comments = "Web server type (e.g. apacheopenssl, iis, tomcat, Other).",
+ Hidden = false,
+ DefaultValue = "Other",
+ Type = "String"
+ },
+ ["Signature Hash Algorithm"] = new PropertyConfigInfo()
+ {
+ Comments = "Signature hash algorithm (PREFER_SHA2, REQUIRE_SHA2, PREFER_SHA1).",
+ Hidden = false,
+ DefaultValue = "PREFER_SHA2",
+ Type = "String"
+ },
+ ["File Auth Domain Validation"] = new PropertyConfigInfo()
+ {
+ Comments = "Use file-based domain validation (True/False).",
+ Hidden = false,
+ DefaultValue = "False",
+ Type = "String"
+ },
+ ["CName Auth Domain Validation"] = new PropertyConfigInfo()
+ {
+ Comments = "Use CNAME-based domain validation (True/False).",
+ Hidden = false,
+ DefaultValue = "False",
+ Type = "String"
+ },
+ ["Is CU Order?"] = new PropertyConfigInfo()
+ {
+ Comments = "Is this a CU (Customer) order (True/False).",
+ Hidden = false,
+ DefaultValue = "False",
+ Type = "String"
+ },
+ ["Is Renewal Order?"] = new PropertyConfigInfo()
+ {
+ Comments = "Is this a renewal order (True/False).",
+ Hidden = false,
+ DefaultValue = "False",
+ Type = "String"
+ },
+ ["Is Trial Order?"] = new PropertyConfigInfo()
+ {
+ Comments = "Is this a trial order (True/False).",
+ Hidden = false,
+ DefaultValue = "False",
+ Type = "String"
+ }
+ };
+ }
+ }
+}
diff --git a/SslStoreCaProxy/SslStoreCaProxy.cs b/SslStoreCaProxy/SslStoreCaProxy.cs
new file mode 100644
index 0000000..04bd232
--- /dev/null
+++ b/SslStoreCaProxy/SslStoreCaProxy.cs
@@ -0,0 +1,521 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Keyfactor.AnyGateway.Extensions;
+using Keyfactor.AnyGateway.SslStore.Client;
+using Keyfactor.AnyGateway.SslStore.Client.Models;
+using Keyfactor.AnyGateway.SslStore.Interfaces;
+using Keyfactor.Logging;
+using Keyfactor.PKI.Enums.EJBCA;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using System.Linq;
+using Keyfactor.PKI.X509;
+
+
+namespace Keyfactor.AnyGateway.SslStore
+{
+ public class SslStoreCaProxy : IAnyCAPlugin
+ {
+ private static readonly ILogger _logger = LogHandler.GetClassLogger();
+ private RequestManager _requestManager;
+ private IAnyCAPluginConfigProvider Config { get; set; }
+ private ICertificateDataReader _certDataReader;
+ private SslStoreCAPluginConfig.Config _config;
+
+ public string PartnerCode { get; set; }
+ public string AuthenticationToken { get; set; }
+ public int PageSize { get; set; }
+ public int RenewalWindow { get; set; }
+
+ public void Initialize(IAnyCAPluginConfigProvider configProvider, ICertificateDataReader certificateDataReader)
+ {
+ _logger.MethodEntry();
+ try
+ {
+ _certDataReader = certificateDataReader;
+ Config = configProvider;
+ var rawData = JsonConvert.SerializeObject(configProvider.CAConnectionData);
+ _config = JsonConvert.DeserializeObject(rawData);
+
+ PartnerCode = _config.PartnerCode;
+ AuthenticationToken = _config.AuthToken;
+ PageSize = _config.PageSize > 0 ? _config.PageSize : SslStoreCAPluginConfig.DefaultPageSize;
+ RenewalWindow = _config.RenewalWindow > 0 ? _config.RenewalWindow : 30;
+
+ _requestManager = new RequestManager(this);
+
+ _logger.LogTrace($"Initialize - Enabled: {_config.Enabled}");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Failed to initialize SslStore CAPlugin: {ex}");
+ throw;
+ }
+ }
+
+ public async Task Ping()
+ {
+ _logger.MethodEntry();
+ if (!_config.Enabled)
+ {
+ _logger.LogWarning("The CA is currently in the Disabled state. Skipping connectivity test...");
+ _logger.MethodExit();
+ return;
+ }
+ _logger.LogDebug("Pinging SslStore to validate connection");
+ _logger.MethodExit();
+ }
+
+ public Task ValidateCAConnectionInfo(Dictionary connectionInfo)
+ {
+ _logger.MethodEntry();
+ _logger.LogDebug("Validating SslStore CA Connection properties");
+ var rawData = JsonConvert.SerializeObject(connectionInfo);
+ var config = JsonConvert.DeserializeObject(rawData);
+
+ if (!config.Enabled)
+ {
+ _logger.LogWarning("The CA is currently in the Disabled state. Skipping config validation...");
+ _logger.MethodExit();
+ return Task.CompletedTask;
+ }
+
+ List missingFields = new List();
+ if (string.IsNullOrEmpty(config.SSLStoreURL)) missingFields.Add(nameof(config.SSLStoreURL));
+ if (string.IsNullOrEmpty(config.PartnerCode)) missingFields.Add(nameof(config.PartnerCode));
+ if (string.IsNullOrEmpty(config.AuthToken)) missingFields.Add(nameof(config.AuthToken));
+
+ if (missingFields.Count > 0)
+ {
+ throw new ArgumentException($"The following required fields are missing or empty: {string.Join(", ", missingFields)}");
+ }
+
+ _config = config;
+ _logger.MethodExit();
+ return Ping();
+ }
+
+ public Task ValidateProductInfo(EnrollmentProductInfo productInfo, Dictionary connectionInfo)
+ {
+ _logger.MethodEntry();
+ _logger.MethodExit();
+ return Task.CompletedTask;
+ }
+
+ public List GetProductIds()
+ {
+ return ProductDefinitions.GetProductIds();
+ }
+
+ public Dictionary GetCAConnectorAnnotations()
+ {
+ _logger.MethodEntry();
+ _logger.MethodExit();
+ return SslStoreCAPluginConfig.GetPluginAnnotations();
+ }
+
+ public Dictionary GetTemplateParameterAnnotations()
+ {
+ _logger.MethodEntry();
+ _logger.MethodExit();
+ return SslStoreCAPluginConfig.GetTemplateParameterAnnotations();
+ }
+
+ public async Task Revoke(string caRequestId, string hexSerialNumber, uint revocationReason)
+ {
+ _logger.MethodEntry();
+ var sslStoreOrderId = ParseSslStoreOrderId(caRequestId);
+ RevokeOrderRequest revokeOrderRequest;
+ if (sslStoreOrderId != null)
+ {
+ revokeOrderRequest = _requestManager.GetRevokeOrderRequestBySslStoreId(sslStoreOrderId);
+ }
+ else
+ {
+ revokeOrderRequest = _requestManager.GetRevokeOrderRequest(caRequestId);
+ }
+ _logger.LogTrace($"Revoke Request JSON {JsonConvert.SerializeObject(revokeOrderRequest)}");
+ try
+ {
+ var client = new SslStoreClient(Config);
+ var requestResponse = await client.SubmitRevokeCertificateAsync(revokeOrderRequest);
+
+ _logger.LogTrace($"Revoke Response JSON {JsonConvert.SerializeObject(requestResponse)}");
+
+ if (requestResponse.AuthResponse.IsError)
+ {
+ _logger.LogError("Revoke Error Occurred");
+ _logger.MethodExit();
+ return (int)EndEntityStatus.FAILED;
+ }
+
+ _logger.MethodExit();
+ return (int)EndEntityStatus.REVOKED;
+ }
+ catch (Exception e)
+ {
+ _logger.LogError($"An Error has occurred during the revoke process {e.Message}");
+ return (int)EndEntityStatus.FAILED;
+ }
+ }
+
+ public async Task Enroll(string csr, string subject, Dictionary san,
+ EnrollmentProductInfo productInfo, RequestFormat requestFormat, EnrollmentType enrollmentType)
+ {
+ _logger.MethodEntry();
+ var client = new SslStoreClient(Config);
+
+ try
+ {
+ INewOrderResponse enrollmentResponse = null;
+
+ if (enrollmentType == EnrollmentType.New)
+ {
+ _logger.LogTrace("Entering New Enrollment");
+
+ if (!productInfo.ProductParameters.ContainsKey("PriorCertSN"))
+ {
+ // Extract domain name from CSR subject and SANs from the Keyfactor san parameter
+ var domainName = subject?.Split(',')
+ .Select(p => p.Trim())
+ .Where(p => p.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
+ .Select(p => p.Substring(3))
+ .FirstOrDefault() ?? "";
+ _logger.LogTrace($"Domain Name from subject: {domainName}");
+
+ var dnsNames = san != null && san.ContainsKey("dns") ? san["dns"] : Array.Empty();
+ _logger.LogTrace($"DNS Names from SAN: {string.Join(",", dnsNames)}");
+
+ string[] arrayApproverEmails = Array.Empty();
+ if (productInfo.ProductParameters.ContainsKey("Approver Email"))
+ {
+ _logger.LogTrace($"Approver Email {productInfo.ProductParameters["Approver Email"]}");
+ arrayApproverEmails = productInfo.ProductParameters["Approver Email"].Split(new char[] { ',' });
+ }
+
+ // Validate approver emails against all domains (CN + SANs)
+ var allDomains = new List();
+ if (!string.IsNullOrEmpty(domainName)) allDomains.Add(domainName);
+ allDomains.AddRange(dnsNames.Where(d => !string.Equals(d, domainName, StringComparison.OrdinalIgnoreCase)));
+
+ var count = 1;
+ foreach (var domain in allDomains)
+ {
+ var emailApproverRequest = _requestManager.GetEmailApproverListRequest(productInfo.ProductID, domain);
+ _logger.LogTrace($"Email Approver Request JSON {JsonConvert.SerializeObject(emailApproverRequest)}");
+
+ var emailApproverResponse = await client.SubmitEmailApproverRequestAsync(emailApproverRequest);
+ _logger.LogTrace($"Email Approver Response JSON {JsonConvert.SerializeObject(emailApproverResponse)}");
+
+ var emailValidation = ValidateEmails(emailApproverResponse, arrayApproverEmails, productInfo, count);
+ _logger.LogTrace($"Email Validation Result {emailValidation}");
+
+ if (emailValidation.Length > 0)
+ {
+ return new EnrollmentResult
+ {
+ Status = (int)EndEntityStatus.FAILED,
+ StatusMessage = emailValidation
+ };
+ }
+ count++;
+ }
+
+ var enrollmentRequest = _requestManager.GetEnrollmentRequest(csr, subject, san, productInfo, Config, false);
+ _logger.LogTrace($"enrollmentRequest JSON {JsonConvert.SerializeObject(enrollmentRequest)}");
+
+ enrollmentResponse = await client.SubmitNewOrderRequestAsync(enrollmentRequest);
+ _logger.LogTrace($"enrollmentResponse JSON {JsonConvert.SerializeObject(enrollmentResponse)}");
+ }
+ else
+ {
+ return new EnrollmentResult
+ {
+ Status = (int)EndEntityStatus.FAILED,
+ StatusMessage = "You cannot renew an expired cert please perform a new enrollment."
+ };
+ }
+ }
+ else if (enrollmentType == EnrollmentType.RenewOrReissue)
+ {
+ _logger.LogTrace("Entering Renew/Reissue Logic...");
+
+ var sn = productInfo.ProductParameters["PriorCertSN"];
+ _logger.LogTrace($"Prior Cert Serial Number: {sn}");
+
+ var caRequestId = await _certDataReader.GetRequestIDBySerialNumber(sn);
+ _logger.LogTrace($"Prior CA Request ID: {caRequestId}");
+
+ var priorSslStoreOrderId = ParseSslStoreOrderId(caRequestId);
+ OrderStatusRequest orderStatusRequest;
+ if (priorSslStoreOrderId != null)
+ {
+ _logger.LogTrace($"Parsed TheSSLStoreOrderID: {priorSslStoreOrderId}");
+ orderStatusRequest = _requestManager.GetOrderStatusRequestBySslStoreId(priorSslStoreOrderId);
+ }
+ else
+ {
+ _logger.LogTrace($"Legacy GUID format, querying by CustomOrderId: {caRequestId}");
+ orderStatusRequest = _requestManager.GetOrderStatusRequest(caRequestId);
+ }
+ _logger.LogTrace($"orderStatusRequest JSON {JsonConvert.SerializeObject(orderStatusRequest)}");
+
+ var orderStatusResponse = await client.SubmitOrderStatusRequestAsync(orderStatusRequest);
+ _logger.LogTrace($"orderStatusResponse JSON {JsonConvert.SerializeObject(orderStatusResponse)}");
+
+ // Determine renewal vs reissue based on order expiry and RenewalWindow
+ var shouldRenew = false;
+ if (DateTime.TryParse(orderStatusResponse.OrderExpiryDateInUtc, out var orderExpiry))
+ {
+ var daysUntilOrderExpiry = (orderExpiry - DateTime.UtcNow).TotalDays;
+ _logger.LogTrace($"Order expiry: {orderExpiry:u}, days remaining: {daysUntilOrderExpiry:F0}, renewal window: {RenewalWindow} days");
+ shouldRenew = daysUntilOrderExpiry <= RenewalWindow;
+ }
+ else
+ {
+ _logger.LogWarning($"Could not parse OrderExpiryDateInUTC '{orderStatusResponse.OrderExpiryDateInUtc}', defaulting to renewal");
+ shouldRenew = true;
+ }
+
+ if (shouldRenew)
+ {
+ _logger.LogTrace("Order is within renewal window, performing renewal (new order)...");
+ var renewRequest = _requestManager.GetRenewalRequest(orderStatusResponse, csr);
+ _logger.LogTrace($"renewRequest JSON {JsonConvert.SerializeObject(renewRequest)}");
+
+ enrollmentResponse = await client.SubmitRenewRequestAsync(renewRequest);
+ _logger.LogTrace($"enrollmentResponse JSON {JsonConvert.SerializeObject(enrollmentResponse)}");
+ }
+ else
+ {
+ _logger.LogTrace("Order has life remaining, performing reissue (same order)...");
+ var reIssueRequest = _requestManager.GetReIssueRequest(orderStatusResponse, csr, false);
+ _logger.LogTrace($"reIssueRequest JSON {JsonConvert.SerializeObject(reIssueRequest)}");
+
+ enrollmentResponse = await client.SubmitReIssueRequestAsync(reIssueRequest);
+ _logger.LogTrace($"reissue enrollmentResponse JSON {JsonConvert.SerializeObject(enrollmentResponse)}");
+ }
+ }
+
+ return GetEnrollmentResult(enrollmentResponse);
+ }
+ finally
+ {
+ _logger.MethodExit();
+ }
+ }
+
+ ///
+ /// Builds a composite CARequestID in the format "{TheSSLStoreOrderID}-{PartnerOrderID}".
+ /// This ensures uniqueness across reissues (same order, different PartnerOrderID).
+ ///
+ private static string BuildCompositeRequestId(string theSslStoreOrderId, string partnerOrderId)
+ {
+ return $"{theSslStoreOrderId}-{partnerOrderId}";
+ }
+
+ ///
+ /// Parses the TheSSLStoreOrderID from a CARequestID. Supports both composite format
+ /// ("{TheSSLStoreOrderID}-{PartnerOrderID}") and legacy GUID format (falls back to
+ /// treating the whole string as a CustomOrderId for backward compatibility).
+ ///
+ private static string ParseSslStoreOrderId(string caRequestId)
+ {
+ if (string.IsNullOrEmpty(caRequestId)) return caRequestId;
+
+ var dashIndex = caRequestId.IndexOf('-');
+ // Composite IDs have a numeric TheSSLStoreOrderID before the first dash.
+ // Legacy GUIDs have hex chars before the first dash so we check for digits only.
+ if (dashIndex > 0 && caRequestId.Substring(0, dashIndex).All(char.IsDigit))
+ {
+ return caRequestId.Substring(0, dashIndex);
+ }
+
+ // Legacy GUID format — return as-is for backward compatibility
+ return null;
+ }
+
+ private EnrollmentResult GetEnrollmentResult(INewOrderResponse newOrderResponse)
+ {
+ if (newOrderResponse != null && newOrderResponse.AuthResponse.IsError)
+ {
+ _logger.MethodExit();
+ return new EnrollmentResult
+ {
+ Status = (int)EndEntityStatus.FAILED,
+ StatusMessage = newOrderResponse.AuthResponse.Message[0]
+ };
+ }
+
+ var majorStatus = newOrderResponse?.OrderStatus?.MajorStatus;
+ var status = _requestManager.MapReturnStatus(majorStatus);
+ var compositeId = BuildCompositeRequestId(newOrderResponse?.TheSslStoreOrderId, newOrderResponse?.PartnerOrderId);
+
+ _logger.LogTrace($"Order {compositeId} (SSLStoreOrderId: {newOrderResponse?.TheSslStoreOrderId}, PartnerOrderId: {newOrderResponse?.PartnerOrderId}) status: {majorStatus} -> mapped to {status}");
+ _logger.MethodExit();
+
+ return new EnrollmentResult
+ {
+ CARequestID = compositeId,
+ Status = status,
+ StatusMessage = $"Order Successfully Created With Order Number {compositeId}"
+ };
+ }
+
+ public async Task GetSingleRecord(string caRequestId)
+ {
+ _logger.MethodEntry();
+
+ var client = new SslStoreClient(Config);
+ var sslStoreOrderId = ParseSslStoreOrderId(caRequestId);
+
+ OrderStatusRequest orderStatusRequest;
+ if (sslStoreOrderId != null)
+ {
+ _logger.LogTrace($"Parsed TheSSLStoreOrderID: {sslStoreOrderId} from CARequestID: {caRequestId}");
+ orderStatusRequest = _requestManager.GetOrderStatusRequestBySslStoreId(sslStoreOrderId);
+ }
+ else
+ {
+ _logger.LogTrace($"Legacy GUID format, querying by CustomOrderId: {caRequestId}");
+ orderStatusRequest = _requestManager.GetOrderStatusRequest(caRequestId);
+ }
+
+ var orderStatusResponse = await client.SubmitOrderStatusRequestAsync(orderStatusRequest);
+ _logger.LogTrace($"orderStatusResponse JSON {JsonConvert.SerializeObject(orderStatusResponse)}");
+
+ var certStatus = _requestManager.MapReturnStatus(orderStatusResponse?.OrderStatus.MajorStatus);
+ var certificate = string.Empty;
+
+ if (certStatus == (int)EndEntityStatus.GENERATED)
+ {
+ var downloadCertificateRequest = _requestManager.GetCertificateRequestBySslStoreId(sslStoreOrderId ?? orderStatusResponse.TheSslStoreOrderId);
+ var certResponse = await client.SubmitDownloadCertificateAsync(downloadCertificateRequest);
+ if (!certResponse.AuthResponse.IsError)
+ {
+ var fullChain = string.Join("\n", certResponse.Certificates.Select(c => c.FileContent));
+ var endEntityCert = X509Utilities.ExtractEndEntityCertificateContents(fullChain, null);
+ certificate = Convert.ToBase64String(endEntityCert.RawData);
+ }
+ }
+
+ _logger.MethodExit();
+ return new AnyCAPluginCertificate
+ {
+ CARequestID = caRequestId,
+ Certificate = certificate,
+ Status = certStatus
+ };
+ }
+
+ public async Task Synchronize(BlockingCollection blockingBuffer,
+ DateTime? lastSync, bool fullSync, CancellationToken cancelToken)
+ {
+ _logger.MethodEntry();
+
+ try
+ {
+ var client = new SslStoreClient(Config);
+ var certs = new BlockingCollection(100);
+ _ = client.SubmitQueryOrderRequestAsync(certs, cancelToken, _requestManager);
+
+ foreach (var currentResponseItem in certs.GetConsumingEnumerable(cancelToken))
+ {
+ if (cancelToken.IsCancellationRequested)
+ {
+ _logger.LogError("Synchronize was canceled.");
+ break;
+ }
+
+ try
+ {
+ _logger.LogTrace($"Took Certificate ID {currentResponseItem?.TheSslStoreOrderId} (CustomOrderId: {currentResponseItem?.CustomOrderId}) from Queue");
+
+ // Use TheSslStoreOrderId for sync lookups since that's what the query returns
+ var orderStatusRequest = _requestManager.GetOrderStatusRequestBySslStoreId(currentResponseItem?.TheSslStoreOrderId);
+ var orderStatusResponse = await client.SubmitOrderStatusRequestAsync(orderStatusRequest);
+
+ var theSslStoreOrderId = orderStatusResponse.TheSslStoreOrderId;
+ var partnerOrderId = orderStatusResponse.PartnerOrderId;
+ if (string.IsNullOrEmpty(theSslStoreOrderId) || string.IsNullOrEmpty(partnerOrderId))
+ {
+ _logger.LogTrace($"Order {currentResponseItem?.TheSslStoreOrderId} missing required IDs, skipping");
+ continue;
+ }
+
+ var compositeId = BuildCompositeRequestId(theSslStoreOrderId, partnerOrderId);
+ var fileContent = "";
+ var certStatus = _requestManager.MapReturnStatus(orderStatusResponse.OrderStatus.MajorStatus);
+
+ if (certStatus == (int)EndEntityStatus.GENERATED)
+ {
+ var downloadCertificateRequest = _requestManager.GetCertificateRequestBySslStoreId(theSslStoreOrderId);
+ var certResponse = await client.SubmitDownloadCertificateAsync(downloadCertificateRequest);
+ if (!certResponse.AuthResponse.IsError)
+ {
+ var fullChain = string.Join("\n", certResponse.Certificates.Select(c => c.FileContent));
+ var endEntityCert = X509Utilities.ExtractEndEntityCertificateContents(fullChain, null);
+ fileContent = Convert.ToBase64String(endEntityCert.RawData);
+ }
+ }
+
+ if ((certStatus == (int)EndEntityStatus.GENERATED && fileContent.Length > 0) ||
+ certStatus == (int)EndEntityStatus.REVOKED)
+ {
+ blockingBuffer.Add(new AnyCAPluginCertificate
+ {
+ CARequestID = compositeId,
+ Certificate = fileContent,
+ Status = certStatus,
+ ProductID = $"{orderStatusResponse.ProductCode}"
+ }, cancelToken);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogError("Synchronize was canceled.");
+ break;
+ }
+ }
+ }
+ catch (AggregateException)
+ {
+ _logger.LogError("SslStore Synchronize Task failed!");
+ throw;
+ }
+
+ _logger.MethodExit();
+ }
+
+ private string ValidateEmails(EmailApproverResponse validEmails, string[] arrayApproverEmails, EnrollmentProductInfo productInfo, int count)
+ {
+ if (arrayApproverEmails.Length > 1 && productInfo.ProductID.Contains("digi"))
+ {
+ return "There should only be one approval email for Digicert products.";
+ }
+
+ if (count == 1 && productInfo.ProductID.Contains("digi") && arrayApproverEmails.Length > 0)
+ {
+ if (!validEmails.ApproverEmailList.Contains(arrayApproverEmails[0]))
+ {
+ return $"Digicert Approver Email must be one of the following {string.Join(",", validEmails.ApproverEmailList)}";
+ }
+ }
+
+ if (!productInfo.ProductID.Contains("digi"))
+ {
+ if (!validEmails.ApproverEmailList.Intersect(arrayApproverEmails).Any())
+ {
+ return $"Sectigo Approver Email must be one of the following {string.Join(",", validEmails.ApproverEmailList)}";
+ }
+ }
+
+ return "";
+ }
+ }
+}
diff --git a/SslStoreCaProxy/SslStoreCaProxy.csproj b/SslStoreCaProxy/SslStoreCaProxy.csproj
new file mode 100644
index 0000000..303fdcd
--- /dev/null
+++ b/SslStoreCaProxy/SslStoreCaProxy.csproj
@@ -0,0 +1,23 @@
+
+
+ net6.0;net8.0;net10.0
+ disable
+ true
+ false
+ Keyfactor.AnyGateway.SslStore
+ SslStoreCaProxy
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
diff --git a/SslStoreCaProxy/manifest.json b/SslStoreCaProxy/manifest.json
new file mode 100644
index 0000000..047caab
--- /dev/null
+++ b/SslStoreCaProxy/manifest.json
@@ -0,0 +1,10 @@
+{
+ "extensions": {
+ "Keyfactor.AnyGateway.Extensions.IAnyCAPlugin": {
+ "SslStoreCaPlugin": {
+ "assemblypath": "SslStoreCaProxy.dll",
+ "TypeFullName": "Keyfactor.AnyGateway.SslStore.SslStoreCaProxy"
+ }
+ }
+ }
+}
diff --git a/docsource/configuration.md b/docsource/configuration.md
new file mode 100644
index 0000000..d3592a4
--- /dev/null
+++ b/docsource/configuration.md
@@ -0,0 +1,338 @@
+## Overview
+
+The SSL Store AnyCA Gateway REST plugin extends the capabilities of the SSL Store Certificate Authority Service to Keyfactor Command via the Keyfactor AnyCA Gateway. SSL Store is a certificate reseller providing access to 80+ certificate products from vendors including DigiCert, Sectigo, RapidSSL, GeoTrust, and Comodo through a single REST API. The plugin represents a fully featured AnyCA Plugin with the following capabilities:
+
+* **CA Sync**:
+ * Download all certificates issued through SSL Store
+ * Full synchronization of all orders with paginated retrieval
+ * Automatic extraction of end-entity certificates from certificate chains
+ * Resilient retry logic (up to 5 retries) for large certificate inventories
+* **Certificate Enrollment**:
+ * Support for new certificate enrollment with CSR
+ * Intelligent renewal vs. reissue logic based on configurable renewal window
+ * Support for DV, OV, and EV certificate products
+ * Multi-domain (MDC/SAN) and wildcard certificate support
+ * Automatic domain validation with approver email verification
+ * 80+ pre-configured certificate products across DigiCert and Sectigo families
+* **Certificate Revocation**:
+ * Request revocation of previously issued certificates via SSL Store refund request API
+
+## Requirements
+
+### SSL Store System Prerequisites
+
+Before configuring the AnyCA Gateway plugin, ensure the following prerequisites are met:
+
+1. **SSL Store Account**:
+ - Active SSL Store partner account with API access enabled
+ - Access to the SSL Store web-based API (WBAPI)
+ - SSL Store account configured and operational
+
+2. **API Credentials**:
+ - SSL Store Partner Code
+ - SSL Store Authentication Token
+ - These credentials must have permissions for:
+ - Certificate enrollment (new order submission)
+ - Certificate download
+ - Certificate revocation (refund request)
+ - Order query and status retrieval
+ - Email approver list retrieval
+
+3. **Network Connectivity**:
+ - Gateway server must have HTTPS access to the SSL Store API endpoint
+ - Production endpoint: `https://wbapi.thesslstore.com`
+ - Sandbox endpoint: `https://sandbox-wbapi.thesslstore.com`
+ - TLS 1.2 or higher must be supported
+
+### Obtaining Required Configuration Information
+
+#### 1. SSL Store Base URL
+
+The SSL Store Base URL is the root endpoint for the SSL Store REST API.
+
+**Available environments:**
+- Production: `https://wbapi.thesslstore.com`
+- Sandbox/Testing: `https://sandbox-wbapi.thesslstore.com`
+
+**To obtain your Base URL:**
+1. Log in to your SSL Store partner portal
+2. Determine whether you are using the production or sandbox environment
+3. Verify the URL is accessible from the Gateway server
+
+#### 2. API Authentication Credentials
+
+The Gateway authenticates to SSL Store using a Partner Code and Authentication Token.
+
+**Steps to obtain API credentials:**
+
+1. **Access SSL Store Partner Portal**:
+ - Log in to your SSL Store partner account
+ - Navigate to API settings
+
+2. **Obtain Credentials**:
+ - **Partner Code**: Your unique partner identifier assigned by SSL Store
+ - **Authentication Token**: A secret token for API authentication
+ - Store these credentials securely
+
+3. **Verify Permissions**:
+ - Ensure the API credentials have permissions for:
+ - Order creation (`/rest/order/neworder`)
+ - Order reissue (`/rest/order/reissue`)
+ - Order query (`/rest/order/query`)
+ - Order status (`/rest/order/status`)
+ - Certificate download (`/rest/order/download`)
+ - Revocation/refund (`/rest/order/refundrequest`)
+ - Email approver list (`/rest/order/approverlist`)
+
+#### 3. Supported Certificate Products
+
+The plugin supports 80+ certificate products from multiple vendors. Products are organized by validation type and vendor:
+
+**DigiCert Products:**
+
+| Product Code | Description | Validation |
+|-------------|-------------|------------|
+| `digi_securesite_flex` | DigiCert Secure Site | OV |
+| `digi_securesite_flex-EO` | DigiCert Secure Site (Enterprise Org) | OV |
+| `digi_securesite_ev_flex` | DigiCert Secure Site EV | EV |
+| `digi_securesite_ev_flex-EO` | DigiCert Secure Site EV (Enterprise Org) | EV |
+| `digi_securesite_pro_flex` | DigiCert Secure Site Pro | OV |
+| `digi_securesite_pro_flex-EO` | DigiCert Secure Site Pro (Enterprise Org) | OV |
+| `digi_securesite_pro_ev_flex` | DigiCert Secure Site Pro EV | EV |
+| `digi_securesite_pro_ev_flex-EO` | DigiCert Secure Site Pro EV (Enterprise Org) | EV |
+| `digi_sslwebserver_flex` | DigiCert SSL Web Server | OV |
+| `digi_sslwebserver_flex-EO` | DigiCert SSL Web Server (Enterprise Org) | OV |
+| `digi_sslwebserver_ev_flex` | DigiCert SSL Web Server EV | EV |
+| `digi_sslwebserver_ev_flex-EO` | DigiCert SSL Web Server EV (Enterprise Org) | EV |
+| `digi_truebizid_flex` | DigiCert TrueBizID | OV |
+| `digi_truebizid_flex-EO` | DigiCert TrueBizID (Enterprise Org) | OV |
+| `digi_truebizid_ev_flex` | DigiCert TrueBizID EV | EV |
+| `digi_truebizid_ev_flex-EO` | DigiCert TrueBizID EV (Enterprise Org) | EV |
+| `digi_ssl_basic` | DigiCert Basic SSL | OV |
+| `digi_ssl_basic-EO` | DigiCert Basic SSL (Enterprise Org) | OV |
+| `digi_ssl_ev_basic` | DigiCert Basic SSL EV | EV |
+| `digi_ssl_ev_basic-EO` | DigiCert Basic SSL EV (Enterprise Org) | EV |
+| `digi_rapidssl` | RapidSSL | DV |
+| `digi_rapidssl_wc` | RapidSSL Wildcard | DV |
+| `digi_ssl_dv_geotrust_flex` | GeoTrust DV SSL | DV |
+| `digi_ssl123_flex` | GeoTrust SSL123 | DV |
+| `digi_quickssl_md` | DigiCert QuickSSL Multi-Domain | DV |
+| `digi_client_premium` | DigiCert Client Premium | Client |
+| `digi_csc` | DigiCert Code Signing | Code Signing |
+| `digi_csc_ev` | DigiCert EV Code Signing | EV Code Signing |
+| `digi_doc_signing_ind_500` | DigiCert Document Signing Individual 500 | Document Signing |
+| `digi_doc_signing_ind_2000` | DigiCert Document Signing Individual 2000 | Document Signing |
+| `digi_doc_signing_org_2000` | DigiCert Document Signing Organization 2000 | Document Signing |
+| `digi_doc_signing_org_5000` | DigiCert Document Signing Organization 5000 | Document Signing |
+
+**Sectigo/Comodo Products:**
+
+| Product Code | Description | Validation |
+|-------------|-------------|------------|
+| `positivessl` | Positive SSL | DV |
+| `positivesslwildcard` | Positive SSL Wildcard | DV |
+| `positivemdcssl` | Positive SSL Multi-Domain | DV |
+| `positivemdcwildcard` | Positive SSL MDC Wildcard | DV |
+| `positiveevssl` | Positive EV SSL | EV |
+| `positiveevmdc` | Positive EV Multi-Domain | EV |
+| `sectigossl` | Sectigo SSL | DV |
+| `sectigowildcard` | Sectigo Wildcard | DV |
+| `sectigoovssl` | Sectigo OV SSL | OV |
+| `sectigoovwildcard` | Sectigo OV Wildcard | OV |
+| `sectigoevssl` | Sectigo EV SSL | EV |
+| `sectigodvucc` | Sectigo DV UCC | DV |
+| `sectigouccwildcard` | Sectigo UCC Wildcard | DV |
+| `sectigomdc` | Sectigo Multi-Domain | OV |
+| `sectigomdcwildcard` | Sectigo MDC Wildcard | OV |
+| `sectigoevmdc` | Sectigo EV Multi-Domain | EV |
+| `comodopremiumssl` | Comodo Premium SSL | OV |
+| `comodopremiumwildcard` | Comodo Premium Wildcard | OV |
+| `comodossl` | Comodo SSL | OV |
+| `comodoevssl` | Comodo EV SSL | EV |
+| `comodomdc` | Comodo Multi-Domain | OV |
+| `comodomdcwildcard` | Comodo MDC Wildcard | OV |
+| `comodoevmdc` | Comodo EV Multi-Domain | EV |
+| `comodoucc` | Comodo UCC | OV |
+| `comodouccwildcard` | Comodo UCC Wildcard | OV |
+| `comodowildcard` | Comodo Wildcard | OV |
+| `comodocsc` | Comodo Code Signing | Code Signing |
+| `comodoevcsc` | Comodo EV Code Signing | EV Code Signing |
+| `comododvucc` | Comodo DV UCC | DV |
+| `comodopciscan` | Comodo PCI Scan | Scanning |
+| `instantssl` | InstantSSL | OV |
+| `instantsslpro` | InstantSSL Pro | OV |
+| `enterprisepro` | Enterprise Pro SSL | OV |
+| `enterpriseprowc` | Enterprise Pro Wildcard | OV |
+| `enterpriseproev` | Enterprise Pro EV | EV |
+| `enterpriseproevmdc` | Enterprise Pro EV Multi-Domain | EV |
+| `enterprisessl` | Enterprise SSL | OV |
+| `essentialssl` | Essential SSL | DV |
+| `essentialwildcard` | Essential Wildcard | DV |
+| `elitessl` | Elite SSL | OV |
+
+**Note:** Products with the `-EO` suffix are Enterprise Organization variants that use a pre-configured DigiCert organization instead of requiring organization details during enrollment. These products require only a Validity Period and Organization ID.
+
+#### 4. Certificate Validity Configuration
+
+Certificate validity is specified in days during enrollment and automatically converted to months for the SSL Store API:
+
+| Days | Months |
+|------|--------|
+| 90 | 3 |
+| 180 | 6 |
+| 365 | 12 |
+| 730 | 24 |
+| 1095 | 36 |
+
+#### 5. Renewal vs. Reissue Logic
+
+The plugin uses a configurable **Renewal Window** (default: 30 days) to determine behavior during certificate renewal:
+
+- If the existing order is **within** the renewal window (i.e., expiring within N days), the plugin performs a **renewal** (new order linked to the original)
+- If the existing order is **outside** the renewal window (still has significant life remaining), the plugin performs a **reissue** on the same order
+
+## Gateway Registration
+
+### CA Connection Configuration
+
+When registering the SSL Store CA in the AnyCA Gateway, you'll need to provide the following configuration parameters:
+
+| Parameter | Description | Required | Default |
+|-----------|-------------|----------|---------|
+| **SSLStoreURL** | Full URL to the SSL Store API endpoint | Yes | `https://sandbox-wbapi.thesslstore.com` |
+| **PartnerCode** | Partner Code obtained from SSL Store | Yes | |
+| **AuthToken** | Authentication Token obtained from SSL Store | Yes | |
+| **PageSize** | Number of records per page during synchronization | No | `100` |
+| **Enabled** | Flag to Enable or Disable the CA connector | No | `true` |
+| **RenewalWindow** | Days before order expiry to trigger renewal vs. reissue | No | `30` |
+
+### Gateway Registration Notes
+
+- Each defined Certificate Authority in the AnyCA Gateway REST can support one SSL Store API endpoint
+- If you have multiple SSL Store environments (production/sandbox), define separate Certificate Authorities for each
+- Each CA configuration will manifest in Command as a separate CA entry
+- The plugin uses REST API authentication with Partner Code and Authentication Token
+- The plugin automatically handles:
+ - Product discovery (80+ products)
+ - Certificate status mapping (Active, Pending, Cancelled)
+ - End-entity certificate extraction from certificate chains
+ - Paginated order synchronization with retry logic
+
+### Security Considerations
+
+1. **Credential Storage**: The AuthToken field is configured as a secret/hidden field and should be stored securely
+2. **Network Security**: Ensure TLS/SSL is properly configured for all API communications
+3. **Least Privilege**: Request API credentials with minimal required permissions
+4. **Audit Logging**: Enable comprehensive logging in both the Gateway and SSL Store for security monitoring
+5. **Credential Rotation**: Regularly rotate API credentials according to your security policy
+6. **Sandbox Testing**: Use the sandbox endpoint (`https://sandbox-wbapi.thesslstore.com`) for initial configuration and testing before switching to production
+
+### CA Connection Fields
+
+Populate using the configuration fields collected in the [requirements](#requirements) section.
+
+* **SSLStoreURL** - The base URL for the SSL Store API endpoint. Use `https://wbapi.thesslstore.com` for production or `https://sandbox-wbapi.thesslstore.com` for testing.
+* **PartnerCode** - The Partner Code obtained from your SSL Store partner account.
+* **AuthToken** - The Authentication Token obtained from your SSL Store partner account.
+* **PageSize** - Number of records to retrieve per page during certificate synchronization. Default is 100.
+* **Enabled** - Flag to enable or disable the CA connector. Set to `true` to enable.
+* **RenewalWindow** - Number of days before an order's expiration date to trigger a renewal (new order) instead of a reissue (same order). Default is 30 days.
+
+## Certificate Template Creation Step
+
+### Template (Product) Configuration
+
+After adding the CA to the Gateway, certificate templates are automatically discovered from the plugin's built-in product registry. Each template may require different enrollment fields depending on the product type and validation level.
+
+**Enrollment fields vary by product type. The following categories exist:**
+
+#### DV Products (Minimal Fields)
+
+Products like `positivessl`, `sectigossl`, `sectigowildcard`:
+
+| Parameter | Description | Required |
+|-----------|-------------|----------|
+| **Admin Contact - Email** | Administrative contact email | Yes |
+| **Approver Email** | Domain validation approver email | Yes |
+| **Validity Period (In Days)** | Certificate validity in days | Yes |
+
+#### OV Products (Organization Fields)
+
+Products like `sectigoovssl`, `comodopremiumssl`, `instantssl`:
+
+| Parameter | Description | Required |
+|-----------|-------------|----------|
+| **Admin Contact - Email** | Administrative contact email | Yes |
+| **Approver Email** | Domain validation approver email | Yes |
+| **Validity Period (In Days)** | Certificate validity in days | Yes |
+| **Organization Name** | Organization name | Yes |
+| **Organization Address** | Organization street address | Yes |
+| **Organization State/Province** | Organization state or province | Yes |
+| **Organization Postal Code** | Organization postal/zip code | Yes |
+| **Organization Country** | Two-letter country code (e.g. US) | Yes |
+| **Organization Phone** | Organization phone number | Yes |
+
+#### DigiCert OV Flex Products
+
+Products like `digi_securesite_flex`, `digi_sslwebserver_flex`, `digi_truebizid_flex`:
+
+| Parameter | Description | Required |
+|-----------|-------------|----------|
+| **Admin Contact - First Name** | Administrative contact first name | Yes |
+| **Admin Contact - Last Name** | Administrative contact last name | Yes |
+| **Admin Contact - Phone** | Administrative contact phone | Yes |
+| **Admin Contact - Email** | Administrative contact email | Yes |
+| **Approver Email** | Domain validation approver email | Yes |
+| **Validity Period (In Days)** | Certificate validity in days | Yes |
+| **Organization Name** | Organization name | Yes |
+| **Organization Address** | Organization street address | Yes |
+| **Organization City** | Organization city | Yes |
+| **Organization State/Province** | Organization state or province | Yes |
+| **Organization Postal Code** | Organization postal/zip code | Yes |
+| **Organization Country** | Two-letter country code | Yes |
+| **Organization Phone** | Organization phone number | Yes |
+
+#### DigiCert EV Flex Products
+
+Products like `digi_securesite_ev_flex`, `digi_ssl_ev_basic`, `digi_truebizid_ev_flex`:
+
+Same as DigiCert OV Flex, plus:
+
+| Parameter | Description | Required |
+|-----------|-------------|----------|
+| **Admin Contact - Title** | Administrative contact job title | Yes |
+
+#### Enterprise Organization (-EO) Products
+
+Products like `digi_securesite_flex-EO`, `digi_sslwebserver_ev_flex-EO`:
+
+| Parameter | Description | Required |
+|-----------|-------------|----------|
+| **Validity Period (In Days)** | Certificate validity in days | Yes |
+| **Organization ID** | DigiCert Organization ID | Yes |
+
+#### EV Products with Jurisdiction
+
+Products like `enterpriseproev`, `positiveevssl`, `positiveevmdc`:
+
+Same as OV Products, plus:
+
+| Parameter | Description | Required |
+|-----------|-------------|----------|
+| **Organization Jurisdiction Country** | Jurisdiction country code for EV validation | Yes |
+
+### Domain Validation - Approver Emails
+
+The plugin validates approver emails against SSL Store's approved list for each domain before enrollment:
+
+- **DigiCert products**: Exactly one approver email is required and must be from the approved list
+- **Sectigo/Comodo products**: At least one approver email must be from the approved list
+- Emails are validated per-domain for multi-domain certificates
+
+### Important Notes
+
+- Product IDs are automatically registered from the plugin's built-in product registry
+- The `Validity Period (In Days)` is automatically converted to months for the SSL Store API
+- For `-EO` (Enterprise Organization) products, the Organization ID dropdown is populated from your DigiCert account's active organizations
+- DNS names (SANs) are extracted from the Keyfactor enrollment request; they do not need to be provided as a separate enrollment field
+- The Common Name (CN) is extracted from the CSR subject
diff --git a/integration-manifest.json b/integration-manifest.json
new file mode 100644
index 0000000..86be3f5
--- /dev/null
+++ b/integration-manifest.json
@@ -0,0 +1,288 @@
+{
+ "$schema": "https://keyfactor.github.io/v2/integration-manifest-schema.json",
+ "name": "SSL Store AnyCA REST plugin",
+ "release_dir": "SslStoreCaProxy/bin/Release",
+ "release_project": "SslStoreCaProxy/SslStoreCaProxy.csproj",
+ "description": "AnyCA Gateway REST plugin that extends SSL Store Certificate Authority Service to Keyfactor Command. SSL Store is a certificate reseller providing access to 80+ certificate products from vendors including DigiCert, Sectigo, RapidSSL, GeoTrust, and Comodo through a single API.",
+ "status": "production",
+ "integration_type": "anyca-plugin",
+ "support_level": "kf-supported",
+ "link_github": false,
+ "update_catalog": false,
+ "gateway_framework": "25.5",
+ "about": {
+ "carest": {
+ "ca_plugin_config": [
+ {
+ "name": "SSLStoreURL",
+ "description": "The Base URL for the SSL Store API endpoint (e.g. https://sandbox-wbapi.thesslstore.com)."
+ },
+ {
+ "name": "PartnerCode",
+ "description": "The Partner Code obtained from SSL Store."
+ },
+ {
+ "name": "AuthToken",
+ "description": "The Authentication Token obtained from SSL Store."
+ },
+ {
+ "name": "PageSize",
+ "description": "The number of records to return per page during synchronization."
+ },
+ {
+ "name": "Enabled",
+ "description": "Flag to Enable or Disable the CA connector."
+ },
+ {
+ "name": "RenewalWindow",
+ "description": "Number of days before order expiry to trigger a renewal instead of a reissue."
+ }
+ ],
+ "enrollment_config": [
+ {
+ "name": "Approver Email",
+ "description": "Comma-separated approver email address(es) for domain validation."
+ },
+ {
+ "name": "Validity Period (In Days)",
+ "description": "Certificate validity period in days (e.g. 90, 365, 730)."
+ },
+ {
+ "name": "Admin Contact - First Name",
+ "description": "Administrative contact first name."
+ },
+ {
+ "name": "Admin Contact - Last Name",
+ "description": "Administrative contact last name."
+ },
+ {
+ "name": "Admin Contact - Phone",
+ "description": "Administrative contact phone number."
+ },
+ {
+ "name": "Admin Contact - Email",
+ "description": "Administrative contact email address."
+ },
+ {
+ "name": "Admin Contact - Title",
+ "description": "Administrative contact job title."
+ },
+ {
+ "name": "Admin Contact - Organization Name",
+ "description": "Administrative contact organization name."
+ },
+ {
+ "name": "Admin Contact - Address",
+ "description": "Administrative contact street address."
+ },
+ {
+ "name": "Admin Contact - City",
+ "description": "Administrative contact city."
+ },
+ {
+ "name": "Admin Contact - Region",
+ "description": "Administrative contact state/province/region."
+ },
+ {
+ "name": "Admin Contact - Postal Code",
+ "description": "Administrative contact postal/zip code."
+ },
+ {
+ "name": "Admin Contact - Country",
+ "description": "Administrative contact two-letter country code (e.g. US)."
+ },
+ {
+ "name": "Technical Contact - First Name",
+ "description": "Technical contact first name."
+ },
+ {
+ "name": "Technical Contact - Last Name",
+ "description": "Technical contact last name."
+ },
+ {
+ "name": "Technical Contact - Phone",
+ "description": "Technical contact phone number."
+ },
+ {
+ "name": "Technical Contact - Email",
+ "description": "Technical contact email address."
+ },
+ {
+ "name": "Technical Contact - Organization Name",
+ "description": "Technical contact organization name."
+ },
+ {
+ "name": "Technical Contact - Address",
+ "description": "Technical contact street address."
+ },
+ {
+ "name": "Technical Contact - City",
+ "description": "Technical contact city."
+ },
+ {
+ "name": "Technical Contact - Region",
+ "description": "Technical contact state/province/region."
+ },
+ {
+ "name": "Technical Contact - Postal Code",
+ "description": "Technical contact postal/zip code."
+ },
+ {
+ "name": "Technical Contact - Country",
+ "description": "Technical contact two-letter country code (e.g. US)."
+ },
+ {
+ "name": "Organization Name",
+ "description": "Organization name for the certificate."
+ },
+ {
+ "name": "Organization Address",
+ "description": "Organization street address."
+ },
+ {
+ "name": "Organization City",
+ "description": "Organization city."
+ },
+ {
+ "name": "Organization Region",
+ "description": "Organization state/province/region."
+ },
+ {
+ "name": "Organization State/Province",
+ "description": "Organization state or province."
+ },
+ {
+ "name": "Organization Postal Code",
+ "description": "Organization postal/zip code."
+ },
+ {
+ "name": "Organization Country",
+ "description": "Organization two-letter country code (e.g. US)."
+ },
+ {
+ "name": "Organization Phone",
+ "description": "Organization phone number."
+ },
+ {
+ "name": "Organization Jurisdiction Country",
+ "description": "Jurisdiction country code for EV certificates."
+ },
+ {
+ "name": "Organization ID",
+ "description": "DigiCert organization ID for EO (Enterprise Organization) products."
+ },
+ {
+ "name": "Server Count",
+ "description": "Number of server licenses for the certificate."
+ },
+ {
+ "name": "Web Server Type",
+ "description": "Web server type (e.g. apacheopenssl, iis, tomcat, Other)."
+ },
+ {
+ "name": "Signature Hash Algorithm",
+ "description": "Signature hash algorithm (PREFER_SHA2, REQUIRE_SHA2, PREFER_SHA1)."
+ },
+ {
+ "name": "File Auth Domain Validation",
+ "description": "Use file-based domain validation (True/False)."
+ },
+ {
+ "name": "CName Auth Domain Validation",
+ "description": "Use CNAME-based domain validation (True/False)."
+ },
+ {
+ "name": "Is CU Order?",
+ "description": "Is this a CU (Customer) order (True/False)."
+ },
+ {
+ "name": "Is Renewal Order?",
+ "description": "Is this a renewal order (True/False)."
+ },
+ {
+ "name": "Is Trial Order?",
+ "description": "Is this a trial order (True/False)."
+ }
+ ],
+ "product_ids": [
+ "comododvucc",
+ "comodoevcsc",
+ "comodoevmdc",
+ "comodoevssl",
+ "comodomdc",
+ "comodomdcwildcard",
+ "comodopciscan",
+ "comodossl",
+ "comodoucc",
+ "comodouccwildcard",
+ "comodowildcard",
+ "digi_client_premium",
+ "digi_csc",
+ "digi_csc_ev",
+ "digi_doc_signing_ind_2000",
+ "digi_doc_signing_ind_500",
+ "digi_doc_signing_org_2000",
+ "digi_doc_signing_org_5000",
+ "elitessl",
+ "enterprisessl",
+ "essentialssl",
+ "essentialwildcard",
+ "hackerprooftm",
+ "hgpcicontrolscan",
+ "pacbasic",
+ "pacpro",
+ "pacenterprise",
+ "comodocsc",
+ "digi_quickssl_md",
+ "digi_securesite_ev_flex-EO",
+ "digi_securesite_flex-EO",
+ "digi_securesite_pro_ev_flex-EO",
+ "digi_securesite_pro_flex",
+ "digi_securesite_pro_flex-EO",
+ "digi_ssl_basic-EO",
+ "digi_ssl_ev_basic-EO",
+ "digi_sslwebserver_ev_flex-EO",
+ "digi_sslwebserver_flex-EO",
+ "digi_truebizid_ev_flex-EO",
+ "digi_truebizid_flex-EO",
+ "comodopremiumssl",
+ "comodopremiumwildcard",
+ "enterpriseprowc",
+ "instantssl",
+ "instantsslpro",
+ "sectigoovssl",
+ "sectigoovwildcard",
+ "digi_securesite_flex",
+ "digi_ssl_basic",
+ "digi_sslwebserver_flex",
+ "digi_truebizid_flex",
+ "positivessl",
+ "positivesslwildcard",
+ "sectigoevssl",
+ "sectigossl",
+ "sectigowildcard",
+ "digi_securesite_ev_flex",
+ "digi_ssl_ev_basic",
+ "digi_sslwebserver_ev_flex",
+ "digi_truebizid_ev_flex",
+ "digi_securesite_pro_ev_flex",
+ "positivemdcssl",
+ "positivemdcwildcard",
+ "sectigodvucc",
+ "sectigouccwildcard",
+ "digi_rapidssl",
+ "digi_rapidssl_wc",
+ "digi_ssl_dv_geotrust_flex",
+ "digi_ssl123_flex",
+ "enterpriseproev",
+ "positiveevssl",
+ "positiveevmdc",
+ "sectigoevmdc",
+ "enterpriseproevmdc",
+ "sectigomdc",
+ "sectigomdcwildcard",
+ "enterprisepro"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/readme_source.md b/readme_source.md
new file mode 100644
index 0000000..ec93003
--- /dev/null
+++ b/readme_source.md
@@ -0,0 +1,10 @@
+
+## Compatibility
+
+The SSL Store AnyCA Gateway REST plugin is compatible with the Keyfactor AnyCA Gateway REST 24.2 and later.
+
+## Support
+The SSL Store AnyCA Gateway REST 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.
+