Skip to content

Commit

Permalink
Merge 1.1.0 to main
Browse files Browse the repository at this point in the history
  • Loading branch information
doebrowsk authored Jan 31, 2025
2 parents 12c9cec + 8e4fee0 commit a85aca9
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 20 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- 1.1.0
- Add support for external SANs/subject (not in CSR)
- 1.0.0
- First production release of the GCP CAS AnyCA Gateway REST plugin that implements:
* CA Sync:
Expand Down
49 changes: 47 additions & 2 deletions GCPCAS/Client/CreateCertificateRequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Google.Cloud.Security.PrivateCA.V1;
using Google.Protobuf.WellKnownTypes;
Expand All @@ -31,6 +32,8 @@ public class CreateCertificateRequestBuilder : ICreateCertificateRequestBuilder

private string _csrString;
private string _certificateTemplate;
private string _subject;
private List<string> _dnsSans;
private int _certificateLifetimeDays = GCPCASPluginConfig.DefaultCertificateLifetime;

public ICreateCertificateRequestBuilder WithCsr(string csr)
Expand Down Expand Up @@ -94,13 +97,30 @@ public ICreateCertificateRequestBuilder WithRequestFormat(RequestFormat requestF

public ICreateCertificateRequestBuilder WithSans(Dictionary<string, string[]> san)
{
if (san != null & san.Count > 0) _logger.LogTrace($"Found non-zero list of SANs - Ignoring and using SANs from CSR");
_dnsSans = new List<string>();
if (san != null & san.Count > 0)
{
var dnsKeys = san.Keys.Where(k => k.Contains("dns", StringComparison.OrdinalIgnoreCase)).ToList();
foreach (var key in dnsKeys)
{
_dnsSans.AddRange(san[key]);
}
_logger.LogTrace($"Found {_dnsSans.Count} SANs");
}
else
{
_logger.LogTrace($"Found no external SANs - Using SANs from CSR");
}
return this;
}

public ICreateCertificateRequestBuilder WithSubject(string subject)
{
if (!string.IsNullOrWhiteSpace(subject)) _logger.LogTrace($"Found non-empty subject {subject} - Ignoring and using CSR value");
if (!string.IsNullOrWhiteSpace(subject))
{
_logger.LogTrace($"Found non-empty subject {subject}");
_subject = subject;
}
return this;
}

Expand All @@ -109,10 +129,35 @@ public CreateCertificateRequest Build(string locationId, string projectId, strin
_logger.LogDebug("Constructing CreateCertificateRequest");
CaPoolName caPoolName = new CaPoolName(projectId, locationId, caPool);

CertificateConfig certConfig = new CertificateConfig();
certConfig.SubjectConfig = new CertificateConfig.Types.SubjectConfig();
bool useConfig = false;
if (!string.IsNullOrEmpty(_subject))
{
certConfig.SubjectConfig.Subject = new Subject
{
CommonName = Utilities.ParseSubject(_subject, "CN=", false),
Organization = Utilities.ParseSubject(_subject, "O=", false),
OrganizationalUnit = Utilities.ParseSubject(_subject, "OU=", false),
CountryCode = Utilities.ParseSubject(_subject, "C=", false),
Locality = Utilities.ParseSubject(_subject, "L=", false)
};
useConfig = true;
}
if (_dnsSans.Count > 0)
{
certConfig.SubjectConfig.SubjectAltName = new SubjectAltNames
{
DnsNames = { _dnsSans }
};
useConfig = true;
}

Certificate theCertificate = new Certificate
{
PemCsr = _csrString,
Lifetime = Duration.FromTimeSpan(new TimeSpan(_certificateLifetimeDays, 0, 0, 0)),
Config = (useConfig) ? certConfig : null,
};

if (!string.IsNullOrWhiteSpace(_certificateTemplate))
Expand Down
30 changes: 30 additions & 0 deletions GCPCAS/Utilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Keyfactor.Extensions.CAPlugin.GCPCAS
{
public class Utilities
{
public static string ParseSubject(string subject, string rdn, bool required = true)
{
string escapedSubject = subject.Replace("\\,", "|");
string rdnString = escapedSubject.Split(',').ToList().Where(x => x.Contains(rdn)).FirstOrDefault();

if (!string.IsNullOrEmpty(rdnString))
{
return rdnString.Replace(rdn, "").Replace("|", ",").Trim();
}
else if (required)
{
throw new Exception($"The request is missing a {rdn} value");
}
else
{
return null;
}
}
}
}
18 changes: 2 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ The [Google Cloud Platform (GCP) CA Services (CAS)](https://cloud.google.com/sec
* CA Sync:
* Download all certificates issued by connected Enterprise tier CAs in GCP CAS (full sync).
* Download all certificates issued by connected Enterprise tier CAs in GCP CAS issued after a specified time (incremental sync).
* Certificate enrollment for all published GoDaddy Certificate SKUs:
* Certificate enrollment for all published GCP Certificate SKUs:
* Support certificate enrollment (new keys/certificate).
* Support auto-enrollment (subject/SANs outside of the CSR)
* Certificate revocation:
* Request revocation of a previously issued certificate.

Expand Down Expand Up @@ -154,21 +155,6 @@ Both the Keyfactor Command and AnyCA Gateway REST servers must trust the root CA

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:

* **CertificateLifetimeDays** - The desired lifetime, in days, of the issued certificate. Used by GCP to create the `not_before_time` and `not_after_time` fields in the signed X.509 certificate. If the lifetime extends past the life of any CA in the issuing chain, this value will be truncated. Additionally, if the lifetime extends past the CA Pool's Maximum Lifetime, this value will be truncated accordingly. The default value is 365 days.
## Plugin Mechanics
### Enrollment/Renewal/Reissuance
The GCP CAS AnyCA Gateway REST plugin treats _all_ certificate enrollment as a new enrollment.
### Synchronization
The GCP CAS AnyCA Gateway REST plugin uses the [`ListCertificatesRequest` RPC](https://cloud.google.com/certificate-authority-service/docs/reference/rpc/google.cloud.security.privateca.v1#google.cloud.security.privateca.v1.ListCertificatesRequest) when synchronizing certificates from GCP. At the time the latest release, this RPC does not enable granularity to list certificates issued by a particular CA. As such, the CA Synchronization job implemented by the plugin will _always_ download all certificates issued by _any CA_ in the CA Pool.
> Friendly reminder to always follow the [GCP CAS best practices](https://cloud.google.com/certificate-authority-service/docs/best-practices)


## License
Expand Down
3 changes: 2 additions & 1 deletion docsource/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ The [Google Cloud Platform (GCP) CA Services (CAS)](https://cloud.google.com/sec
* CA Sync:
* Download all certificates issued by connected Enterprise tier CAs in GCP CAS (full sync).
* Download all certificates issued by connected Enterprise tier CAs in GCP CAS issued after a specified time (incremental sync).
* Certificate enrollment for all published GoDaddy Certificate SKUs:
* Certificate enrollment for all published GCP Certificate SKUs:
* Support certificate enrollment (new keys/certificate).
* Support auto-enrollment (subject/SANs outside of the CSR)
* Certificate revocation:
* Request revocation of a previously issued certificate.

Expand Down
2 changes: 1 addition & 1 deletion integration-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@
]
}
}
}
}

0 comments on commit a85aca9

Please sign in to comment.