From 472b45dcb484bf783f80383f9731077073f61df7 Mon Sep 17 00:00:00 2001 From: Dave Galey <89407235+dgaley@users.noreply.github.com> Date: Tue, 14 Feb 2023 14:01:50 -0500 Subject: [PATCH] Release 1.0 (#1) * update packages * added logging and licensing info Approved by Mark Thwaites for Pilot Ready 24032 --- .../workflows/keyfactor-starter-workflow.yml | 4 ++- README.md | 10 ++++-- amazon-acmpca-cagateway/ACMPCACertificate.cs | 9 +++++- amazon-acmpca-cagateway/ACMPCAConfig.cs | 9 +++++- amazon-acmpca-cagateway/ACMPCAConnector.cs | 32 +++++++++++++++++-- amazon-acmpca-cagateway/ACMPCAConstants.cs | 9 +++++- .../Client/ACMPCAClient.cs | 28 ++++++++++++++-- .../amazon-acmpca-cagateway.csproj | 14 ++++---- amazon-acmpca-cagateway/packages.config | 8 ++--- integration-manifest.json | 3 +- 10 files changed, 101 insertions(+), 25 deletions(-) diff --git a/.github/workflows/keyfactor-starter-workflow.yml b/.github/workflows/keyfactor-starter-workflow.yml index 4c91342..e43fba7 100644 --- a/.github/workflows/keyfactor-starter-workflow.yml +++ b/.github/workflows/keyfactor-starter-workflow.yml @@ -23,13 +23,15 @@ jobs: with: release_version: ${{ needs.call-create-github-release-workflow.outputs.release_version }} release_url: ${{ needs.call-create-github-release-workflow.outputs.release_url }} - release_dir: EXAMPLE_SOLUTION/bin/Release/BUILD_TARGET # TODO: set build output directory to upload as a release, relative to checkout workspace + release_dir: amazon-acmpca-cagateway/bin/Release secrets: token: ${{ secrets.PRIVATE_PACKAGE_ACCESS }} call-generate-readme-workflow: if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' uses: Keyfactor/actions/.github/workflows/generate-readme.yml@main + secrets: + token: ${{ secrets.APPROVE_README_PUSH }} call-update-catalog-workflow: needs: get-manifest-properties diff --git a/README.md b/README.md index 54fa861..3fc85b2 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,23 @@ This integration allows for the Synchronization, Enrollment, and Revocation of certificates from Amazon Certificate Manager Private CA. -#### Integration status: Prototype - Demonstration quality. Not for use in customer environments. +#### Integration status: Pilot - Ready for use in test environments. Not for use in production. ## About the Keyfactor AnyGateway CA Connector This repository contains an AnyGateway CA Connector, which is a plugin to the Keyfactor AnyGateway. AnyGateway CA Connectors allow Keyfactor Command to be used for inventory, issuance, and revocation of certificates from a third-party certificate authority. ---- +## Support for Amazon ACM PCA CA AnyGateway + +Amazon ACM PCA CA AnyGateway is open source and community supported, meaning that there is **no SLA** applicable for these tools. + +###### To report a problem or suggest a new feature, use the **[Issues](../../issues)** tab. If you want to contribute actual bug fixes or proposed enhancements, use the **[Pull requests](../../pulls)** tab. +___ ---- # Introduction This AnyGateway plug-in enables issuance, revocation, and synchronization of certificates from Amazon's AWS Certificate Manager Private CA diff --git a/amazon-acmpca-cagateway/ACMPCACertificate.cs b/amazon-acmpca-cagateway/ACMPCACertificate.cs index ffe3a23..9f1b50b 100644 --- a/amazon-acmpca-cagateway/ACMPCACertificate.cs +++ b/amazon-acmpca-cagateway/ACMPCACertificate.cs @@ -1,4 +1,11 @@ -using Newtonsoft.Json; +// Copyright 2022 Keyfactor +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions +// and limitations under the License. + +using Newtonsoft.Json; using System; using System.Collections.Generic; diff --git a/amazon-acmpca-cagateway/ACMPCAConfig.cs b/amazon-acmpca-cagateway/ACMPCAConfig.cs index e79db12..a34a948 100644 --- a/amazon-acmpca-cagateway/ACMPCAConfig.cs +++ b/amazon-acmpca-cagateway/ACMPCAConfig.cs @@ -1,4 +1,11 @@ -using Amazon; +// Copyright 2022 Keyfactor +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions +// and limitations under the License. + +using Amazon; using System; using System.Collections.Generic; diff --git a/amazon-acmpca-cagateway/ACMPCAConnector.cs b/amazon-acmpca-cagateway/ACMPCAConnector.cs index a29d179..c6168ba 100644 --- a/amazon-acmpca-cagateway/ACMPCAConnector.cs +++ b/amazon-acmpca-cagateway/ACMPCAConnector.cs @@ -20,8 +20,11 @@ using CSS.PKI; using Keyfactor.Extensions.AnyGateway.Amazon.ACMPCA.Client; +using Keyfactor.Logging; using Keyfactor.PKI.PEM; +using Microsoft.Extensions.Logging; + using Newtonsoft.Json; using System; @@ -45,16 +48,20 @@ public class ACMPCAConnector : BaseCAConnector, ICAConnectorConfigInfoProvider private ACMPCAClient Client { get; set; } + private static readonly ILogger Log = LogHandler.GetClassLogger(); + #endregion Fields and Constructors #region ICAConnector Methods public override void Initialize(ICAConnectorConfigProvider configProvider) { + Log.MethodEntry(LogLevel.Trace); string rawconfig = JsonConvert.SerializeObject(configProvider.CAConnectionData); ACMPCAConfig config = JsonConvert.DeserializeObject(rawconfig); Client = new ACMPCAClient(config); + Log.MethodExit(LogLevel.Trace); } /// @@ -70,6 +77,7 @@ public override void Initialize(ICAConnectorConfigProvider configProvider) /// public override EnrollmentResult Enroll(ICertificateDataReader certificateDataReader, string csr, string subject, Dictionary san, EnrollmentProductInfo productInfo, PKIConstants.X509.RequestFormat requestFormat, RequestUtilities.EnrollmentType enrollmentType) { + Log.MethodEntry(LogLevel.Trace); string csrString = PemUtilities.DERToPEM(PemUtilities.PEMToDER(csr), PemUtilities.PemObjectType.CertRequest); IssueCertificateRequest issueRequest = new IssueCertificateRequest { @@ -91,7 +99,7 @@ public override EnrollmentResult Enroll(ICertificateDataReader certificateDataRe Thread.Sleep(1000); CAConnectorCertificate cert = Client.GetCertificateByARN(certArn); - + Log.MethodExit(LogLevel.Trace); return new EnrollmentResult() { CARequestID = certArn.Split('/')[3], @@ -108,7 +116,9 @@ public override EnrollmentResult Enroll(ICertificateDataReader certificateDataRe /// public override CAConnectorCertificate GetSingleRecord(string caRequestID) { + Log.MethodEntry(LogLevel.Trace); CAConnectorCertificate cert = Client.GetCertificateByRequestID(caRequestID); + Log.MethodExit(LogLevel.Trace); return cert; } @@ -117,7 +127,9 @@ public override CAConnectorCertificate GetSingleRecord(string caRequestID) /// public override void Ping() { + Log.MethodEntry(LogLevel.Trace); Client.VerifyCAConnection(); + Log.MethodExit(LogLevel.Trace); } /// @@ -128,6 +140,7 @@ public override void Ping() /// The revocation reason. public override int Revoke(string caRequestID, string hexSerialNumber, uint revocationReason) { + Log.MethodEntry(LogLevel.Trace); string serialNum = caRequestID; RevokeCertificateRequest revokeCertificateRequest = new RevokeCertificateRequest() @@ -169,7 +182,9 @@ public override int Revoke(string caRequestID, string hexSerialNumber, uint revo revokeCertificateRequest.RevocationReason = RevocationReason.UNSPECIFIED; break; } - return Client.RevokeCertificate(revokeCertificateRequest); + var ret = Client.RevokeCertificate(revokeCertificateRequest); + Log.MethodExit(LogLevel.Trace); + return ret; } /// @@ -181,8 +196,9 @@ public override int Revoke(string caRequestID, string hexSerialNumber, uint revo /// The cancellation token. public override void Synchronize(ICertificateDataReader certificateDataReader, BlockingCollection blockingBuffer, CertificateAuthoritySyncInfo certificateAuthoritySyncInfo, CancellationToken cancelToken) { + Log.MethodEntry(LogLevel.Trace); var certs = Client.GetAuditReport(); - + Log.LogDebug($"Sync found {certs.Count} certs."); foreach (var cert in certs) { CAConnectorCertificate dbCert = certificateDataReader.GetCertificateRecord(cert.CertificateARN, string.Empty); @@ -204,6 +220,7 @@ public override void Synchronize(ICertificateDataReader certificateDataReader, B } dbCert.CARequestID = cert.CertificateARN.Split('/')[3]; + Log.LogTrace($"Processing cert with request ID {dbCert.CARequestID}"); dbCert.Certificate = pcaCert.Certificate; dbCert.Status = status; if (status == (int)RequestDisposition.REVOKED) @@ -216,7 +233,12 @@ public override void Synchronize(ICertificateDataReader certificateDataReader, B blockingBuffer.Add(dbCert); } + else + { + Log.LogTrace($"Cert with request ID {cert.CertificateARN.Split('/')[3]} has unchanged status and this is not a full sync, skipping..."); + } } + Log.MethodExit(LogLevel.Trace); } /// @@ -225,6 +247,7 @@ public override void Synchronize(ICertificateDataReader certificateDataReader, B /// The information used to connect to the CA. public override void ValidateCAConnectionInfo(Dictionary connectionInfo) { + Log.MethodEntry(LogLevel.Trace); List errors = new List(); string accessKey = connectionInfo.ContainsKey(ACMPCAConstants.ACCESS_KEY) ? (string)connectionInfo[ACMPCAConstants.ACCESS_KEY] : string.Empty; @@ -273,6 +296,7 @@ public override void ValidateCAConnectionInfo(Dictionary connect { ThrowValidationException(errors); } + Log.MethodExit(LogLevel.Trace); } /// @@ -281,6 +305,7 @@ public override void ValidateCAConnectionInfo(Dictionary connect /// The product information. public override void ValidateProductInfo(EnrollmentProductInfo productInfo, Dictionary connectionInfo) { + Log.MethodEntry(LogLevel.Trace); try { var template = ACMPCAConstants.TemplateARNs[productInfo.ProductID.ToLower()]; @@ -293,6 +318,7 @@ public override void ValidateProductInfo(EnrollmentProductInfo productInfo, Dict { throw new Exception("ProductID not recognized.", ex); } + Log.MethodExit(LogLevel.Trace); } #region Obsolete Methods diff --git a/amazon-acmpca-cagateway/ACMPCAConstants.cs b/amazon-acmpca-cagateway/ACMPCAConstants.cs index e2eca27..556ee92 100644 --- a/amazon-acmpca-cagateway/ACMPCAConstants.cs +++ b/amazon-acmpca-cagateway/ACMPCAConstants.cs @@ -1,4 +1,11 @@ -using System; +// Copyright 2022 Keyfactor +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions +// and limitations under the License. + +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/amazon-acmpca-cagateway/Client/ACMPCAClient.cs b/amazon-acmpca-cagateway/Client/ACMPCAClient.cs index f4070ea..10838bf 100644 --- a/amazon-acmpca-cagateway/Client/ACMPCAClient.cs +++ b/amazon-acmpca-cagateway/Client/ACMPCAClient.cs @@ -1,4 +1,11 @@ -using Amazon; +// Copyright 2022 Keyfactor +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions +// and limitations under the License. + +using Amazon; using Amazon.ACMPCA; using Amazon.ACMPCA.Model; using Amazon.S3; @@ -41,21 +48,25 @@ public ACMPCAClient(ACMPCAConfig config) public string RequestCertificate(IssueCertificateRequest request) { + Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace); request.CertificateAuthorityArn = Config.CAArn; request.SigningAlgorithm = SigningAlgorithm.SHA256WITHRSA; var response = GetPCAClient().IssueCertificate(request); - + Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace); return response.CertificateArn; } public CAConnectorCertificate GetCertificateByRequestID(string requestId) { + Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace); string certArn = $"{Config.CAArn}/certificate/{requestId}"; + Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace); return GetCertificateByARN(certArn); } public CAConnectorCertificate GetCertificateByARN(string certARN) { + Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace); GetCertificateRequest getCertificateRequest = new GetCertificateRequest() { CertificateArn = certARN, @@ -80,6 +91,7 @@ public CAConnectorCertificate GetCertificateByARN(string certARN) if (string.IsNullOrEmpty(getCertificateResponse.Certificate)) { Logger.Error($"Certificate with ARN {certARN} not found."); + throw new Exception($"Certificate with ARN {certARN} not found."); } X509Certificate2 cert = CertificateConverterFactory.FromPEM(getCertificateResponse.Certificate).ToX509Certificate2(); @@ -115,7 +127,7 @@ public CAConnectorCertificate GetCertificateByARN(string certARN) { product += "ClientAuth"; } - + Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace); return new CAConnectorCertificate { CARequestID = certARN.Split('/')[3], @@ -128,6 +140,7 @@ public CAConnectorCertificate GetCertificateByARN(string certARN) public void VerifyCAConnection() { + Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace); try { var request = new DescribeCertificateAuthorityRequest() @@ -146,17 +159,21 @@ public void VerifyCAConnection() Logger.Error($"Unable to communicate with Amazon CA: {ex.Message}"); throw; } + Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace); } public int RevokeCertificate(RevokeCertificateRequest request) { + Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace); request.CertificateAuthorityArn = Config.CAArn; var _ = GetPCAClient().RevokeCertificate(request); + Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace); return (int)RequestDisposition.REVOKED; } public List GetAuditReport() { + Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace); CreateCertificateAuthorityAuditReportRequest request = new CreateCertificateAuthorityAuditReportRequest() { CertificateAuthorityArn = Config.CAArn, @@ -176,18 +193,22 @@ public List GetAuditReport() using (StreamReader reader = new StreamReader(responseStream)) { string respStr = reader.ReadToEnd(); + Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace); return JsonConvert.DeserializeObject>(respStr); } } private IAmazonACMPCA GetPCAClient() { + Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace); IAmazonACMPCA client = new AmazonACMPCAClient(Config.AccessKey, Config.AccessSecret, Config.GetRegion()); + Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace); return client; } private IAmazonS3 GetS3Client() { + Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace); string region = ""; using (IAmazonS3 tempClient = new AmazonS3Client(Config.AccessKey, Config.AccessSecret, Config.GetRegion())) { @@ -195,6 +216,7 @@ private IAmazonS3 GetS3Client() region = bucketResponse.Location.Value; } var s3Client = new AmazonS3Client(Config.AccessKey, Config.AccessSecret, RegionEndpoint.GetBySystemName(region)); + Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace); return s3Client; } } diff --git a/amazon-acmpca-cagateway/amazon-acmpca-cagateway.csproj b/amazon-acmpca-cagateway/amazon-acmpca-cagateway.csproj index 3772825..41b245f 100644 --- a/amazon-acmpca-cagateway/amazon-acmpca-cagateway.csproj +++ b/amazon-acmpca-cagateway/amazon-acmpca-cagateway.csproj @@ -32,16 +32,16 @@ - ..\packages\AWSSDK.ACMPCA.3.7.4.77\lib\net45\AWSSDK.ACMPCA.dll + ..\packages\AWSSDK.ACMPCA.3.7.4.83\lib\net45\AWSSDK.ACMPCA.dll - ..\packages\AWSSDK.CertificateManager.3.7.1.152\lib\net45\AWSSDK.CertificateManager.dll + ..\packages\AWSSDK.CertificateManager.3.7.2.5\lib\net45\AWSSDK.CertificateManager.dll - ..\packages\AWSSDK.Core.3.7.12.23\lib\net45\AWSSDK.Core.dll + ..\packages\AWSSDK.Core.3.7.13.18\lib\net45\AWSSDK.Core.dll - ..\packages\AWSSDK.S3.3.7.9.61\lib\net45\AWSSDK.S3.dll + ..\packages\AWSSDK.S3.3.7.9.67\lib\net45\AWSSDK.S3.dll ..\packages\Portable.BouncyCastle.1.8.9\lib\net40\BouncyCastle.Crypto.dll @@ -160,9 +160,9 @@ - - - + + + \ No newline at end of file diff --git a/amazon-acmpca-cagateway/packages.config b/amazon-acmpca-cagateway/packages.config index e7a86be..0c9e641 100644 --- a/amazon-acmpca-cagateway/packages.config +++ b/amazon-acmpca-cagateway/packages.config @@ -1,9 +1,9 @@  - - - - + + + + diff --git a/integration-manifest.json b/integration-manifest.json index f388284..08c6e64 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -2,7 +2,8 @@ "$schema": "https://keyfactor.github.io/integration-manifest-schema.json", "integration_type": "ca-gateway", "name": "Amazon ACM PCA CA AnyGateway", - "status": "prototype", + "status": "pilot", + "support_level": "community", "link_github": false, "description": "This integration allows for the Synchronization, Enrollment, and Revocation of certificates from Amazon Certificate Manager Private CA." } \ No newline at end of file