@@ -144,6 +144,8 @@ pub mod windows {
144144 }
145145
146146 fn resolve ( & self , client_hello : ClientHello < ' _ > ) -> anyhow:: Result < Arc < CertifiedKey > > {
147+ use core:: fmt:: Write as _;
148+
147149 trace ! ( server_name = ?client_hello. server_name( ) , "Received ClientHello" ) ;
148150
149151 let request_server_name = client_hello
@@ -172,7 +174,7 @@ pub mod windows {
172174
173175 // Look up certificate by subject.
174176 // TODO(perf): the resolution result could probably be cached.
175- let mut contexts = store. find_by_subject_str ( & self . subject_name ) . with_context ( || {
177+ let contexts = store. find_by_subject_str ( & self . subject_name ) . with_context ( || {
176178 format ! (
177179 "failed to find server certificate for {} from system store" ,
178180 self . subject_name
@@ -187,45 +189,113 @@ pub mod windows {
187189
188190 trace ! ( subject_name = %self . subject_name, count = contexts. len( ) , "Found certificate contexts" ) ;
189191
190- // Sort certificates from the furthest to the earliest expiration
192+ let now = picky:: x509:: date:: UtcDate :: now ( ) ;
193+
194+ // Initial processing and filtering of the available candidates.
195+ let mut contexts: Vec < CertHandleCtx > = contexts
196+ . into_iter ( )
197+ . enumerate ( )
198+ . filter_map ( |( idx, ctx) | {
199+ let not_after = match picky:: x509:: Cert :: from_der ( ctx. as_der ( ) ) {
200+ Ok ( cert) => {
201+ let serial_number = cert. serial_number ( ) . 0 . iter ( ) . fold (
202+ String :: new ( ) ,
203+ |mut acc, byte| {
204+ let _ = write ! ( acc, "{byte:X?}" ) ;
205+ acc
206+ } ,
207+ ) ;
208+ let subject = cert. subject_name ( ) ;
209+ let issuer = cert. issuer_name ( ) ;
210+ let not_before = cert. valid_not_before ( ) ;
211+ let not_after = cert. valid_not_after ( ) ;
212+
213+ trace ! ( %idx, %serial_number, %subject, %issuer, %not_before, %not_after, "Parsed store certificate" ) ;
214+
215+ if now < not_before {
216+ debug ! (
217+ %idx, %serial_number, %not_before, "Filtered out certificate based on not before validity date"
218+ ) ;
219+ return None ;
220+ }
221+
222+ let has_server_auth_key_purpose = cert. extensions ( ) . iter ( ) . any ( |ext| match ext. extn_value ( ) {
223+ picky:: x509:: extension:: ExtensionView :: ExtendedKeyUsage ( eku) => eku. contains ( picky:: oids:: kp_server_auth ( ) ) ,
224+ _ => false ,
225+ } ) ;
226+
227+ if !has_server_auth_key_purpose {
228+ debug ! (
229+ %idx, %serial_number, "Filtered out certificate because it does not have the server auth extended usage"
230+ ) ;
231+ return None ;
232+ }
233+
234+ not_after
235+ }
236+ Err ( error) => {
237+ debug ! ( %error, "Failed to parse store certificate number {idx}" ) ;
238+ picky:: x509:: date:: UtcDate :: ymd ( 1900 , 1 , 1 ) . expect ( "hardcoded" )
239+ }
240+ } ;
241+
242+ Some ( CertHandleCtx {
243+ idx,
244+ handle : ctx,
245+ not_after,
246+ } )
247+ } )
248+ . collect ( ) ;
249+
250+ // Sort certificates from the farthest to the earliest expiration
191251 // time. Note that it appears the certificates are already returned
192252 // in this order, but it is not a documented behavior. It really
193253 // depends on the internal order maintained by the store, and there
194254 // is no guarantee about what this order is, thus we implement the
195255 // logic here anyway.
196- contexts. sort_by_cached_key ( |ctx| match picky:: x509:: Cert :: from_der ( ctx. as_der ( ) ) {
197- Ok ( cert) => cert. valid_not_after ( ) ,
198- Err ( error) => {
199- warn ! ( %error, "Failed to parse store certificate" ) ;
200- picky:: x509:: date:: UtcDate :: ymd ( 1900 , 1 , 1 ) . expect ( "hardcoded" )
201- }
202- } ) ;
256+ contexts. sort_by ( |a, b| b. not_after . cmp ( & a. not_after ) ) ;
257+
258+ if enabled ! ( tracing:: Level :: TRACE ) {
259+ contexts. iter ( ) . enumerate ( ) . for_each ( |( sorted_idx, ctx) | trace ! ( %sorted_idx, idx = %ctx. idx, not_after = %ctx. not_after, "Sorted certificate" ) ) ;
260+ }
203261
204262 // Attempt to acquire a private key and construct CngSigningKey.
205263 let ( context, key) = contexts
206264 . into_iter ( )
207- . rev ( ) // Revert for: closer to farthest -> farthest to closer
208265 . find_map ( |ctx| {
209- let key = ctx. acquire_key ( ) . ok ( ) ?;
210- CngSigningKey :: new ( key) . ok ( ) . map ( |key| ( ctx, key) )
266+ let key = ctx
267+ . handle
268+ . acquire_key ( )
269+ . inspect_err ( |error| debug ! ( idx = %ctx. idx, %error, "Failed to acquire key for certificate" ) )
270+ . ok ( ) ?;
271+ CngSigningKey :: new ( key)
272+ . inspect_err ( |error| debug ! ( idx = %ctx. idx, %error, "CngSigningKey::new failed" ) )
273+ . ok ( )
274+ . map ( |key| ( ctx, key) )
211275 } )
212- . context ( "failed to aquire private key for certificate " ) ?;
276+ . context ( "no usable certificate found in the system store " ) ?;
213277
214- trace ! ( key_algorithm_group = ?key. key( ) . algorithm_group( ) ) ;
215- trace ! ( key_algorithm = ?key. key( ) . algorithm( ) ) ;
278+ trace ! ( idx = context. idx, not_after = %context. not_after, key_algorithm_group = ?key. key( ) . algorithm_group( ) , key_algorithm = ?key. key( ) . algorithm( ) , "Selected certificate" ) ;
216279
217280 // Attempt to acquire a full certificate chain.
218281 let chain = context
282+ . handle
219283 . as_chain_der ( )
220284 . context ( "certification chain is not available for this certificate" ) ?;
221285 let certs = chain. into_iter ( ) . map ( CertificateDer :: from) . collect ( ) ;
222286
223287 // Return CertifiedKey instance.
224- Ok ( Arc :: new ( CertifiedKey {
288+ return Ok ( Arc :: new ( CertifiedKey {
225289 cert : certs,
226290 key : Arc :: new ( key) ,
227291 ocsp : None ,
228- } ) )
292+ } ) ) ;
293+
294+ struct CertHandleCtx {
295+ idx : usize ,
296+ handle : rustls_cng:: cert:: CertContext ,
297+ not_after : picky:: x509:: date:: UtcDate ,
298+ }
229299 }
230300 }
231301
0 commit comments