11using System ;
2- using System . Linq ;
32using System . Security ;
43using System . Security . Cryptography ;
54using System . Security . Cryptography . X509Certificates ;
65using Azure . Identity ;
7- using Microsoft . Graph . Cli . Core . Utils ;
86
97namespace Microsoft . Graph . Cli . Core . Authentication ;
108
@@ -23,7 +21,8 @@ public static class ClientCertificateCredentialFactory
2321 /// <param name="authorityHost">The entra authentication endpoint (to use with national clouds)</param>
2422 /// <returns>A ClientCertificateCredential</returns>
2523 /// <exception cref="ArgumentNullException">When a null url is provided for the authority host.</exception>
26- public static ClientCertificateCredential GetClientCertificateCredential ( string ? tenantId , string ? clientId , string ? certificateName , string ? certificateThumbPrint , Uri authorityHost )
24+ public static ClientCertificateCredential GetClientCertificateCredential ( string ? tenantId , string ? clientId ,
25+ string ? certificateName , string ? certificateThumbPrint , Uri authorityHost )
2726 {
2827 if ( string . IsNullOrWhiteSpace ( certificateName ) && string . IsNullOrWhiteSpace ( certificateThumbPrint ) )
2928 {
@@ -46,11 +45,13 @@ public static ClientCertificateCredential GetClientCertificateCredential(string?
4645
4746 X509Certificate2 ? certificate ;
4847
49- if ( ! string . IsNullOrWhiteSpace ( certificateName ) && TryGetCertificateFromStore ( certificateName , isThumbPrint : false , out certificate ) )
48+ if ( ! string . IsNullOrWhiteSpace ( certificateName ) &&
49+ TryGetCertificateFromStore ( certificateName , isThumbPrint : false , out certificate ) )
5050 {
5151 return new ClientCertificateCredential ( tenantId , clientId , certificate , credOptions ) ;
5252 }
53- else if ( ! string . IsNullOrWhiteSpace ( certificateThumbPrint ) && TryGetCertificateFromStore ( certificateThumbPrint , isThumbPrint : true , out certificate ) )
53+ else if ( ! string . IsNullOrWhiteSpace ( certificateThumbPrint ) &&
54+ TryGetCertificateFromStore ( certificateThumbPrint , isThumbPrint : true , out certificate ) )
5455 {
5556 return new ClientCertificateCredential ( tenantId , clientId , certificate , credOptions ) ;
5657 }
@@ -65,7 +66,8 @@ public static ClientCertificateCredential GetClientCertificateCredential(string?
6566 /// <param name="isThumbPrint">If true, try to find the certificate by the thumb print.</param>
6667 /// <param name="certificate">A matching unexpired certificate from the store.</param>
6768 /// <returns>Returns true if the certificate was fetched successfully.</returns>
68- internal static bool TryGetCertificateFromStore ( string certificateNameOrThumbPrint , bool isThumbPrint , out X509Certificate2 ? certificate )
69+ internal static bool TryGetCertificateFromStore ( string certificateNameOrThumbPrint , bool isThumbPrint ,
70+ out X509Certificate2 ? certificate )
6971 {
7072 bool result = false ;
7173 certificate = null ;
@@ -74,19 +76,18 @@ internal static bool TryGetCertificateFromStore(string certificateNameOrThumbPri
7476 try
7577 {
7678 store . Open ( OpenFlags . ReadOnly ) ;
77-
78- // If using a certificate with a trusted root you do not need to FindByTimeValid, instead:
79- // currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, true);
80- X509Certificate2Collection signingCerts = store . Certificates . Find ( X509FindType . FindByTimeValid , DateTime . Now , false )
81- . Find ( isThumbPrint ? X509FindType . FindByThumbprint : X509FindType . FindBySubjectDistinguishedName , certificateNameOrThumbPrint , false ) ;
79+ X509Certificate2Collection signingCerts = store . Certificates
80+ . Find ( X509FindType . FindByTimeValid , DateTime . Now , false )
81+ . Find ( isThumbPrint ? X509FindType . FindByThumbprint : X509FindType . FindBySubjectDistinguishedName ,
82+ certificateNameOrThumbPrint , false ) ;
8283 if ( signingCerts . Count == 0 )
8384 {
8485 result = false ;
8586 }
8687 else
8788 {
88- // Return the first certificate in the collection, has the right name and is current.
89- certificate = signingCerts . OrderByDescending ( static c => c . NotBefore ) . FirstOrDefault ( ) ;
89+ certificate = FindLatestByValidity ( signingCerts ) ;
90+
9091 result = true ;
9192 }
9293 }
@@ -98,7 +99,7 @@ internal static bool TryGetCertificateFromStore(string certificateNameOrThumbPri
9899 }
99100 catch ( SecurityException )
100101 {
101- // Isufficient permissions to read the store
102+ // Insufficient permissions to read the store
102103 result = false ;
103104 }
104105 catch ( ArgumentException )
@@ -113,12 +114,30 @@ internal static bool TryGetCertificateFromStore(string certificateNameOrThumbPri
113114 certificate . Dispose ( ) ;
114115 certificate = null ;
115116 }
117+
116118 store . Close ( ) ;
117119 }
118120
119121 return result ;
120122 }
121123
124+ internal static X509Certificate2 ? FindLatestByValidity ( X509Certificate2Collection signingCerts )
125+ {
126+ X509Certificate2 ? certificate = null ;
127+ // Return the first certificate in the collection, has the right name and is current.
128+ foreach ( var cert in signingCerts )
129+ {
130+ // Use this certificate if it became valid after the currently selected one.
131+ if ( certificate is null || cert . NotBefore > certificate . NotBefore )
132+ {
133+ certificate ? . Dispose ( ) ;
134+ certificate = cert ;
135+ }
136+ }
137+
138+ return certificate ;
139+ }
140+
122141 /// <summary>
123142 /// Opens a certificate file.
124143 /// </summary>
0 commit comments