diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b8f82c..093894b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,3 +14,6 @@ Fix for JSON serialization of revocation 1.1.0 Add support for using the cert upload feature to upload auth certs Switch to .NET 8 + +1.1.1 +Allow for manual specification of enrollment term length \ No newline at end of file diff --git a/README.md b/README.md index 4b14544..1a4ca46 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ In addition, for the admin account you plan to use, make sure it has the API adm * **MultiDomain** - This flag lets Keyfactor know if the certificate can contain multiple domain names. Depending on the setting, the SAN entries of the request will change to support Sectigo requirements. * **Organization** - If the organization name is provided here, the Sectigo gateway will use that organization name in requests instead of whatever is in the O= field in the request subject. * **Department** - If your Sectigo account is using department-level products, put the appropriate department name here. Previously, this was alternatively supplied in the OU= subject field, which is now deprecated. + * **Lifetime** - OPTIONAL: The term length (in days) to use for enrollment. If not provided, the default is the first value available in the profile definition in your Sectigo account. diff --git a/integration-manifest.json b/integration-manifest.json index aba2711..8665460 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -78,6 +78,10 @@ { "name": "Department", "description": "If your Sectigo account is using department-level products, put the appropriate department name here. Previously, this was alternatively supplied in the OU= subject field, which is now deprecated." + }, + { + "name": "Lifetime", + "description": "OPTIONAL: The term length (in days) to use for enrollment. If not provided, the default is the first value available in the profile definition in your Sectigo account." } ] } diff --git a/sectigo-scm-caplugin/Constants.cs b/sectigo-scm-caplugin/Constants.cs index 014be4c..47f7af0 100644 --- a/sectigo-scm-caplugin/Constants.cs +++ b/sectigo-scm-caplugin/Constants.cs @@ -27,6 +27,7 @@ public class Config public const string MULTIDOMAIN = "MultiDomain"; public const string ORGANIZATION = "Organization"; public const string DEPARTMENT = "Department"; + public const string LIFETIME = "Lifetime"; } //headers for API client diff --git a/sectigo-scm-caplugin/SectigoCAPlugin.cs b/sectigo-scm-caplugin/SectigoCAPlugin.cs index c88038b..0ce86de 100644 --- a/sectigo-scm-caplugin/SectigoCAPlugin.cs +++ b/sectigo-scm-caplugin/SectigoCAPlugin.cs @@ -196,6 +196,26 @@ public async Task Enroll(string csr, string subject, Dictionar _logger.LogTrace($"Found {enrollmentProfile.name} profile for enroll request"); } + int termLength; + var profileTerms = Task.Run(async () => await GetProfileTerms(int.Parse(productInfo.ProductID))).Result; + if (!string.IsNullOrEmpty(productInfo.ProductParameters[Constants.Config.LIFETIME])) + { + var tempTerm = int.Parse(productInfo.ProductParameters[Constants.Config.LIFETIME]); + if (profileTerms.Contains(tempTerm)) + { + termLength = tempTerm; + } + else + { + _logger.LogError($"Specified term length of {tempTerm} does not match available terms for product ID {productInfo.ProductID}. Available terms are {string.Join(",", profileTerms)}"); + throw new Exception($"Specified term length of {tempTerm} does not match available terms for product ID {productInfo.ProductID}"); + } + } + else + { + termLength = profileTerms[0]; + } + int sslId; string priorSn = string.Empty; Certificate newCert = null; @@ -216,7 +236,7 @@ public async Task Enroll(string csr, string subject, Dictionar { csr = csr, orgId = requestOrgId, - term = Task.Run(async () => await GetProfileTerm(int.Parse(productInfo.ProductID))).Result, + term = termLength, certType = enrollmentProfile.id, //External requestor is expected to be an email. Use config to pull the enrollment field or send blank //sectigo will default to the account (API account) making the request. @@ -431,6 +451,13 @@ public Dictionary GetTemplateParameterAnnotations() Hidden = false, DefaultValue = "", Type = "String" + }, + [Constants.Config.LIFETIME] = new PropertyConfigInfo() + { + Comments = "OPTIONAL: The term length (in days) to use for enrollment. If not provided, the default is the first value available in the profile definition in your Sectigo account.", + Hidden = false, + DefaultValue = "", + Type = "String" } }; } @@ -520,7 +547,7 @@ public async Task Synchronize(BlockingCollection blockin _logger.LogError($"Synchronize task failed with the following message: {producerTask.Exception.Flatten().Message}"); throw producerTask.Exception.Flatten(); } - + _logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Processing record {certToAdd.Id}"); string dbCertId = null; int dbCertStatus = -1; //serial number is blank on certs that have not been issued (awaiting approval) @@ -566,23 +593,26 @@ public async Task Synchronize(BlockingCollection blockin } //Download to get full certdata required for sync process - _logger.LogTrace($"Attempt to Pickup Certificate {certToAdd.CommonName} (ID: {certToAdd.Id})"); + _logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Attempt to Pickup Certificate {certToAdd.CommonName}"); var certdataApi = Task.Run(async () => await client.PickupCertificate(certToAdd.Id, certToAdd.CommonName)).Result; if (certdataApi != null) certData = Convert.ToBase64String(certdataApi.GetRawCertData()); + if (certToAdd == null || String.IsNullOrEmpty(certToAdd.SerialNumber) || String.IsNullOrEmpty(certToAdd.CommonName) || String.IsNullOrEmpty(certData)) { _logger.LogDebug($"Certificate Data unavailable for {certToAdd.CommonName} (ID: {certToAdd.Id}). Skipping "); continue; } + _logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Retrieved cert data: {certData}"); string prodId = ""; try { - _logger.LogTrace($"Cert ID: {certToAdd.Id.ToString()}"); - _logger.LogTrace($"Sync ID: {syncReqId.ToString()}"); - _logger.LogTrace($"Product ID: {certToAdd.CertType.id.ToString()}"); + _logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Cert ID: {certToAdd.Id.ToString()}"); + _logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Sync ID: {syncReqId.ToString()}"); + _logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Product ID: {certToAdd.CertType.id.ToString()}"); + _logger.LogTrace($"SYNC TRACE ({certToAdd.Id}): Status: {certToAdd.status}"); prodId = certToAdd.CertType.id.ToString(); } catch { } @@ -674,11 +704,11 @@ private async Task GetOrganizationAsync(string orgName) return orgList.Organizations.Where(x => x.name.ToLower().Equals(orgName.ToLower())).FirstOrDefault(); } - private async Task GetProfileTerm(int profileId) + private async Task> GetProfileTerms(int profileId) { var client = SectigoClient.InitializeClient(_config, _certificateResolver); var profileList = await client.ListSslProfiles(); - return profileList.SslProfiles.Where(x => x.id == profileId).FirstOrDefault().terms[0]; + return profileList.SslProfiles.Where(x => x.id == profileId).FirstOrDefault().terms.ToList(); } private async Task GetProfile(int profileId)