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
11 changes: 9 additions & 2 deletions .github/workflows/keyfactor-bootstrap-workflow-v3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ on:

jobs:
call-starter-workflow:
uses: keyfactor/actions/.github/workflows/starter.yml@v3.1.2
uses: keyfactor/actions/.github/workflows/starter.yml@v5
with:
command_token_url: ${{ vars.COMMAND_TOKEN_URL }}
command_hostname: ${{ vars.COMMAND_HOSTNAME }}
command_base_api_path: ${{ vars.COMMAND_API_PATH }}
secrets:
token: ${{ secrets.V2BUILDTOKEN}}
APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}}
gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }}
gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }}
scan_token: ${{ secrets.SAST_TOKEN }}
entra_username: ${{ secrets.DOCTOOL_ENTRA_USERNAME }}
entra_password: ${{ secrets.DOCTOOL_ENTRA_PASSWD }}
command_client_id: ${{ secrets.COMMAND_CLIENT_ID }}
command_client_secret: ${{ secrets.COMMAND_CLIENT_SECRET }}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v1.2.0
- Added support for CSC TrustedSecure EV, Multiple Names; CSC TrustedSecure OV Wildcard, Multiple Names; and CSC TrustedSecure DV Wildcard, Multiple Names certificate products

v.1.1.1
- Added Incremental Sync that goes back X Number of days
- Fixed issue with parsing certain certificates that were in zip format
Expand Down
535 changes: 310 additions & 225 deletions README.md

Large diffs are not rendered by default.

378 changes: 271 additions & 107 deletions cscglobal-caplugin/CSCGlobalCAPlugin.cs

Large diffs are not rendered by default.

112 changes: 101 additions & 11 deletions cscglobal-caplugin/Client/CscGlobalClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,25 @@ public sealed class CscGlobalClient : ICscGlobalClient

public CscGlobalClient(IAnyCAPluginConfigProvider config)
{
Logger = LogHandler.GetClassLogger<CSCGlobalCAPlugin>();
Logger = LogHandler.GetClassLogger<CscGlobalClient>();
if (config == null) throw new ArgumentNullException(nameof(config));
if (config.CAConnectionData == null)
{
Logger.LogError("CA connection data is null; client will not be able to call the CSC Global API");
return;
}

if (config.CAConnectionData.ContainsKey(Constants.CscGlobalApiKey))
{
BaseUrl = new Uri(config.CAConnectionData[Constants.CscGlobalUrl].ToString());
ApiKey = config.CAConnectionData[Constants.CscGlobalApiKey].ToString();
Authorization = config.CAConnectionData[Constants.BearerToken].ToString();
RestClient = ConfigureRestClient();
Logger.LogDebug($"CscGlobalClient configured for base URL {BaseUrl}");
}
else
{
Logger.LogError($"CA connection data is missing required key '{Constants.CscGlobalApiKey}'; client will not be able to call the CSC Global API");
}
}

Expand All @@ -41,25 +53,38 @@ public CscGlobalClient(IAnyCAPluginConfigProvider config)
public async Task<RegistrationResponse> SubmitRegistrationAsync(
RegistrationRequest registerRequest)
{
Logger.MethodEntry(LogLevel.Debug);
using (var resp = await RestClient.PostAsync("/dbs/api/v2/tls/registration", new StringContent(
JsonConvert.SerializeObject(registerRequest), Encoding.ASCII, "application/json")))
{
Logger.LogTrace(JsonConvert.SerializeObject(registerRequest));
var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
if (resp.StatusCode == HttpStatusCode.BadRequest) //Csc Sends Errors back in 400 Json Response
{
var rawErrorResponse = await resp.Content.ReadAsStringAsync();
var errorResponse =
JsonConvert.DeserializeObject<RegistrationError>(await resp.Content.ReadAsStringAsync(),
settings);
JsonConvert.DeserializeObject<RegistrationError>(rawErrorResponse, settings);
Logger.LogWarning($"Registration request rejected by CSC Global: {errorResponse?.Description ?? rawErrorResponse}");
var response = new RegistrationResponse();
response.RegistrationError = errorResponse;
response.Result = null;
return response;
}

if (!resp.IsSuccessStatusCode)
{
var errorBody = await resp.Content.ReadAsStringAsync();
Logger.LogError($"Registration request failed with status code {resp.StatusCode} | Message: {errorBody}");
throw new HttpRequestException($"Registration request failed with status code {resp.StatusCode}: {errorBody}");
}

var registrationResponse =
JsonConvert.DeserializeObject<RegistrationResponse>(await resp.Content.ReadAsStringAsync(),
settings);
if (registrationResponse == null)
throw new InvalidOperationException("Registration request succeeded but the response body could not be parsed");

Logger.MethodExit(LogLevel.Debug);
return registrationResponse;
}
}
Expand All @@ -81,17 +106,28 @@ public async Task<RenewalResponse> SubmitRenewalAsync(
var errorResponse =
JsonConvert.DeserializeObject<RegistrationError>(rawErrorResponse,
settings);
Logger.LogWarning($"Renewal request rejected by CSC Global: {errorResponse?.Description ?? rawErrorResponse}");
var response = new RenewalResponse();
response.RegistrationError = errorResponse;
response.Result = null;
return response;
}

if (!resp.IsSuccessStatusCode)
{
var errorBody = await resp.Content.ReadAsStringAsync();
Logger.LogError($"Renewal request failed with status code {resp.StatusCode} | Message: {errorBody}");
throw new HttpRequestException($"Renewal request failed with status code {resp.StatusCode}: {errorBody}");
}

var rawRenewResponse = await resp.Content.ReadAsStringAsync();
Logger.LogTrace("Logging Success Response Raw");
Logger.LogTrace(rawRenewResponse);
var renewalResponse =
JsonConvert.DeserializeObject<RenewalResponse>(rawRenewResponse);
if (renewalResponse == null)
throw new InvalidOperationException("Renewal request succeeded but the response body could not be parsed");

return renewalResponse;
}
}
Expand All @@ -107,61 +143,110 @@ public async Task<ReissueResponse> SubmitReissueAsync(
var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
if (resp.StatusCode == HttpStatusCode.BadRequest) //Csc Sends Errors back in 400 Json Response
{
var rawErrorResponse = await resp.Content.ReadAsStringAsync();
var errorResponse =
JsonConvert.DeserializeObject<RegistrationError>(await resp.Content.ReadAsStringAsync(),
settings);
JsonConvert.DeserializeObject<RegistrationError>(rawErrorResponse, settings);
Logger.LogWarning($"Reissue request rejected by CSC Global: {errorResponse?.Description ?? rawErrorResponse}");
var response = new ReissueResponse();
response.RegistrationError = errorResponse;
response.Result = null;
return response;
}

if (!resp.IsSuccessStatusCode)
{
var errorBody = await resp.Content.ReadAsStringAsync();
Logger.LogError($"Reissue request failed with status code {resp.StatusCode} | Message: {errorBody}");
throw new HttpRequestException($"Reissue request failed with status code {resp.StatusCode}: {errorBody}");
}

var reissueResponse =
JsonConvert.DeserializeObject<ReissueResponse>(await resp.Content.ReadAsStringAsync());
if (reissueResponse == null)
throw new InvalidOperationException("Reissue request succeeded but the response body could not be parsed");

return reissueResponse;
}
}

public async Task<CertificateResponse> SubmitGetCertificateAsync(string certificateId)
{
Logger.MethodEntry(LogLevel.Debug);
Logger.LogTrace($"Getting certificate with ID {certificateId}");
using (var resp = await RestClient.GetAsync($"/dbs/api/v2/tls/certificate/{certificateId}"))
{
if (!resp.IsSuccessStatusCode)
{
var errorBody = await resp.Content.ReadAsStringAsync();
Logger.LogError($"Failed to get certificate {certificateId}. Status code {resp.StatusCode} | Message: {errorBody}");
}

resp.EnsureSuccessStatusCode();
var getCertificateResponse =
JsonConvert.DeserializeObject<CertificateResponse>(await resp.Content.ReadAsStringAsync());
if (getCertificateResponse == null)
throw new InvalidOperationException($"Get certificate request for {certificateId} succeeded but the response body could not be parsed");

Logger.MethodExit(LogLevel.Debug);
return getCertificateResponse;
}
}

public async Task<List<GetCustomField>> SubmitGetCustomFields()
{
Logger.MethodEntry(LogLevel.Debug);
using (var resp = await RestClient.GetAsync("/dbs/api/v2/admin/customfields"))
{
if (!resp.IsSuccessStatusCode)
{
var errorBody = await resp.Content.ReadAsStringAsync();
Logger.LogError($"Failed to get custom fields. Status code {resp.StatusCode} | Message: {errorBody}");
}

resp.EnsureSuccessStatusCode();
var getCustomFieldsResponse =
JsonConvert.DeserializeObject<GetCustomFields>(await resp.Content.ReadAsStringAsync());
return getCustomFieldsResponse.CustomFields;
if (getCustomFieldsResponse == null)
throw new InvalidOperationException("Get custom fields request succeeded but the response body could not be parsed");

Logger.LogTrace($"Retrieved {getCustomFieldsResponse.CustomFields?.Count ?? 0} custom field(s)");
Logger.MethodExit(LogLevel.Debug);
return getCustomFieldsResponse.CustomFields ?? new List<GetCustomField>();
}
}

public async Task<RevokeResponse> SubmitRevokeCertificateAsync(string uuId)
{
Logger.MethodEntry(LogLevel.Debug);
Logger.LogTrace($"Revoking certificate with UUID {uuId}");
using (var resp = await RestClient.PutAsync($"/dbs/api/v2/tls/revoke/{uuId}", new StringContent("")))
{
var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
if (resp.StatusCode == HttpStatusCode.BadRequest) //Csc Sends Errors back in 400 Json Response
{
var rawErrorResponse = await resp.Content.ReadAsStringAsync();
var errorResponse =
JsonConvert.DeserializeObject<RegistrationError>(await resp.Content.ReadAsStringAsync(),
settings);
JsonConvert.DeserializeObject<RegistrationError>(rawErrorResponse, settings);
Logger.LogWarning($"Revoke request rejected by CSC Global for UUID {uuId}: {errorResponse?.Description ?? rawErrorResponse}");
var response = new RevokeResponse();
response.RegistrationError = errorResponse;
response.RevokeSuccess = null;
return response;
}

if (!resp.IsSuccessStatusCode)
{
var errorBody = await resp.Content.ReadAsStringAsync();
Logger.LogError($"Revoke request for UUID {uuId} failed with status code {resp.StatusCode} | Message: {errorBody}");
throw new HttpRequestException($"Revoke request failed with status code {resp.StatusCode}: {errorBody}");
}

var getRevokeResponse =
JsonConvert.DeserializeObject<RevokeResponse>(await resp.Content.ReadAsStringAsync());
if (getRevokeResponse == null)
throw new InvalidOperationException("Revoke request succeeded but the response body could not be parsed");

Logger.MethodExit(LogLevel.Debug);
return getRevokeResponse;
}
}
Expand All @@ -179,13 +264,18 @@ public async Task<CertificateListResponse> SubmitCertificateListRequestAsync(str

if (!resp.IsSuccessStatusCode)
{
var responseMessage = resp.Content.ReadAsStringAsync().Result;
Logger.LogError(
$"Failed Request to Keyfactor. Retrying request. Status Code {resp.StatusCode} | Message: {responseMessage}");
var responseMessage = await resp.Content.ReadAsStringAsync();
Logger.LogError($"Certificate list request failed. Status Code {resp.StatusCode} | Message: {responseMessage}");
throw new HttpRequestException($"Certificate list request failed with status code {resp.StatusCode}: {responseMessage}");
}

var certificateListResponse =
JsonConvert.DeserializeObject<CertificateListResponse>(await resp.Content.ReadAsStringAsync());
if (certificateListResponse == null)
throw new InvalidOperationException("Certificate list request succeeded but the response body could not be parsed");

Logger.LogInformation($"Certificate list request returned {certificateListResponse.Results?.Count ?? 0} result(s)");
Logger.MethodExit(LogLevel.Debug);
return certificateListResponse;
}

Expand Down
5 changes: 4 additions & 1 deletion cscglobal-caplugin/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ public class ProductIDs
"CSC TrustedSecure Premium Wildcard Certificate",
"CSC TrustedSecure Domain Validated SSL",
"CSC TrustedSecure Domain Validated Wildcard SSL",
"CSC TrustedSecure Domain Validated UC Certificate"
"CSC TrustedSecure Domain Validated UC Certificate",
"CSC TrustedSecure EV, Multiple Names",
"CSC TrustedSecure OV Wildcard, Multiple Names",
"CSC TrustedSecure DV Wildcard, Multiple Names"
};
}

Expand Down
Loading
Loading