@@ -87,10 +87,12 @@ public async Task<AnyCAPluginCertificate> GetSingleRecord(string caRequestID)
8787 throw ;
8888 }
8989 }
90-
91- //done
92- public async Task Synchronize ( BlockingCollection < AnyCAPluginCertificate > blockingBuffer , DateTime ? lastSync ,
93- bool fullSync , CancellationToken cancelToken )
90+ // done
91+ public async Task Synchronize (
92+ BlockingCollection < AnyCAPluginCertificate > blockingBuffer ,
93+ DateTime ? lastSync ,
94+ bool fullSync ,
95+ CancellationToken cancelToken )
9496 {
9597 Logger . MethodEntry ( ) ;
9698 Logger . LogTrace (
@@ -114,6 +116,7 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
114116 var caRequestId = SafeRequestIdFromArn ( audit . certificateArn ) ;
115117
116118 if ( string . IsNullOrWhiteSpace ( caRequestId ) && ! string . IsNullOrWhiteSpace ( audit . certificateSerial ) )
119+ {
117120 try
118121 {
119122 caRequestId = await _certificateDataReader
@@ -125,6 +128,7 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
125128 Logger . LogTrace (
126129 $ "Could not map serial to requestId. serial={ audit . certificateSerial } . { ex . Message } ") ;
127130 }
131+ }
128132
129133 if ( string . IsNullOrWhiteSpace ( caRequestId ) )
130134 {
@@ -160,7 +164,8 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
160164 {
161165 var exp = _certificateDataReader . GetExpirationDateByRequestId ( caRequestId ) ;
162166 if ( exp . HasValue && exp . Value . ToUniversalTime ( ) <= DateTime . UtcNow &&
163- newStatus == ( int ) EndEntityStatus . GENERATED ) newStatus = ( int ) EndEntityStatus . HISTORICAL ;
167+ newStatus == ( int ) EndEntityStatus . GENERATED )
168+ newStatus = ( int ) EndEntityStatus . HISTORICAL ;
164169 }
165170 catch ( Exception ex )
166171 {
@@ -184,6 +189,7 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
184189 }
185190
186191 if ( exists )
192+ {
187193 try
188194 {
189195 var oldStatus = await _certificateDataReader . GetStatusByRequestID ( caRequestId )
@@ -200,65 +206,92 @@ public async Task Synchronize(BlockingCollection<AnyCAPluginCertificate> blockin
200206 Logger . LogWarning (
201207 $ "GetStatusByRequestID failed for { caRequestId } : { ex . Message } . Proceeding to emit.") ;
202208 }
209+ }
203210 }
204211
205- // If we only need to update status (revoked/historical) and you don't want to fetch the cert again:
206- // - For revoked/historical, Keyfactor usually still accepts just status update, but depending on your pipeline,
207- // you may want to always include Certificate for GENERATED.
208- // Here: fetch cert for GENERATED/HISTORICAL; skip fetch for REVOKED if ARN missing.
209- string ? certB64 = null ;
212+ // Fetch certificate material when needed:
213+ // - GENERATED/HISTORICAL: must fetch (otherwise Keyfactor can't ingest new cert)
214+ // - REVOKED: best-effort fetch if ARN exists (so revoked updates can include PEM); otherwise emit status-only
215+ string ? certPayload = null ;
210216 string ? productId = null ;
211217
212- if ( newStatus == ( int ) EndEntityStatus . GENERATED || newStatus == ( int ) EndEntityStatus . HISTORICAL )
213- {
214- if ( string . IsNullOrWhiteSpace ( audit . certificateArn ) )
215- {
216- Logger . LogTrace ( $ "Skipping { caRequestId } : no certificateArn to retrieve certificate.") ;
217- continue ;
218- }
218+ bool mustFetchForIngest =
219+ newStatus == ( int ) EndEntityStatus . GENERATED ||
220+ newStatus == ( int ) EndEntityStatus . HISTORICAL ;
219221
220- var certResp = await AwsClient . SubmitGetCertificateByArnAsync ( audit . certificateArn , cancelToken )
221- . ConfigureAwait ( false ) ;
222+ bool bestEffortFetch =
223+ newStatus == ( int ) EndEntityStatus . REVOKED ;
222224
223- if ( certResp ? . Status == ( int ) EndEntityStatus . INPROCESS )
225+ bool shouldAttemptFetch = mustFetchForIngest || bestEffortFetch ;
226+
227+ if ( shouldAttemptFetch )
228+ {
229+ if ( string . IsNullOrWhiteSpace ( audit . certificateArn ) )
224230 {
225- // Emit in-process update without cert payload
226- blockingBuffer . Add ( new AnyCAPluginCertificate
231+ if ( mustFetchForIngest )
227232 {
228- CARequestID = caRequestId ,
229- Status = ( int ) EndEntityStatus . INPROCESS ,
230- ProductID = certResp . CertificateType
231- } , cancelToken ) ;
233+ Logger . LogTrace ( $ "Skipping { caRequestId } : no certificateArn to retrieve certificate.") ;
234+ continue ; // can't ingest a new/active cert without payload
235+ }
232236
233- continue ;
237+ // REVOKED (best-effort): status-only update
238+ Logger . LogTrace ( $ "Revoked status-only for { caRequestId } : no certificateArn present.") ;
234239 }
235-
236- if ( certResp ? . RegistrationError != null )
240+ else
237241 {
238- Logger . LogTrace (
239- $ "Skipping { caRequestId } : GetCertificate error: { certResp . RegistrationError . Description } ") ;
240- continue ;
241- }
242+ var certResp = await AwsClient
243+ . SubmitGetCertificateByArnAsync ( audit . certificateArn , cancelToken )
244+ . ConfigureAwait ( false ) ;
242245
243- certB64 = certResp . Certificate ;
244- productId = certResp . CertificateType ;
246+ if ( certResp ? . Status == ( int ) EndEntityStatus . INPROCESS )
247+ {
248+ // Emit in-process update without cert payload
249+ blockingBuffer . Add ( new AnyCAPluginCertificate
250+ {
251+ CARequestID = caRequestId ,
252+ Status = ( int ) EndEntityStatus . INPROCESS ,
253+ ProductID = certResp . CertificateType
254+ } , cancelToken ) ;
245255
256+ continue ;
257+ }
246258
247- if ( string . IsNullOrWhiteSpace ( certB64 ) )
248- {
249- Logger . LogTrace ( $ "Skipping { caRequestId } : unable to obtain end-entity certificate payload.") ;
250- continue ;
259+ if ( certResp ? . RegistrationError != null )
260+ {
261+ if ( mustFetchForIngest )
262+ {
263+ Logger . LogTrace (
264+ $ "Skipping { caRequestId } : GetCertificate error: { certResp . RegistrationError . Description } ") ;
265+ continue ;
266+ }
267+
268+ // REVOKED best-effort: proceed status-only
269+ Logger . LogTrace (
270+ $ "Revoked status-only for { caRequestId } : GetCertificate error: { certResp . RegistrationError . Description } ") ;
271+ }
272+ else
273+ {
274+ certPayload = certResp ? . Certificate ;
275+ productId = certResp ? . CertificateType ;
276+
277+ if ( mustFetchForIngest && string . IsNullOrWhiteSpace ( certPayload ) )
278+ {
279+ Logger . LogTrace ( $ "Skipping { caRequestId } : unable to obtain end-entity certificate payload.") ;
280+ continue ;
281+ }
282+ }
251283 }
252284 }
253285
254286 var finalsubmit = new AnyCAPluginCertificate
255287 {
256288 CARequestID = caRequestId ,
257- Certificate =
258- GetEndEntityCertificate ( certB64 ) , // null is OK for REVOKED updates if your pipeline accepts it
289+ // For REVOKED: this will now be populated when ARN exists + GetCertificate succeeds; otherwise null (status-only).
290+ Certificate = GetEndEntityCertificate ( certPayload ) ,
259291 Status = newStatus ,
260292 ProductID = productId
261293 } ;
294+
262295 // Emit to buffer as AnyGateway expects.
263296 blockingBuffer . Add ( finalsubmit , cancelToken ) ;
264297 }
@@ -382,60 +415,60 @@ public async Task<EnrollmentResult> Enroll(
382415 switch ( enrollmentType )
383416 {
384417 case EnrollmentType . New :
385- {
386- return await IssueAndFetchAsync (
387- csr ,
388- productInfo . ProductID ,
389- days ,
390- signingAlgorithm ,
391- "Certificate Issued" )
392- . ConfigureAwait ( false ) ;
393- }
394-
395- case EnrollmentType . RenewOrReissue :
396- {
397- if ( productInfo . ProductParameters == null ||
398- ! TryGetProductParam ( productInfo . ProductParameters , "PriorCertSN" , out var priorSn ) ||
399- string . IsNullOrWhiteSpace ( priorSn ) )
400- return new EnrollmentResult
401- {
402- Status = ( int ) EndEntityStatus . FAILED ,
403- StatusMessage =
404- "Renew/Reissue requires ProductParameters['PriorCertSN'] (hex serial number)."
405- } ;
406-
407- string priorRequestId ;
408- try
409418 {
410- priorRequestId = await _certificateDataReader
411- . GetRequestIDBySerialNumber ( priorSn )
419+ return await IssueAndFetchAsync (
420+ csr ,
421+ productInfo . ProductID ,
422+ days ,
423+ signingAlgorithm ,
424+ "Certificate Issued" )
412425 . ConfigureAwait ( false ) ;
413426 }
414- catch ( Exception ex )
427+
428+ case EnrollmentType . RenewOrReissue :
415429 {
416- return new EnrollmentResult
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
417442 {
418- Status = ( int ) EndEntityStatus . FAILED ,
419- StatusMessage = $ "Could not resolve PriorCertSN to request id: { ex . Message } "
420- } ;
421- }
443+ priorRequestId = await _certificateDataReader
444+ . GetRequestIDBySerialNumber ( priorSn )
445+ . ConfigureAwait ( false ) ;
446+ }
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+ }
422455
423- var expiration = _certificateDataReader . GetExpirationDateByRequestId ( priorRequestId ) ;
424- var isRenewal = expiration . HasValue && expiration . Value . ToUniversalTime ( ) <= DateTime . UtcNow ;
425-
426- var msg = isRenewal ? "Certificate Renewed" : "Certificate Reissued" ;
427- var token = BuildIdempotencyToken ( isRenewal ? "renew" : "reissue" , priorRequestId , csr ) ;
428-
429- // Still "IssueCertificate" under the hood; PCA doesn't have first-class renew/reissue.
430- return await IssueAndFetchAsync (
431- csr ,
432- productInfo . ProductID ,
433- days ,
434- msg ,
435- // Optional: stable-ish idempotency (helps avoid duplicates if caller retries quickly)
436- token )
437- . ConfigureAwait ( false ) ;
438- }
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+ }
439472
440473 default :
441474 return new EnrollmentResult
@@ -652,7 +685,7 @@ public Dictionary<string, PropertyConfigInfo> GetCAConnectorAnnotations()
652685 DefaultValue = "" ,
653686 Type = "String"
654687 } ,
655- [ Constants . Enabled ] = new ( )
688+ [ Constants . Enabled ] = new ( )
656689 {
657690 Comments = "Flag to Enable or Disable gateway functionality. Disabling is primarily used to allow creation of the CA prior to configuration information being available." ,
658691 Hidden = false ,
0 commit comments