Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev 1.1 #4

Merged
merged 5 commits into from
Jan 31, 2025
Merged
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: 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 @@
]
}
}
}
}
Loading