Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,5 @@ Switch to .NET 8

1.1.1
Allow for manual specification of enrollment term length

1.1.2
Add Lifetime parameter to allow for manual specification of cert validity
Bugfix - Properly handle syncs of 0 records
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<h1 align="center" style="border-bottom: none">
Sectigo Certificate Manager Gateway AnyCA Gateway REST Plugin
Sectigo Certificate Manager AnyCA Gateway REST Plugin
</h1>

<p align="center">
Expand Down Expand Up @@ -45,10 +45,10 @@ The Sectigo AnyCA Gateway REST plugin extends the capabilities of the Sectigo Ce

## Compatibility

The Sectigo Certificate Manager Gateway AnyCA Gateway REST plugin is compatible with the Keyfactor AnyCA Gateway REST 24.2.0 and later.
The Sectigo Certificate Manager AnyCA Gateway REST plugin is compatible with the Keyfactor AnyCA Gateway REST 24.2.0 and later.

## Support
The Sectigo Certificate Manager Gateway 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.
The Sectigo Certificate Manager 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.

Expand All @@ -61,7 +61,7 @@ In addition, for the admin account you plan to use, make sure it has the API adm

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 [Sectigo Certificate Manager Gateway AnyCA Gateway REST plugin](https://github.com/Keyfactor/sectigo-scm-caplugin/releases/latest) from GitHub.
2. On the server hosting the AnyCA Gateway REST, download and unzip the latest [Sectigo Certificate Manager AnyCA Gateway REST plugin](https://github.com/Keyfactor/sectigo-scm-caplugin/releases/latest) from GitHub.

3. Copy the unzipped directory (usually called `net6.0` or `net8.0`) to the Extensions directory:

Expand All @@ -72,11 +72,11 @@ In addition, for the admin account you plan to use, make sure it has the API adm
Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net8.0\Extensions
```

> The directory containing the Sectigo Certificate Manager Gateway AnyCA Gateway REST plugin DLLs (`net6.0` or `net8.0`) can be named anything, as long as it is unique within the `Extensions` directory.
> The directory containing the Sectigo Certificate Manager 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 Sectigo Certificate Manager Gateway plugin by hovering over the ⓘ symbol to the right of the Gateway on the top left of the portal.
5. Navigate to the AnyCA Gateway REST portal and verify that the Gateway recognizes the Sectigo Certificate Manager plugin by hovering over the ⓘ symbol to the right of the Gateway on the top left of the portal.

## Configuration

Expand Down
76 changes: 24 additions & 52 deletions sectigo-scm-caplugin/Client/SectigoClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using Org.BouncyCastle.Asn1.Ocsp;

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
Expand All @@ -19,6 +21,8 @@
using System.Threading;
using System.Threading.Tasks;

using Error = Keyfactor.Extensions.CAPlugin.Sectigo.API.Error;

namespace Keyfactor.Extensions.CAPlugin.Sectigo.Client
{
public class SectigoClient
Expand All @@ -34,7 +38,9 @@ public SectigoClient(HttpClient client)

public async Task<Certificate> GetCertificate(int sslId)
{
var response = await RestClient.GetAsync($"api/ssl/v1/{sslId}");
string url = $"api/ssl/v1/{sslId}";
Logger.LogTrace($"API Request: GET {url}");
var response = await RestClient.GetAsync(url);
return await ProcessResponse<Certificate>(response);
}

Expand Down Expand Up @@ -139,7 +145,7 @@ public async Task CertificateListProducer(BlockingCollection<Certificate> certs,
public async Task<List<Certificate>> PageCertificates(int position = 0, int size = 25, string filter = "")
{
string filterQueryString = string.IsNullOrEmpty(filter) ? string.Empty : $"&{filter}";
Logger.LogTrace($"API Request: api/ssl/v1?position={position}&size={size}{filterQueryString}".TrimEnd());
Logger.LogTrace($"API Request: GET api/ssl/v1?position={position}&size={size}{filterQueryString}".TrimEnd());
var response = await RestClient.GetAsync($"api/ssl/v1?position={position}&size={size}{filterQueryString}".TrimEnd());
return await ProcessResponse<List<Certificate>>(response);
}
Expand All @@ -151,37 +157,26 @@ public async Task<bool> RevokeSslCertificateById(int sslId, int revcode, string
reasonCode = revcode,
reason = revreason
};
Logger.LogTrace($"API Request: POST api/ssl/v1/revoke/{sslId}\nParameters: {JsonConvert.SerializeObject(data, Formatting.Indented)}");
var response = await RestClient.PostAsJsonAsync($"api/ssl/v1/revoke/{sslId}", data);
if (response.IsSuccessStatusCode)
{
return true;
}
var failedResp = ProcessResponse<RevocationResponse>(response).Result;
return failedResp.IsSuccess;//Should throw an exception with error message from API
var resp = ProcessResponse<RevocationResponse>(response).Result;

return true;//Should throw an exception with error message from API, should only hit this if success
}

public async Task<ListOrganizationsResponse> ListOrganizations()
{
Logger.LogTrace($"API Request: GET api/organization/v1");
var response = await RestClient.GetAsync("api/organization/v1");
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
Logger.LogTrace($"Raw Response: {responseContent}");
}
var orgsResponse = await ProcessResponse<List<Organization>>(response);

return new ListOrganizationsResponse { Organizations = orgsResponse };
}

public async Task<OrganizationDetailsResponse> GetOrganizationDetails(int orgId)
{
Logger.LogTrace($"API Request: GET api/organization/v1/{orgId}");
var response = await RestClient.GetAsync($"api/organization/v1/{orgId}");
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
Logger.LogTrace($"Raw Response: {responseContent}");
}

var orgDetailsResponse = await ProcessResponse<OrganizationDetailsResponse>(response);
return orgDetailsResponse;
}
Expand All @@ -203,6 +198,7 @@ public async Task<ListPersonsResponse> ListPersons(int orgId)

public async Task<ListCustomFieldsResponse> ListCustomFields()
{
Logger.LogTrace($"API Request: GET api/ssl/v1/customFields");
var response = await RestClient.GetAsync("api/ssl/v1/customFields");
return new ListCustomFieldsResponse { CustomFields = await ProcessResponse<List<CustomField>>(response) };
}
Expand All @@ -214,13 +210,14 @@ public async Task<ListSslProfilesResponse> ListSslProfiles(int? orgId = null)
{
urlSuffix = $"?organizationId={orgId}";
}

Logger.LogTrace($"API Request: GET api/ssl/v1/types{urlSuffix}");
var response = await RestClient.GetAsync($"api/ssl/v1/types{urlSuffix}");
return new ListSslProfilesResponse { SslProfiles = await ProcessResponse<List<Profile>>(response) };
}

public async Task<List<Person>> PagePersons(int orgId, int position = 0, int size = 25)
{
Logger.LogTrace($"API Request: GET api/person/v1?position={position}&size={size}&organizationId={orgId}");
var response = await RestClient.GetAsync($"api/person/v1?position={position}&size={size}&organizationId={orgId}");
return await ProcessResponse<List<Person>>(response);
}
Expand All @@ -229,6 +226,7 @@ public async Task<int> Enroll(EnrollRequest request)
{
try
{
Logger.LogTrace($"API Request: POST api/ssl/v1/enroll\nParameters: {JsonConvert.SerializeObject(request, Formatting.Indented)}");
var response = await RestClient.PostAsJsonAsync("api/ssl/v1/enroll", request);
var enrollResponse = await ProcessResponse<EnrollResponse>(response);

Expand All @@ -248,35 +246,14 @@ public async Task<int> Enroll(EnrollRequest request)
}
}

public async Task<int> Renew(int sslId)
{
try
{
var response = await RestClient.PostAsJsonAsync($"api/ssl/v1/renewById/{sslId}", "");
var renewResponse = await ProcessResponse<EnrollResponse>(response);

return renewResponse.sslId;
}
catch (InvalidOperationException invalidOp)
{
throw new Exception($"Invalid Operation. {invalidOp.Message}|{invalidOp.StackTrace}");
}
catch (HttpRequestException httpEx)
{
throw new Exception($"HttpRequestException. {httpEx.Message}|{httpEx.StackTrace}");
}
catch (Exception)
{
throw;
}
}

public async Task<X509Certificate2> PickupCertificate(int sslId, string subject)
{
Logger.LogTrace($"API Request: GET api/ssl/v1/collect/{sslId}/x509C0");
var response = await RestClient.GetAsync($"api/ssl/v1/collect/{sslId}/x509CO");

if (response.IsSuccessStatusCode && response.Content.Headers.ContentLength > 0)
{
Logger.LogTrace($"Raw response: {response.Content.ReadAsStringAsync()}");
string pemChain = await response.Content.ReadAsStringAsync();

string[] splitChain = pemChain.Replace("\r\n", string.Empty).Split(new string[] { "-----" }, StringSplitOptions.RemoveEmptyEntries);
Expand All @@ -287,24 +264,19 @@ public async Task<X509Certificate2> PickupCertificate(int sslId, string subject)
//return new X509Certificate2();
}

public async Task Reissue(ReissueRequest request, int sslId)
{
var response = await RestClient.PostAsJsonAsync($"api/ssl/v1/replace/{sslId}", request);
response.EnsureSuccessStatusCode();
}

#region Static Methods

private static async Task<T> ProcessResponse<T>(HttpResponseMessage response)
{
string responseContent = await response.Content.ReadAsStringAsync();
Logger.LogDebug($"Raw API response: {responseContent}");
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseContent);
}
else
{
var error = JsonConvert.DeserializeObject<Error>(await response.Content.ReadAsStringAsync());
var error = JsonConvert.DeserializeObject<Error>(responseContent);
throw new Exception($"{error.Code} | {error.Description}");
}
}
Expand Down
2 changes: 0 additions & 2 deletions sectigo-scm-caplugin/SectigoCAPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,6 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
};

_logger.LogDebug($"Submit {enrollmentType} request");
var jsonReq = JsonConvert.SerializeObject(request, Formatting.Indented);
_logger.LogDebug($"Request object: {jsonReq}");
sslId = Task.Run(async () => await client.Enroll(request)).Result;
newCert = Task.Run(async () => await client.GetCertificate(sslId)).Result;
_logger.LogDebug($"Enrolled for Certificate {newCert.CommonName} (ID: {newCert.Id}) | Status: {newCert.status}. Attempt to Pickup Certificate.");
Expand Down
Loading