Skip to content

Commit

Permalink
Release 1.0 (#1)
Browse files Browse the repository at this point in the history
* update packages
* added logging and licensing info
Approved by Mark Thwaites for Pilot Ready 24032
  • Loading branch information
dgaley authored Feb 14, 2023
1 parent 6cc5b18 commit 472b45d
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 25 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/keyfactor-starter-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion amazon-acmpca-cagateway/ACMPCACertificate.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
9 changes: 8 additions & 1 deletion amazon-acmpca-cagateway/ACMPCAConfig.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
32 changes: 29 additions & 3 deletions amazon-acmpca-cagateway/ACMPCAConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,16 +48,20 @@ public class ACMPCAConnector : BaseCAConnector, ICAConnectorConfigInfoProvider

private ACMPCAClient Client { get; set; }

private static readonly ILogger Log = LogHandler.GetClassLogger<ACMPCAConnector>();

#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<ACMPCAConfig>(rawconfig);

Client = new ACMPCAClient(config);
Log.MethodExit(LogLevel.Trace);
}

/// <summary>
Expand All @@ -70,6 +77,7 @@ public override void Initialize(ICAConnectorConfigProvider configProvider)
/// <returns></returns>
public override EnrollmentResult Enroll(ICertificateDataReader certificateDataReader, string csr, string subject, Dictionary<string, string[]> 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
{
Expand All @@ -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],
Expand All @@ -108,7 +116,9 @@ public override EnrollmentResult Enroll(ICertificateDataReader certificateDataRe
/// <returns></returns>
public override CAConnectorCertificate GetSingleRecord(string caRequestID)
{
Log.MethodEntry(LogLevel.Trace);
CAConnectorCertificate cert = Client.GetCertificateByRequestID(caRequestID);
Log.MethodExit(LogLevel.Trace);
return cert;
}

Expand All @@ -117,7 +127,9 @@ public override CAConnectorCertificate GetSingleRecord(string caRequestID)
/// </summary>
public override void Ping()
{
Log.MethodEntry(LogLevel.Trace);
Client.VerifyCAConnection();
Log.MethodExit(LogLevel.Trace);
}

/// <summary>
Expand All @@ -128,6 +140,7 @@ public override void Ping()
/// <param name="revocationReason">The revocation reason.</param>
public override int Revoke(string caRequestID, string hexSerialNumber, uint revocationReason)
{
Log.MethodEntry(LogLevel.Trace);
string serialNum = caRequestID;

RevokeCertificateRequest revokeCertificateRequest = new RevokeCertificateRequest()
Expand Down Expand Up @@ -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;
}

/// <summary>
Expand All @@ -181,8 +196,9 @@ public override int Revoke(string caRequestID, string hexSerialNumber, uint revo
/// <param name="cancelToken">The cancellation token.</param>
public override void Synchronize(ICertificateDataReader certificateDataReader, BlockingCollection<CAConnectorCertificate> 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);
Expand All @@ -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)
Expand All @@ -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);
}

/// <summary>
Expand All @@ -225,6 +247,7 @@ public override void Synchronize(ICertificateDataReader certificateDataReader, B
/// <param name="connectionInfo">The information used to connect to the CA.</param>
public override void ValidateCAConnectionInfo(Dictionary<string, object> connectionInfo)
{
Log.MethodEntry(LogLevel.Trace);
List<string> errors = new List<string>();

string accessKey = connectionInfo.ContainsKey(ACMPCAConstants.ACCESS_KEY) ? (string)connectionInfo[ACMPCAConstants.ACCESS_KEY] : string.Empty;
Expand Down Expand Up @@ -273,6 +296,7 @@ public override void ValidateCAConnectionInfo(Dictionary<string, object> connect
{
ThrowValidationException(errors);
}
Log.MethodExit(LogLevel.Trace);
}

/// <summary>
Expand All @@ -281,6 +305,7 @@ public override void ValidateCAConnectionInfo(Dictionary<string, object> connect
/// <param name="productInfo">The product information.</param>
public override void ValidateProductInfo(EnrollmentProductInfo productInfo, Dictionary<string, object> connectionInfo)
{
Log.MethodEntry(LogLevel.Trace);
try
{
var template = ACMPCAConstants.TemplateARNs[productInfo.ProductID.ToLower()];
Expand All @@ -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
Expand Down
9 changes: 8 additions & 1 deletion amazon-acmpca-cagateway/ACMPCAConstants.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
28 changes: 25 additions & 3 deletions amazon-acmpca-cagateway/Client/ACMPCAClient.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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();

Expand Down Expand Up @@ -115,7 +127,7 @@ public CAConnectorCertificate GetCertificateByARN(string certARN)
{
product += "ClientAuth";
}

Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace);
return new CAConnectorCertificate
{
CARequestID = certARN.Split('/')[3],
Expand All @@ -128,6 +140,7 @@ public CAConnectorCertificate GetCertificateByARN(string certARN)

public void VerifyCAConnection()
{
Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace);
try
{
var request = new DescribeCertificateAuthorityRequest()
Expand All @@ -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<ACMPCACertificate> GetAuditReport()
{
Logger.MethodEntry(ILogExtensions.MethodLogLevel.Trace);
CreateCertificateAuthorityAuditReportRequest request = new CreateCertificateAuthorityAuditReportRequest()
{
CertificateAuthorityArn = Config.CAArn,
Expand All @@ -176,25 +193,30 @@ public List<ACMPCACertificate> GetAuditReport()
using (StreamReader reader = new StreamReader(responseStream))
{
string respStr = reader.ReadToEnd();
Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace);
return JsonConvert.DeserializeObject<List<ACMPCACertificate>>(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()))
{
var bucketResponse = tempClient.GetBucketLocation(Config.S3Bucket);
region = bucketResponse.Location.Value;
}
var s3Client = new AmazonS3Client(Config.AccessKey, Config.AccessSecret, RegionEndpoint.GetBySystemName(region));
Logger.MethodExit(ILogExtensions.MethodLogLevel.Trace);
return s3Client;
}
}
Expand Down
14 changes: 7 additions & 7 deletions amazon-acmpca-cagateway/amazon-acmpca-cagateway.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="AWSSDK.ACMPCA, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604, processorArchitecture=MSIL">
<HintPath>..\packages\AWSSDK.ACMPCA.3.7.4.77\lib\net45\AWSSDK.ACMPCA.dll</HintPath>
<HintPath>..\packages\AWSSDK.ACMPCA.3.7.4.83\lib\net45\AWSSDK.ACMPCA.dll</HintPath>
</Reference>
<Reference Include="AWSSDK.CertificateManager, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604, processorArchitecture=MSIL">
<HintPath>..\packages\AWSSDK.CertificateManager.3.7.1.152\lib\net45\AWSSDK.CertificateManager.dll</HintPath>
<HintPath>..\packages\AWSSDK.CertificateManager.3.7.2.5\lib\net45\AWSSDK.CertificateManager.dll</HintPath>
</Reference>
<Reference Include="AWSSDK.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604, processorArchitecture=MSIL">
<HintPath>..\packages\AWSSDK.Core.3.7.12.23\lib\net45\AWSSDK.Core.dll</HintPath>
<HintPath>..\packages\AWSSDK.Core.3.7.13.18\lib\net45\AWSSDK.Core.dll</HintPath>
</Reference>
<Reference Include="AWSSDK.S3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=885c28607f98e604, processorArchitecture=MSIL">
<HintPath>..\packages\AWSSDK.S3.3.7.9.61\lib\net45\AWSSDK.S3.dll</HintPath>
<HintPath>..\packages\AWSSDK.S3.3.7.9.67\lib\net45\AWSSDK.S3.dll</HintPath>
</Reference>
<Reference Include="BouncyCastle.Crypto, Version=1.8.9.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
<HintPath>..\packages\Portable.BouncyCastle.1.8.9\lib\net40\BouncyCastle.Crypto.dll</HintPath>
Expand Down Expand Up @@ -160,9 +160,9 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\AWSSDK.ACMPCA.3.7.4.77\analyzers\dotnet\cs\AWSSDK.ACMPCA.CodeAnalysis.dll" />
<Analyzer Include="..\packages\AWSSDK.CertificateManager.3.7.1.152\analyzers\dotnet\cs\AWSSDK.CertificateManager.CodeAnalysis.dll" />
<Analyzer Include="..\packages\AWSSDK.S3.3.7.9.61\analyzers\dotnet\cs\AWSSDK.S3.CodeAnalysis.dll" />
<Analyzer Include="..\packages\AWSSDK.ACMPCA.3.7.4.83\analyzers\dotnet\cs\AWSSDK.ACMPCA.CodeAnalysis.dll" />
<Analyzer Include="..\packages\AWSSDK.CertificateManager.3.7.2.5\analyzers\dotnet\cs\AWSSDK.CertificateManager.CodeAnalysis.dll" />
<Analyzer Include="..\packages\AWSSDK.S3.3.7.9.67\analyzers\dotnet\cs\AWSSDK.S3.CodeAnalysis.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Loading

0 comments on commit 472b45d

Please sign in to comment.