Skip to content
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
3 changes: 3 additions & 0 deletions SslStoreCaProxy.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Solution>
<Project Path="SslStoreCaProxy/SslStoreCaProxy.csproj" />
</Solution>
20 changes: 19 additions & 1 deletion SslStoreCaProxy/RequestManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@
};
}

public DownloadCertificateRequest GetCertificateRequestBySslStoreId(string theSslStoreOrderId)
{
return new DownloadCertificateRequest
{
AuthRequest = GetAuthRequest(),
TheSslStoreOrderId = theSslStoreOrderId
};
}

public RevokeOrderRequest GetRevokeOrderRequest(string customOrderId)
{
return new RevokeOrderRequest
Expand All @@ -151,6 +160,15 @@
};
}

public RevokeOrderRequest GetRevokeOrderRequestBySslStoreId(string theSslStoreOrderId)
{
return new RevokeOrderRequest
{
AuthRequest = GetAuthRequest(),
TheSslStoreOrderId = theSslStoreOrderId
};
}

public int GetClientPageSize(IAnyCAPluginConfigProvider config)
{
if (config.CAConnectionData.ContainsKey(Constants.PageSize))
Expand Down Expand Up @@ -198,7 +216,7 @@
case "Cancelled":
return (int)EndEntityStatus.REVOKED;
default:
return (int)EndEntityStatus.FAILED;
return (int)EndEntityStatus.NEW;
}
}

Expand Down Expand Up @@ -334,7 +352,7 @@
{
foreach (var c in certificates)
{
var cert = new X509Certificate2(Encoding.UTF8.GetBytes(c.FileContent));

Check warning on line 355 in SslStoreCaProxy/RequestManager.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

'X509Certificate2.X509Certificate2(byte[])' is obsolete: 'Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.' (https://aka.ms/dotnet-warnings/SYSLIB0057)

Check warning on line 355 in SslStoreCaProxy/RequestManager.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

'X509Certificate2.X509Certificate2(byte[])' is obsolete: 'Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.' (https://aka.ms/dotnet-warnings/SYSLIB0057)

Check warning on line 355 in SslStoreCaProxy/RequestManager.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

'X509Certificate2.X509Certificate2(byte[])' is obsolete: 'Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.' (https://aka.ms/dotnet-warnings/SYSLIB0057)

Check warning on line 355 in SslStoreCaProxy/RequestManager.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

'X509Certificate2.X509Certificate2(byte[])' is obsolete: 'Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.' (https://aka.ms/dotnet-warnings/SYSLIB0057)

Check warning on line 355 in SslStoreCaProxy/RequestManager.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

'X509Certificate2.X509Certificate2(byte[])' is obsolete: 'Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.' (https://aka.ms/dotnet-warnings/SYSLIB0057)

Check warning on line 355 in SslStoreCaProxy/RequestManager.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

'X509Certificate2.X509Certificate2(byte[])' is obsolete: 'Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.' (https://aka.ms/dotnet-warnings/SYSLIB0057)
if (cert.SubjectName.Name != null && cert.SubjectName.Name.Contains(commonName)) return c.FileContent;
}

Expand Down
95 changes: 79 additions & 16 deletions SslStoreCaProxy/SslStoreCaProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,16 @@ public Dictionary<string, PropertyConfigInfo> GetTemplateParameterAnnotations()
public async Task<int> Revoke(string caRequestId, string hexSerialNumber, uint revocationReason)
{
_logger.MethodEntry();
var revokeOrderRequest = _requestManager.GetRevokeOrderRequest(caRequestId);
var sslStoreOrderId = ParseSslStoreOrderId(caRequestId);
RevokeOrderRequest revokeOrderRequest;
if (sslStoreOrderId != null)
{
revokeOrderRequest = _requestManager.GetRevokeOrderRequestBySslStoreId(sslStoreOrderId);
}
else
{
revokeOrderRequest = _requestManager.GetRevokeOrderRequest(caRequestId);
}
_logger.LogTrace($"Revoke Request JSON {JsonConvert.SerializeObject(revokeOrderRequest)}");
try
{
Expand Down Expand Up @@ -240,9 +249,20 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
_logger.LogTrace($"Prior Cert Serial Number: {sn}");

var caRequestId = await _certDataReader.GetRequestIDBySerialNumber(sn);
_logger.LogTrace($"Prior CA Request ID (CustomOrderId): {caRequestId}");
_logger.LogTrace($"Prior CA Request ID: {caRequestId}");

var orderStatusRequest = _requestManager.GetOrderStatusRequest(caRequestId);
var priorSslStoreOrderId = ParseSslStoreOrderId(caRequestId);
OrderStatusRequest orderStatusRequest;
if (priorSslStoreOrderId != null)
{
_logger.LogTrace($"Parsed TheSSLStoreOrderID: {priorSslStoreOrderId}");
orderStatusRequest = _requestManager.GetOrderStatusRequestBySslStoreId(priorSslStoreOrderId);
}
else
{
_logger.LogTrace($"Legacy GUID format, querying by CustomOrderId: {caRequestId}");
orderStatusRequest = _requestManager.GetOrderStatusRequest(caRequestId);
}
_logger.LogTrace($"orderStatusRequest JSON {JsonConvert.SerializeObject(orderStatusRequest)}");

var orderStatusResponse = await client.SubmitOrderStatusRequestAsync(orderStatusRequest);
Expand Down Expand Up @@ -290,6 +310,36 @@ public async Task<EnrollmentResult> Enroll(string csr, string subject, Dictionar
}
}

/// <summary>
/// Builds a composite CARequestID in the format "{TheSSLStoreOrderID}-{PartnerOrderID}".
/// This ensures uniqueness across reissues (same order, different PartnerOrderID).
/// </summary>
private static string BuildCompositeRequestId(string theSslStoreOrderId, string partnerOrderId)
{
return $"{theSslStoreOrderId}-{partnerOrderId}";
}

/// <summary>
/// Parses the TheSSLStoreOrderID from a CARequestID. Supports both composite format
/// ("{TheSSLStoreOrderID}-{PartnerOrderID}") and legacy GUID format (falls back to
/// treating the whole string as a CustomOrderId for backward compatibility).
/// </summary>
private static string ParseSslStoreOrderId(string caRequestId)
{
if (string.IsNullOrEmpty(caRequestId)) return caRequestId;

var dashIndex = caRequestId.IndexOf('-');
// Composite IDs have a numeric TheSSLStoreOrderID before the first dash.
// Legacy GUIDs have hex chars before the first dash so we check for digits only.
if (dashIndex > 0 && caRequestId.Substring(0, dashIndex).All(char.IsDigit))
{
return caRequestId.Substring(0, dashIndex);
}

// Legacy GUID format — return as-is for backward compatibility
return null;
}

private EnrollmentResult GetEnrollmentResult(INewOrderResponse newOrderResponse)
{
if (newOrderResponse != null && newOrderResponse.AuthResponse.IsError)
Expand All @@ -304,16 +354,16 @@ private EnrollmentResult GetEnrollmentResult(INewOrderResponse newOrderResponse)

var majorStatus = newOrderResponse?.OrderStatus?.MajorStatus;
var status = _requestManager.MapReturnStatus(majorStatus);
var orderId = newOrderResponse?.CustomOrderId;
var compositeId = BuildCompositeRequestId(newOrderResponse?.TheSslStoreOrderId, newOrderResponse?.PartnerOrderId);

_logger.LogTrace($"Order {orderId} status: {majorStatus} -> mapped to {status}");
_logger.LogTrace($"Order {compositeId} (SSLStoreOrderId: {newOrderResponse?.TheSslStoreOrderId}, PartnerOrderId: {newOrderResponse?.PartnerOrderId}) status: {majorStatus} -> mapped to {status}");
_logger.MethodExit();

return new EnrollmentResult
{
CARequestID = orderId,
CARequestID = compositeId,
Status = status,
StatusMessage = $"Order Successfully Created With Order Number {orderId}"
StatusMessage = $"Order Successfully Created With Order Number {compositeId}"
};
}

Expand All @@ -322,8 +372,19 @@ public async Task<AnyCAPluginCertificate> GetSingleRecord(string caRequestId)
_logger.MethodEntry();

var client = new SslStoreClient(Config);
var orderStatusRequest = _requestManager.GetOrderStatusRequest(caRequestId);
_logger.LogTrace($"orderStatusRequest JSON {JsonConvert.SerializeObject(orderStatusRequest)}");
var sslStoreOrderId = ParseSslStoreOrderId(caRequestId);

OrderStatusRequest orderStatusRequest;
if (sslStoreOrderId != null)
{
_logger.LogTrace($"Parsed TheSSLStoreOrderID: {sslStoreOrderId} from CARequestID: {caRequestId}");
orderStatusRequest = _requestManager.GetOrderStatusRequestBySslStoreId(sslStoreOrderId);
}
else
{
_logger.LogTrace($"Legacy GUID format, querying by CustomOrderId: {caRequestId}");
orderStatusRequest = _requestManager.GetOrderStatusRequest(caRequestId);
}

var orderStatusResponse = await client.SubmitOrderStatusRequestAsync(orderStatusRequest);
_logger.LogTrace($"orderStatusResponse JSON {JsonConvert.SerializeObject(orderStatusResponse)}");
Expand All @@ -333,7 +394,7 @@ public async Task<AnyCAPluginCertificate> GetSingleRecord(string caRequestId)

if (certStatus == (int)EndEntityStatus.GENERATED)
{
var downloadCertificateRequest = _requestManager.GetCertificateRequest(caRequestId);
var downloadCertificateRequest = _requestManager.GetCertificateRequestBySslStoreId(sslStoreOrderId ?? orderStatusResponse.TheSslStoreOrderId);
var certResponse = await client.SubmitDownloadCertificateAsync(downloadCertificateRequest);
if (!certResponse.AuthResponse.IsError)
{
Expand Down Expand Up @@ -379,19 +440,21 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
var orderStatusRequest = _requestManager.GetOrderStatusRequestBySslStoreId(currentResponseItem?.TheSslStoreOrderId);
var orderStatusResponse = await client.SubmitOrderStatusRequestAsync(orderStatusRequest);

var customOrderId = orderStatusResponse.CustomOrderId;
if (string.IsNullOrEmpty(customOrderId))
var theSslStoreOrderId = orderStatusResponse.TheSslStoreOrderId;
var partnerOrderId = orderStatusResponse.PartnerOrderId;
if (string.IsNullOrEmpty(theSslStoreOrderId) || string.IsNullOrEmpty(partnerOrderId))
{
_logger.LogTrace($"Order {currentResponseItem?.TheSslStoreOrderId} has no CustomOrderId, skipping");
_logger.LogTrace($"Order {currentResponseItem?.TheSslStoreOrderId} missing required IDs, skipping");
continue;
}

var compositeId = BuildCompositeRequestId(theSslStoreOrderId, partnerOrderId);
var fileContent = "";
var certStatus = _requestManager.MapReturnStatus(orderStatusResponse.OrderStatus.MajorStatus);

if (certStatus == (int)EndEntityStatus.GENERATED)
{
var downloadCertificateRequest = _requestManager.GetCertificateRequest(customOrderId);
var downloadCertificateRequest = _requestManager.GetCertificateRequestBySslStoreId(theSslStoreOrderId);
var certResponse = await client.SubmitDownloadCertificateAsync(downloadCertificateRequest);
if (!certResponse.AuthResponse.IsError)
{
Expand All @@ -400,13 +463,13 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
fileContent = Convert.ToBase64String(endEntityCert.RawData);
}
}

if ((certStatus == (int)EndEntityStatus.GENERATED && fileContent.Length > 0) ||
certStatus == (int)EndEntityStatus.REVOKED)
{
blockingBuffer.Add(new AnyCAPluginCertificate
{
CARequestID = customOrderId,
CARequestID = compositeId,
Certificate = fileContent,
Status = certStatus,
ProductID = $"{orderStatusResponse.ProductCode}"
Expand Down
4 changes: 2 additions & 2 deletions integration-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"status": "production",
"integration_type": "anyca-plugin",
"support_level": "kf-supported",
"link_github": true,
"update_catalog": true,
"link_github": false,
"update_catalog": false,
"gateway_framework": "25.5",
"about": {
"carest": {
Expand Down
Loading