@@ -161,4 +161,50 @@ impl CertContext {
161161 }
162162 }
163163 }
164+
165+ /// Returns the time when the certificate was added to the store.
166+ ///
167+ /// `None` if the property is not set.
168+ #[ cfg( feature = "time" ) ]
169+ pub fn timestamp ( & self ) -> Result < Option < time:: UtcDateTime > > {
170+ use std:: time:: Duration ;
171+ use windows_sys:: core:: HRESULT ;
172+ use windows_sys:: Win32 :: Foundation :: { GetLastError , CRYPT_E_NOT_FOUND , FILETIME } ;
173+
174+ // Duration between Windows epoch (1601-01-01) and Unix epoch (1970-01-01)
175+ const WINDOWS_TO_UNIX_EPOCH : Duration = Duration :: from_secs ( 11_644_473_600 ) ;
176+
177+ let mut filetime = FILETIME :: default ( ) ;
178+ let mut size = mem:: size_of :: < FILETIME > ( ) as u32 ;
179+
180+ let success = unsafe {
181+ CertGetCertificateContextProperty (
182+ self . inner ( ) ,
183+ CERT_DATE_STAMP_PROP_ID ,
184+ & mut filetime as * mut _ as * mut _ ,
185+ & mut size,
186+ )
187+ } != 0 ;
188+
189+ if !success {
190+ let error = unsafe { GetLastError ( ) } ;
191+ return match error as HRESULT {
192+ CRYPT_E_NOT_FOUND => Ok ( None ) ,
193+ _ => Err ( CngError :: WindowsError ( error) ) ,
194+ } ;
195+ }
196+
197+ let ticks = ( ( filetime. dwHighDateTime as u64 ) << 32 ) | filetime. dwLowDateTime as u64 ;
198+
199+ // Each tick is 100 nanoseconds (FILETIME is in 100ns units)
200+ let duration_since_windows_epoch = Duration :: from_nanos ( ticks * 100 ) ;
201+
202+ let duration_since_unix_epoch = duration_since_windows_epoch
203+ . checked_sub ( WINDOWS_TO_UNIX_EPOCH )
204+ . unwrap_or ( Duration :: ZERO ) ;
205+
206+ let timestamp = time:: UtcDateTime :: UNIX_EPOCH + duration_since_unix_epoch;
207+
208+ Ok ( Some ( timestamp) )
209+ }
164210}
0 commit comments