1313using Org . BouncyCastle . Asn1 . Pkcs ;
1414using Org . BouncyCastle . Asn1 . X509 ;
1515using Org . BouncyCastle . Pkcs ;
16+ using System . Security . Cryptography ;
1617using System ;
1718using System . Collections . Generic ;
1819using System . Linq ;
@@ -301,6 +302,9 @@ public async Task<EnrollmentResult> Enroll(
301302 _logger . LogInformation ( "Order created. OrderUrl: {OrderUrl}, Status: {Status}" ,
302303 order . OrderUrl , order . Payload ? . Status ) ;
303304
305+ // Extract order identifier BEFORE finalization to ensure we use the original order URL
306+ var orderIdentifier = ExtractOrderIdentifier ( order . OrderUrl ) ;
307+
304308 // Store pending order immediately
305309 var accountId = accountDetails . Kid . Split ( '/' ) . Last ( ) ;
306310
@@ -310,9 +314,6 @@ public async Task<EnrollmentResult> Enroll(
310314 // Finalize with original CSR bytes
311315 order = await acmeClient . FinalizeOrderAsync ( order , csrBytes ) ;
312316
313- // Extract order identifier (path only) for database storage
314- var orderIdentifier = ExtractOrderIdentifier ( order . OrderUrl ) ;
315-
316317 // If order is valid immediately, download cert
317318 if ( order . Payload ? . Status == "valid" && ! string . IsNullOrEmpty ( order . Payload . Certificate ) )
318319 {
@@ -361,30 +362,25 @@ public async Task<EnrollmentResult> Enroll(
361362
362363
363364 /// <summary>
364- /// Extracts the order path from the full ACME order URL for use as a unique identifier.
365- /// This removes the scheme, host, and port, keeping only the path portion.
365+ /// Generates a fixed-length SHA256 hash of the ACME order URL for database storage.
366+ /// Produces a consistent 40-char hex string regardless of URL length or ACME CA format.
367+ /// The full order URL is logged separately during enrollment for traceability.
366368 /// </summary>
367- /// <param name="orderUrl">Full order URL (e.g., https://dv.acme-v02.api.pki.goog/order/ABC123)</param>
368- /// <returns>Order path without leading slash (e.g., "order/ABC123")</returns>
369- /// <example>
370- /// Input: "https://dv.acme-v02.api.pki.goog/order/IlYl06mPl5VcAQpx3pzR6w"
371- /// Output: "order/IlYl06mPl5VcAQpx3pzR6w"
372- /// </example>
373369 private static string ExtractOrderIdentifier ( string orderUrl )
374370 {
375371 if ( string . IsNullOrWhiteSpace ( orderUrl ) )
376372 return orderUrl ;
377373
378- try
374+ using ( var sha256 = SHA256 . Create ( ) )
379375 {
380- var uri = new Uri ( orderUrl ) ;
381- // Remove leading slash and return the path
382- return uri . AbsolutePath . TrimStart ( '/' ) ;
383- }
384- catch ( Exception )
385- {
386- // If URL parsing fails, return the original (shouldn't happen with valid ACME URLs)
387- return orderUrl ;
376+ var hashBytes = sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( orderUrl ) ) ;
377+ // Take first 20 bytes (40 hex chars) — fits in DB column and is collision-safe
378+ var sb = new StringBuilder ( 40 ) ;
379+ for ( int i = 0 ; i < 20 ; i ++ )
380+ {
381+ sb . Append ( hashBytes [ i ] . ToString ( "x2" ) ) ;
382+ }
383+ return sb . ToString ( ) ;
388384 }
389385 }
390386
0 commit comments