I see the valid dates and such, but I'm looking for the date the certificate was actually installed.
2 Answers
Certificates are stored in the registry in the following two locations the final key value is the same as the certificate thumb print. So providing you have the thumbprint value you would be able to query the correct regkey
[HKLM\SOFTWARE\Microsoft\SystemCertificates\]
[HKCU\Software\Microsoft\SystemCertificates\]
Using the PowerShell function from here Get-RegistryKeyLastWriteTime you can query the registry key for the Last Write Time.
Full code for the PowerShell function below in case link dies (This is not my work)
Function Get-RegistryKeyTimestamp {
<#
.SYNOPSIS
Retrieves the registry key timestamp from a local or remote system.
.DESCRIPTION
Retrieves the registry key timestamp from a local or remote system.
.PARAMETER RegistryKey
Registry key object that can be passed into function.
.PARAMETER SubKey
The subkey path to view timestamp.
.PARAMETER RegistryHive
The registry hive that you will connect to.
Accepted Values:
ClassesRoot
CurrentUser
LocalMachine
Users
PerformanceData
CurrentConfig
DynData
.NOTES
Name: Get-RegistryKeyTimestamp
Author: Boe Prox
Version History:
1.0 -- Boe Prox 17 Dec 2014
-Initial Build
.EXAMPLE
$RegistryKey = Get-Item "HKLM:\System\CurrentControlSet\Control\Lsa"
$RegistryKey | Get-RegistryKeyTimestamp | Format-List
FullName : HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa
Name : Lsa
LastWriteTime : 12/16/2014 10:16:35 PM
Description
-----------
Displays the lastwritetime timestamp for the Lsa registry key.
.EXAMPLE
Get-RegistryKeyTimestamp -Computername Server1 -RegistryHive LocalMachine -SubKey 'System\CurrentControlSet\Control\Lsa' |
Format-List
FullName : HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa
Name : Lsa
LastWriteTime : 12/17/2014 6:46:08 AM
Description
-----------
Displays the lastwritetime timestamp for the Lsa registry key of the remote system.
.INPUTS
System.String
Microsoft.Win32.RegistryKey
.OUTPUTS
Microsoft.Registry.Timestamp
#>
[OutputType('Microsoft.Registry.Timestamp')]
[cmdletbinding(
DefaultParameterSetName = 'ByValue'
)]
Param (
[parameter(ValueFromPipeline=$True, ParameterSetName='ByValue')]
[Microsoft.Win32.RegistryKey]$RegistryKey,
[parameter(ParameterSetName='ByPath')]
[string]$SubKey,
[parameter(ParameterSetName='ByPath')]
[Microsoft.Win32.RegistryHive]$RegistryHive,
[parameter(ParameterSetName='ByPath')]
[string]$Computername
)
Begin {
#region Create Win32 API Object
Try {
[void][advapi32]
} Catch {
#region Module Builder
$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object System.Reflection.AssemblyName('RegAssembly')
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('RegistryTimeStampModule', $False)
#endregion Module Builder
#region DllImport
$TypeBuilder = $ModuleBuilder.DefineType('advapi32', 'Public, Class')
#region RegQueryInfoKey Method
$PInvokeMethod = $TypeBuilder.DefineMethod(
'RegQueryInfoKey', #Method Name
[Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
[IntPtr], #Method Return Type
[Type[]] @(
[Microsoft.Win32.SafeHandles.SafeRegistryHandle], #Registry Handle
[System.Text.StringBuilder], #Class Name
[UInt32 ].MakeByRefType(), #Class Length
[UInt32], #Reserved
[UInt32 ].MakeByRefType(), #Subkey Count
[UInt32 ].MakeByRefType(), #Max Subkey Name Length
[UInt32 ].MakeByRefType(), #Max Class Length
[UInt32 ].MakeByRefType(), #Value Count
[UInt32 ].MakeByRefType(), #Max Value Name Length
[UInt32 ].MakeByRefType(), #Max Value Name Length
[UInt32 ].MakeByRefType(), #Security Descriptor Size
[long].MakeByRefType() #LastWriteTime
) #Method Parameters
)
$DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
$FieldArray = [Reflection.FieldInfo[]] @(
[Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
[Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
)
$FieldValueArray = [Object[]] @(
'RegQueryInfoKey', #CASE SENSITIVE!!
$True
)
$SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
$DllImportConstructor,
@('advapi32.dll'),
$FieldArray,
$FieldValueArray
)
$PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)
#endregion RegQueryInfoKey Method
[void]$TypeBuilder.CreateType()
#endregion DllImport
}
#endregion Create Win32 API object
}
Process {
#region Constant Variables
$ClassLength = 255
[long]$TimeStamp = $null
#endregion Constant Variables
#region Registry Key Data
If ($PSCmdlet.ParameterSetName -eq 'ByPath') {
#Get registry key data
$RegistryKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($RegistryHive, $Computername).OpenSubKey($SubKey)
If ($RegistryKey -isnot [Microsoft.Win32.RegistryKey]) {
Throw "Cannot open or locate $SubKey on $Computername"
}
}
$ClassName = New-Object System.Text.StringBuilder $RegistryKey.Name
$RegistryHandle = $RegistryKey.Handle
#endregion Registry Key Data
#region Retrieve timestamp
$Return = [advapi32]::RegQueryInfoKey(
$RegistryHandle,
$ClassName,
[ref]$ClassLength,
$Null,
[ref]$Null,
[ref]$Null,
[ref]$Null,
[ref]$Null,
[ref]$Null,
[ref]$Null,
[ref]$Null,
[ref]$TimeStamp
)
Switch ($Return) {
0 {
#Convert High/Low date to DateTime Object
$LastWriteTime = [datetime]::FromFileTime($TimeStamp)
#Return object
$Object = [pscustomobject]@{
FullName = $RegistryKey.Name
Name = $RegistryKey.Name -replace '.*\\(.*)','$1'
LastWriteTime = $LastWriteTime
}
$Object.pstypenames.insert(0,'Microsoft.Registry.Timestamp')
$Object
}
122 {
Throw "ERROR_INSUFFICIENT_BUFFER (0x7a)"
}
Default {
Throw "Error ($return) occurred"
}
}
#endregion Retrieve timestamp
}
}
Usage:
$RegistryKey = Get-Item "HKLM:<key name>"
$RegistryKey | Get-RegistryKeyTimestamp | Format-List

- 3,773
- 2
- 25
- 39
-
this code isn't reliable. If there were additional changes, the code will show only them and won't show what OP asked. – Crypt32 Feb 13 '16 at 15:07
-
@CryptoGuy Additional changes such as? The only thing you do with certificates is install/renew/delete. We can ignore delete and after you install the only thing you are likely do is renew in which case this will give you that date – Drifter104 Feb 14 '16 at 11:19
-
It is true when there is only one certificate. If there other certificates, their changes will cause to change the write timestamp change. – Crypt32 Feb 14 '16 at 11:27
-
1@CryptoGuy there is a key per certificate as per my answer – Drifter104 Feb 14 '16 at 11:41
-
as per https://docs.microsoft.com/en-us/windows/win32/seccrypto/system-store-locations I found the cert I was looking for in Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root – Owensteam Oct 29 '20 at 11:47
You should be able to search your Application event log in windows to find the cert, however if your event log has overflowed and you don't have log rotation/archiving turned on, that data may be lost forever.
Just search for the exact friendly name string or thumbprint (without spaces), for example the following shows up in my event log comment:
Successful auto update of third-party root certificate:: Subject: <CN=QuoVadis Root CA 2, O=QuoVadis Limited, C=BM> Sha1 thumbprint: <CA3AFBCF1240364B44B216208880483919937CF7>.
for this cert:

- 4,243
- 8
- 29
- 44

- 11
-
Any reference to an event in the event log should include the source and event ID, in this case Microsoft-Windows-CAPI2 event ID 1 (https://technet.microsoft.com/en-us/library/cc733922(v=ws.10).aspx). Also, this event pertains only to the automatic renewal of root certificates; I understand the question to be more broadly asking about the installation of *any* certificate. – Mark Berry Sep 01 '17 at 01:33