From 731592248ed311dc253f00522fefe211e8d657af Mon Sep 17 00:00:00 2001 From: David Galey Date: Wed, 11 Feb 2026 02:56:42 -0500 Subject: [PATCH 1/2] allow manual lifetime specification --- CHANGELOG.md | 3 +++ sectigo-scm-caplugin/Constants.cs | 1 + sectigo-scm-caplugin/SectigoCAPlugin.cs | 33 ++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 3 deletions(-) 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/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..ac056cf 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" } }; } @@ -674,11 +701,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) From 60686a2bf31164f3dd76f59d047c6f2262724c47 Mon Sep 17 00:00:00 2001 From: Keyfactor Date: Wed, 11 Feb 2026 07:58:37 +0000 Subject: [PATCH 2/2] Update generated docs --- README.md | 1 + integration-manifest.json | 4 ++++ 2 files changed, 5 insertions(+) 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." } ] }