Skip to content

Commit 57f1c9e

Browse files
committed
Fix for the ARN not getting attached to enrollment request.
1 parent ac43462 commit 57f1c9e

8 files changed

Lines changed: 96 additions & 79 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,5 @@ MigrationBackup/
361361

362362
# Fody - auto-generated XML schema
363363
FodyWeavers.xsd
364+
/.claude
365+
/issue.txt

README.md

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<h1 align="center" style="border-bottom: none">
2-
AWSPCA CA Gateway AnyCA Gateway REST Plugin
2+
AWSPCA CAPlugin AnyCA Gateway REST Plugin
33
</h1>
44

55
<p align="center">
66
<!-- Badges -->
77
<img src="https://img.shields.io/badge/integration_status-pilot-3D1973?style=flat-square" alt="Integration Status: pilot" />
8-
<a href="https://github.com/Keyfactor/aws-pca-caplugin/releases"><img src="https://img.shields.io/github/v/release/Keyfactor/aws-pca-caplugin?style=flat-square" alt="Release" /></a>
9-
<img src="https://img.shields.io/github/issues/Keyfactor/aws-pca-caplugin?style=flat-square" alt="Issues" />
10-
<img src="https://img.shields.io/github/downloads/Keyfactor/aws-pca-caplugin/total?style=flat-square&label=downloads&color=28B905" alt="GitHub Downloads (all assets, all releases)" />
8+
<a href="https://github.com/Keyfactor/aws-pca-caplugin-dev/releases"><img src="https://img.shields.io/github/v/release/Keyfactor/aws-pca-caplugin-dev?style=flat-square" alt="Release" /></a>
9+
<img src="https://img.shields.io/github/issues/Keyfactor/aws-pca-caplugin-dev?style=flat-square" alt="Issues" />
10+
<img src="https://img.shields.io/github/downloads/Keyfactor/aws-pca-caplugin-dev/total?style=flat-square&label=downloads&color=28B905" alt="GitHub Downloads (all assets, all releases)" />
1111
</p>
1212

1313
<p align="center">
@@ -38,10 +38,10 @@ This integration allows for the Synchronization, Enrollment, and Revocation of c
3838

3939
## Compatibility
4040

41-
The AWSPCA CA Gateway AnyCA Gateway REST plugin is compatible with the Keyfactor AnyCA Gateway REST 25.4.0 and later.
41+
The AWSPCA CAPlugin AnyCA Gateway REST plugin is compatible with the Keyfactor AnyCA Gateway REST 25.4.0 and later.
4242

4343
## Support
44-
The AWSPCA CA Gateway AnyCA Gateway REST plugin is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket with your Keyfactor representative. If you have a support issue, please open a support ticket via the Keyfactor Support Portal at https://support.keyfactor.com.
44+
The AWSPCA CAPlugin AnyCA Gateway REST plugin is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket with your Keyfactor representative. If you have a support issue, please open a support ticket via the Keyfactor Support Portal at https://support.keyfactor.com.
4545

4646
> 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.
4747
@@ -53,7 +53,7 @@ This integration is tested and confirmed as working for Anygateway REST 24.4 and
5353

5454
1. Install the AnyCA Gateway REST per the [official Keyfactor documentation](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/InstallIntroduction.htm).
5555

56-
2. On the server hosting the AnyCA Gateway REST, download and unzip the latest [AWSPCA CA Gateway AnyCA Gateway REST plugin](https://github.com/Keyfactor/aws-pca-caplugin/releases/latest) from GitHub.
56+
2. On the server hosting the AnyCA Gateway REST, download and unzip the latest [AWSPCA CAPlugin AnyCA Gateway REST plugin](https://github.com/Keyfactor/aws-pca-caplugin-dev/releases/latest) from GitHub.
5757

5858
3. Copy the unzipped directory (usually called `net6.0` or `net8.0`) to the Extensions directory:
5959

@@ -64,11 +64,11 @@ This integration is tested and confirmed as working for Anygateway REST 24.4 and
6464
Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net8.0\Extensions
6565
```
6666

67-
> The directory containing the AWSPCA CA Gateway AnyCA Gateway REST plugin DLLs (`net6.0` or `net8.0`) can be named anything, as long as it is unique within the `Extensions` directory.
67+
> The directory containing the AWSPCA CAPlugin AnyCA Gateway REST plugin DLLs (`net6.0` or `net8.0`) can be named anything, as long as it is unique within the `Extensions` directory.
6868

6969
4. Restart the AnyCA Gateway REST service.
7070

71-
5. Navigate to the AnyCA Gateway REST portal and verify that the Gateway recognizes the AWSPCA CA Gateway plugin by hovering over the ⓘ symbol to the right of the Gateway on the top left of the portal.
71+
5. Navigate to the AnyCA Gateway REST portal and verify that the Gateway recognizes the AWSPCA CAPlugin plugin by hovering over the ⓘ symbol to the right of the Gateway on the top left of the portal.
7272

7373
## Configuration
7474

@@ -100,11 +100,12 @@ This integration is tested and confirmed as working for Anygateway REST 24.4 and
100100
* **ExternalId** - Optional sts:ExternalId to supply on AssumeRole calls.
101101
* **Enabled** - Flag to Enable or Disable gateway functionality. Disabling is primarily used to allow creation of the CA prior to configuration information being available.
102102

103-
2. Define [Certificate Profiles](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCP-Gateway.htm) and [Certificate Templates](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCA-Gateway.htm) for the Certificate Authority as required. One Certificate Profile must be defined per Certificate Template. It's recommended that each Certificate Profile be named after the Product ID. The AWSPCA CA Gateway plugin supports the following product IDs:
103+
2. Define [Certificate Profiles](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCP-Gateway.htm) and [Certificate Templates](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCA-Gateway.htm) for the Certificate Authority as required. One Certificate Profile must be defined per Certificate Template. It's recommended that each Certificate Profile be named after the Product ID. The AWSPCA CAPlugin plugin supports the following product IDs:
104104
105105
* **EndEntity**
106106
* **EndEntityClientAuth**
107107
* **EndEntityServerAuth**
108+
* **CodeSigning**
108109
109110
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.
110111
@@ -119,11 +120,6 @@ This integration is tested and confirmed as working for Anygateway REST 24.4 and
119120
The CAPlugin currently supports **one** authentication method: **AWS Access Key ID + Secret Access Key**.
120121
**OAuth** and **Default SDK authentication** will be enabled in later updates. There is functionality present via the **Keyfactor AWS Authentication** library, but these alternate methods are currently ***untested***.
121122
122-
### Known Issues
123-
124-
At present, a fresh install of Keyfactor Command 24.4 used in conjuction with Keyfactor Gateway REST 25.4.0.0 is confirmed as working. A fresh install of Command 25.3 used with REST 25.4.0.0 is also confirmed as working.
125-
Latest version of Command 25.4 may run into issues, investigation into compatibility issues is ongoing.
126-
127123
### What you need ready
128124
129125
Before configuring the CAPlugin, have the following prepared:

aws-pca-caplugin/AWSPCACAPlugin.cs

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -415,60 +415,60 @@ public async Task<EnrollmentResult> Enroll(
415415
switch (enrollmentType)
416416
{
417417
case EnrollmentType.New:
418-
{
419-
return await IssueAndFetchAsync(
420-
csr,
421-
productInfo.ProductID,
422-
days,
423-
signingAlgorithm,
424-
"Certificate Issued")
425-
.ConfigureAwait(false);
426-
}
418+
{
419+
return await IssueAndFetchAsync(
420+
csr,
421+
productInfo.ProductID,
422+
days,
423+
signingAlgorithm,
424+
"Certificate Issued")
425+
.ConfigureAwait(false);
426+
}
427427

428428
case EnrollmentType.RenewOrReissue:
429-
{
430-
if (productInfo.ProductParameters == null ||
431-
!TryGetProductParam(productInfo.ProductParameters, "PriorCertSN", out var priorSn) ||
432-
string.IsNullOrWhiteSpace(priorSn))
433-
return new EnrollmentResult
434-
{
435-
Status = (int)EndEntityStatus.FAILED,
436-
StatusMessage =
437-
"Renew/Reissue requires ProductParameters['PriorCertSN'] (hex serial number)."
438-
};
439-
440-
string priorRequestId;
441-
try
442-
{
443-
priorRequestId = await _certificateDataReader
444-
.GetRequestIDBySerialNumber(priorSn)
445-
.ConfigureAwait(false);
446-
}
447-
catch (Exception ex)
429+
{
430+
if (productInfo.ProductParameters == null ||
431+
!TryGetProductParam(productInfo.ProductParameters, "PriorCertSN", out var priorSn) ||
432+
string.IsNullOrWhiteSpace(priorSn))
433+
return new EnrollmentResult
448434
{
449-
return new EnrollmentResult
450-
{
451-
Status = (int)EndEntityStatus.FAILED,
452-
StatusMessage = $"Could not resolve PriorCertSN to request id: {ex.Message}"
453-
};
454-
}
455-
456-
var expiration = _certificateDataReader.GetExpirationDateByRequestId(priorRequestId);
457-
var isRenewal = expiration.HasValue && expiration.Value.ToUniversalTime() <= DateTime.UtcNow;
435+
Status = (int)EndEntityStatus.FAILED,
436+
StatusMessage =
437+
"Renew/Reissue requires ProductParameters['PriorCertSN'] (hex serial number)."
438+
};
458439

459-
var msg = isRenewal ? "Certificate Renewed" : "Certificate Reissued";
460-
var token = BuildIdempotencyToken(isRenewal ? "renew" : "reissue", priorRequestId, csr);
461-
462-
// Still "IssueCertificate" under the hood; PCA doesn't have first-class renew/reissue.
463-
return await IssueAndFetchAsync(
464-
csr,
465-
productInfo.ProductID,
466-
days,
467-
msg,
468-
// Optional: stable-ish idempotency (helps avoid duplicates if caller retries quickly)
469-
token)
440+
string priorRequestId;
441+
try
442+
{
443+
priorRequestId = await _certificateDataReader
444+
.GetRequestIDBySerialNumber(priorSn)
470445
.ConfigureAwait(false);
471446
}
447+
catch (Exception ex)
448+
{
449+
return new EnrollmentResult
450+
{
451+
Status = (int)EndEntityStatus.FAILED,
452+
StatusMessage = $"Could not resolve PriorCertSN to request id: {ex.Message}"
453+
};
454+
}
455+
456+
var expiration = _certificateDataReader.GetExpirationDateByRequestId(priorRequestId);
457+
var isRenewal = expiration.HasValue && expiration.Value.ToUniversalTime() <= DateTime.UtcNow;
458+
459+
var msg = isRenewal ? "Certificate Renewed" : "Certificate Reissued";
460+
var token = BuildIdempotencyToken(isRenewal ? "renew" : "reissue", priorRequestId, csr);
461+
462+
// Still "IssueCertificate" under the hood; PCA doesn't have first-class renew/reissue.
463+
return await IssueAndFetchAsync(
464+
csr,
465+
productInfo.ProductID,
466+
days,
467+
msg,
468+
// Optional: stable-ish idempotency (helps avoid duplicates if caller retries quickly)
469+
token)
470+
.ConfigureAwait(false);
471+
}
472472

473473
default:
474474
return new EnrollmentResult
@@ -685,7 +685,7 @@ public Dictionary<string, PropertyConfigInfo> GetCAConnectorAnnotations()
685685
DefaultValue = "",
686686
Type = "String"
687687
},
688-
[Constants.Enabled] = new()
688+
[Constants.Enabled] = new ()
689689
{
690690
Comments = "Flag to Enable or Disable gateway functionality. Disabling is primarily used to allow creation of the CA prior to configuration information being available.",
691691
Hidden = false,

aws-pca-caplugin/AWSPCACAPlugin.csproj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@
1313

1414

1515
<Target Name="CustomPostBuild" AfterTargets="PostBuildEvent">
16-
<Exec Condition="'$(Configuration)'=='DebugAndPush'"
17-
Command="PowerShell -ExecutionPolicy Bypass -File &quot;C:\Users\mkachkaev\source\repos\scripts\SyncScriptAWS_GT.ps1&quot;&#xA;" />
16+
<Exec Condition="'$(Configuration)'=='DebugAndPush'" Command="PowerShell -ExecutionPolicy Bypass -File &quot;C:\Users\mkachkaev\source\scripts\SyncScriptAWS_GT.ps1&quot;&#xA;" />
1817
</Target>
1918

2019

2120
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
22-
<PackageReference Include="Keyfactor.AnyGateway.IAnyCAPlugin" Version="3.1.0" />
23-
<PackageReference Include="Keyfactor.PKI" Version="8.2.2" />
21+
<PackageReference Include="Keyfactor.AnyGateway.IAnyCAPlugin" Version="3.2.0" />
22+
<PackageReference Include="Keyfactor.PKI" Version="8.3.1" />
2423
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
2524
</ItemGroup>
2625

@@ -35,7 +34,7 @@
3534
<PackageReference Include="AWSSDK.ACMPCA" Version="4.0.3.11" />
3635
<PackageReference Include="AWSSDK.Core" Version="4.0.3.12" />
3736
<PackageReference Include="AWSSDK.S3" Version="4.0.18.3" />
38-
<PackageReference Include="Keyfactor.Common" Version="2.9.0" />
37+
<PackageReference Include="Keyfactor.Common" Version="2.11.0" />
3938
<PackageReference Include="Keyfactor.Extensions.Aws.Auth" Version="0.5.1" />
4039
<PackageReference Include="Keyfactor.Logging" Version="1.3.0" />
4140
</ItemGroup>

aws-pca-caplugin/Client/ACMPCAClient.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public sealed class AwsPcaClient : IAwsPcaClient
3939
private const string ENHANCED_KEY_USAGE_OID = "2.5.29.37";
4040
private const string SERVER_AUTH_OID = "1.3.6.1.5.5.7.3.1";
4141
private const string CLIENT_AUTH_OID = "1.3.6.1.5.5.7.3.2";
42+
private const string CODE_SIGNING_OID = "1.3.6.1.5.5.7.3.3";
4243

4344
private readonly SemaphoreSlim _caInfoLock = new(1, 1);
4445
private readonly AWSCredentials AwsCredentials;
@@ -60,7 +61,6 @@ public AwsPcaClient(IAnyCAPluginConfigProvider configProvider)
6061
throw new ArgumentNullException(nameof(configProvider),
6162
"Config provider and CAConnectionData are required.");
6263

63-
6464
var enabled = bool.Parse(GetRequiredString(configProvider, "Enabled"));
6565
if (!enabled)
6666
{
@@ -145,12 +145,29 @@ public async Task<IssueCertificateResponse> SubmitIssueCertificateAsync(
145145
if (signingAlgoRes.Error != null)
146146
return new IssueCertificateResponse { RegistrationError = signingAlgoRes.Error };
147147

148+
// Map the requested ProductId to its AWS PCA template ARN. Without this,
149+
// PCA falls back to EndEntityCertificate/V1 and every cert gets the default
150+
// Server+Client Auth EKU combination regardless of the selected product.
151+
if (string.IsNullOrWhiteSpace(request.ProductId) ||
152+
!Constants.TemplateARNs.TryGetValue(request.ProductId, out var templateArn))
153+
return new IssueCertificateResponse
154+
{
155+
RegistrationError = new RegistrationError
156+
{
157+
Description = string.IsNullOrWhiteSpace(request.ProductId)
158+
? "ProductId is required to resolve the AWS PCA template ARN."
159+
: $"Unsupported ProductId '{request.ProductId}'. Supported: {string.Join(", ", Constants.TemplateARNs.Keys)}",
160+
ErrorCode = "InvalidRequest"
161+
}
162+
};
163+
148164
var signingAlgorithm = signingAlgoRes.Value!;
149165
var issueReq = new Amazon.ACMPCA.Model.IssueCertificateRequest
150166
{
151167
CertificateAuthorityArn = CaArn,
152168
Csr = new MemoryStream(Encoding.ASCII.GetBytes(csrBytes)),
153169
SigningAlgorithm = signingAlgorithm,
170+
TemplateArn = templateArn,
154171
IdempotencyToken = request.IdempotencyToken ?? Guid.NewGuid().ToString("N"),
155172
Validity = new Validity
156173
{
@@ -554,14 +571,14 @@ private static string Wrap64(string b64)
554571

555572
/// <summary>
556573
/// Infers one of the supported template type keys:
557-
/// EndEntity, EndEntityClientAuth, EndEntityServerAuth.
574+
/// EndEntity, EndEntityClientAuth, EndEntityServerAuth, CodeSigning.
558575
/// Returns "Unknown" if certificate parsing/inspection fails.
559576
/// </summary>
560577
public static string InferTemplateTypeKey(X509Certificate2 cert)
561578
{
562579
try
563580
{
564-
bool server = false, client = false;
581+
bool server = false, client = false, codeSigning = false;
565582

566583
// Find EKU extension (do not rely on indexer throwing)
567584
var eku = cert.Extensions
@@ -574,8 +591,11 @@ public static string InferTemplateTypeKey(X509Certificate2 cert)
574591
server = true;
575592
else if (string.Equals(usage.Value, CLIENT_AUTH_OID, StringComparison.Ordinal))
576593
client = true;
594+
else if (string.Equals(usage.Value, CODE_SIGNING_OID, StringComparison.Ordinal))
595+
codeSigning = true;
577596

578597
// Map to your known keys
598+
if (codeSigning && !server && !client) return "CodeSigning";
579599
if (server && !client) return "EndEntityServerAuth";
580600
if (client && !server) return "EndEntityClientAuth";
581601

@@ -761,4 +781,4 @@ private static class ConfigKeys
761781
public const string Region = "Region";
762782
public const string S3Bucket = "S3Bucket";
763783
}
764-
}
784+
}

aws-pca-caplugin/Constants.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ public static class Constants
2222
{
2323
"EndEntityServerAuth",
2424
"arn:aws:acm-pca:::template/EndEntityServerAuthCertificate/V1"
25+
},
26+
{
27+
"CodeSigning",
28+
"arn:aws:acm-pca:::template/CodeSigningCertificate/V1"
2529
}
2630
};
2731

docsource/configuration.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ Download the **PCA root certificate** from AWS and have it ready to import into
1313
The CAPlugin currently supports **one** authentication method: **AWS Access Key ID + Secret Access Key**.
1414
**OAuth** and **Default SDK authentication** will be enabled in later updates. There is functionality present via the **Keyfactor AWS Authentication** library, but these alternate methods are currently ***untested***.
1515

16-
### Known Issues
17-
18-
At present, a fresh install of Keyfactor Command 24.4 used in conjuction with Keyfactor Gateway REST 25.4.0.0 is confirmed as working. A fresh install of Command 25.3 used with REST 25.4.0.0 is also confirmed as working.
19-
Latest version of Command 25.4 may run into issues, investigation into compatibility issues is ongoing.
20-
2116
### What you need ready
2217

2318
Before configuring the CAPlugin, have the following prepared:

integration-manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"product_ids": [
9696
"EndEntity",
9797
"EndEntityClientAuth",
98-
"EndEntityServerAuth"
98+
"EndEntityServerAuth",
99+
"CodeSigning"
99100
]
100101
}
101102
}

0 commit comments

Comments
 (0)