2222using Org . BouncyCastle . Asn1 . Pkcs ;
2323using Org . BouncyCastle . Asn1 . X509 ;
2424using Org . BouncyCastle . Pkcs ;
25+ using System . Security . Cryptography ;
2526
2627namespace Keyfactor . Extensions . CAPlugin . Acme
2728{
@@ -62,6 +63,7 @@ public class AcmeCaPlugin : IAnyCAPlugin
6263 {
6364 private static readonly ILogger _logger = LogHandler . GetClassLogger < AcmeCaPlugin > ( ) ;
6465 private IAnyCAPluginConfigProvider Config { get ; set ; }
66+ private AcmeClientConfig _config ;
6567
6668 // Constants for better maintainability
6769 private const string DEFAULT_PRODUCT_ID = "default" ;
@@ -76,6 +78,16 @@ public void Initialize(IAnyCAPluginConfigProvider configProvider, ICertificateDa
7678 {
7779 _logger . MethodEntry ( ) ;
7880 Config = configProvider ?? throw new ArgumentNullException ( nameof ( configProvider ) ) ;
81+ _config = GetConfig ( ) ;
82+ _logger . LogTrace ( "Enabled: {Enabled}" , _config . Enabled ) ;
83+
84+ if ( ! _config . Enabled )
85+ {
86+ _logger . LogWarning ( "The CA is currently in the Disabled state. It must be Enabled to perform operations. Skipping config validation..." ) ;
87+ _logger . MethodExit ( ) ;
88+ return ;
89+ }
90+
7991 _logger . MethodExit ( ) ;
8092 }
8193
@@ -88,6 +100,12 @@ public void Initialize(IAnyCAPluginConfigProvider configProvider, ICertificateDa
88100 public async Task Ping ( )
89101 {
90102 _logger . MethodEntry ( ) ;
103+ if ( ! _config . Enabled )
104+ {
105+ _logger . LogWarning ( "The CA is currently in the Disabled state. It must be Enabled to perform operations. Skipping connectivity test..." ) ;
106+ _logger . MethodExit ( ) ;
107+ return ;
108+ }
91109
92110 HttpClient httpClient = null ;
93111 try
@@ -165,6 +183,13 @@ public Task ValidateCAConnectionInfo(Dictionary<string, object> connectionInfo)
165183 var rawData = JsonConvert . SerializeObject ( connectionInfo ) ;
166184 var config = JsonConvert . DeserializeObject < AcmeClientConfig > ( rawData ) ;
167185
186+ if ( config != null && ! config . Enabled )
187+ {
188+ _logger . LogWarning ( "The CA is currently in the Disabled state. It must be Enabled to perform operations. Skipping config validation..." ) ;
189+ _logger . MethodExit ( ) ;
190+ return Task . CompletedTask ;
191+ }
192+
168193 // Validate required configuration fields
169194 var missingFields = new List < string > ( ) ;
170195 if ( string . IsNullOrWhiteSpace ( config ? . DirectoryUrl ) )
@@ -230,6 +255,17 @@ public async Task<EnrollmentResult> Enroll(
230255 {
231256 _logger . MethodEntry ( ) ;
232257
258+ if ( ! _config . Enabled )
259+ {
260+ _logger . LogWarning ( "The CA is currently in the Disabled state. It must be Enabled to perform operations. Enrollment rejected." ) ;
261+ _logger . MethodExit ( ) ;
262+ return new EnrollmentResult
263+ {
264+ Status = ( int ) EndEntityStatus . FAILED ,
265+ StatusMessage = "CA connector is disabled. Enable it in the CA configuration to perform enrollments."
266+ } ;
267+ }
268+
233269 if ( string . IsNullOrWhiteSpace ( csr ) )
234270 throw new ArgumentException ( "CSR cannot be null or empty" , nameof ( csr ) ) ;
235271 if ( string . IsNullOrWhiteSpace ( subject ) )
@@ -262,6 +298,12 @@ public async Task<EnrollmentResult> Enroll(
262298 // Create order
263299 var order = await acmeClient . CreateOrderAsync ( identifiers , null ) ;
264300
301+ _logger . LogInformation ( "Order created. OrderUrl: {OrderUrl}, Status: {Status}" ,
302+ order . OrderUrl , order . Payload ? . Status ) ;
303+
304+ // Extract order identifier BEFORE finalization to ensure we use the original order URL
305+ var orderIdentifier = ExtractOrderIdentifier ( order . OrderUrl ) ;
306+
265307 // Store pending order immediately
266308 var accountId = accountDetails . Kid . Split ( '/' ) . Last ( ) ;
267309
@@ -277,20 +319,24 @@ public async Task<EnrollmentResult> Enroll(
277319 var certBytes = await acmeClient . GetCertificateAsync ( order ) ;
278320 var certPem = EncodeToPem ( certBytes , "CERTIFICATE" ) ;
279321
322+ _logger . LogInformation ( "✅ Enrollment completed successfully. OrderUrl: {OrderUrl}, CARequestID: {OrderId}, Status: GENERATED" ,
323+ order . OrderUrl , orderIdentifier ) ;
324+
280325 return new EnrollmentResult
281326 {
282- CARequestID = order . Payload . Finalize ,
327+ CARequestID = orderIdentifier ,
283328 Certificate = certPem ,
284329 Status = ( int ) EndEntityStatus . GENERATED
285330 } ;
286331 }
287332 else
288333 {
289- _logger . LogInformation ( "⏳ Order not valid yet — will be synced later. Status: {Status}" , order . Payload ? . Status ) ;
334+ _logger . LogInformation ( "⏳ Order not valid yet — will be synced later. OrderUrl: {OrderUrl}, CARequestID: {OrderId}, Status: {Status}" ,
335+ order . OrderUrl , orderIdentifier , order . Payload ? . Status ) ;
290336 // Order stays saved for next sync
291337 return new EnrollmentResult
292338 {
293- CARequestID = order . Payload . Finalize ,
339+ CARequestID = orderIdentifier ,
294340 Status = ( int ) EndEntityStatus . FAILED ,
295341 StatusMessage = "Could not retrieve order in allowed time."
296342 } ;
@@ -314,6 +360,29 @@ public async Task<EnrollmentResult> Enroll(
314360
315361
316362
363+ /// <summary>
364+ /// Generates a fixed-length SHA256 hash of the ACME order URL for database storage.
365+ /// Produces a consistent 40-char hex string regardless of URL length or ACME CA format.
366+ /// The full order URL is logged separately during enrollment for traceability.
367+ /// </summary>
368+ private static string ExtractOrderIdentifier ( string orderUrl )
369+ {
370+ if ( string . IsNullOrWhiteSpace ( orderUrl ) )
371+ return orderUrl ;
372+
373+ using ( var sha256 = SHA256 . Create ( ) )
374+ {
375+ var hashBytes = sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( orderUrl ) ) ;
376+ // Take first 20 bytes (40 hex chars) — fits in DB column and is collision-safe
377+ var sb = new StringBuilder ( 40 ) ;
378+ for ( int i = 0 ; i < 20 ; i ++ )
379+ {
380+ sb . Append ( hashBytes [ i ] . ToString ( "x2" ) ) ;
381+ }
382+ return sb . ToString ( ) ;
383+ }
384+ }
385+
317386 /// <summary>
318387 /// Extracts the domain name from X.509 subject string
319388 /// </summary>
0 commit comments