We help IT Professionals succeed at work.

Need to get certificates inventory for each server into the spreadsheet- such as expiration date, name of the cert, issuer, cert purpose

6,177 Views
Last Modified: 2015-04-27
Need to get certificates inventory for each server into the spreadsheet- such as expiration date, name of the cert, issuer, cert purpose.

I think I should use a certutil or something similar to export it.

Could you please help with what parameters to use for certutil to export certificates info for each server into csv.
Thank you very much.
Comment
Watch Question

Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:
What do you mean by "cert purpose"?

You could use certutil, I wrote a bit of PowerShell that'd grab certificate stores (local and remote) though.
function Get-InCertificate {
  # .SYNOPSIS
  #   Get certificates from a local or remote certificate store.
  # .DESCRIPTION
  #   Get X509 certificates from a certificate store.
  # .PARAMETER ComputerName
  #   An optional ComputerName to use for this query. If ComputerName is not specified Get-InCertificate uses the current computer.
  # .PARAMETER Expired
  #   Filter results to only include expired certificates.
  # .PARAMETER ExpiresOn
  #   Filter restults to only include certificates which expire on the specified day (between 00:00:00 and 23:59:59).
  #
  #   This parameter may be used in conjunction with Expired to find certificates which expired on a specific day.
  # .PARAMETER HasPrivateKey
  #   Filter results to only include certificates which have a private key available.
  # .PARAMETER Request
  #   Show pending certificate requests.
  # .PARAMETER StoreLocation
  #   Get-InCertificate gets certificates from the LocalMachine store. The CurrentUser store may be specified.
  # .PARAMETER StoreName
  #   Get-InCertificate gets certificates from all stores. A specific store name, or list of store names, may be supplied if required.
  # .INPUTS
  #   System.Security.Cryptography.X509Certificates.StoreName
  #   System.Security.Cryptography.X509Certificates.StoreLocation
  #   System.String
  # .EXAMPLE
  #   Get-InCertificate -StoreName My -StoreLocation CurrentUser
  #
  #   Get all certificates from the Personal store for the CurrentUser (caller).
  # .EXAMPLE
  #   Get-InCertificate -StoreLocation LocalMachine -Request
  #
  #   Get pending certificate requests.
  # .EXAMPLE
  #   C:\PS>Get-Content ServerList.txt | ForEach-Object {
  #   >>  Get-InCertificate -StoreName My -HasPrivateKey -ComputerName $_
  #   >> }
  #
  #   Get certificates with private keys from the personal store (LocalMachine location) for each of the servers listed in ServerList.txt.
  # .NOTES
  #   Author: Chris Dent
  #
  #   Change log:
  #     09/02/2015 - Chris Dent - BugFix: Parameter existence check for ExpiresOn.
  #     04/02/2015 - Chris Dent - Added Issuer and NotAfter parameters.
  #     22/01/2015 - Chris Dent - Added Request parameter.
  #     24/06/2014 - Chris Dent - Added HasPrivateKey and Expired parameters.
  #     12/06/2014 - Chris Dent - First release.  
  
  [CmdLetBinding(DefaultParameterSetName = 'Certificate')]
  param(
    [Parameter(ParameterSetName = 'Certificate')]
    [Security.Cryptography.X509Certificates.StoreName[]]$StoreName = [Enum]::GetNames([Security.Cryptography.X509Certificates.StoreName]),

    [Security.Cryptography.X509Certificates.StoreLocation]$StoreLocation = "LocalMachine",
  
    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [Alias('ComputerNameString', 'Name')]
    [String]$ComputerName = $env:ComputerName,
    
    [Parameter(ParameterSetName = 'Certificate')]
    [Switch]$HasPrivateKey,
    
    [Parameter(ParameterSetName = 'Certificate')]
    [Switch]$Expired,
    
    [Parameter(ParameterSetName = 'Certificate')]
    [ValidateScript( { Get-Date $_ } )]
    $ExpiresOn,
    
    [Parameter(ParameterSetName = 'Certificate')]
    [ValidateNotNullOrEmpty()]
    [String]$Issuer,
    
    [Parameter(ParameterSetName = 'Request')]
    [Switch]$Request
  )

  begin {
    if ($StoreLocation -ne [Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine -and $ComputerName -ne $env:ComputerName) {
      Write-Warning "Certificates in the CurrentUser location cannot be read remotely."
      break
    }
  
    $WhereStatementText = '$_'
    if ($HasPrivateKey) {
      $WhereStatementText = $WhereStatementText + ' -and $_.HasPrivateKey'
    }
    if ($Expired) {
      $WhereStatementText = $WhereStatementText + ' -and $_.NotAfter -lt (Get-Date)'
    }
    if ($psboundparameters.ContainsKey("ExpiresOn")) {
      $WhereStatementText = $WhereStatementText + ' -and $_.NotAfter -gt (Get-Date $ExpiresOn).Date -and $_.NotAfter -lt (Get-Date $ExpiresOn).Date.AddDays(1).AddSeconds(-1)'
    }
    if ($psboundparameters.ContainsKey("Issuer")) {
      $WhereStatementText = $WhereStatementText + ' -and $_.Issuer -like "*CN=$Issuer*"'
    }
    $WhereStatement = [ScriptBlock]::Create($WhereStatementText)
  }
  
  process {
    if ($Request) {
      if ($ComputerName -eq $env:ComputerName) {
        $StorePath = "REQUEST"
      } else {
        $StorePath = "\\$ComputerName\REQUEST"
      }
    
      $Store = New-Object Security.Cryptography.X509Certificates.X509Store($StorePath, $StoreLocation)
      $Store.Open([Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
      $Store.Certificates |
        Add-Member StorePath -MemberType NoteProperty -Value $StorePath -PassThru |
        Add-Member ComputerName -MemberType NoteProperty -Value $ComputerName -PassThru
    } else {
      $StoreName | ForEach-Object {
        if ($ComputerName -eq $env:ComputerName) {
          $StorePath = $_
        } else {
          $StorePath = "\\$ComputerName\$_"
        }
    
        $Store = New-Object Security.Cryptography.X509Certificates.X509Store($StorePath, $StoreLocation)
        $Store.Open([Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
        
        if ($?) {
          $Store.Certificates |
            Add-Member StorePath -MemberType NoteProperty -Value $StorePath -PassThru |
            Add-Member ComputerName -MemberType NoteProperty -Value $ComputerName -PassThru |
            Where-Object $WhereStatement
          
          $Store.Close()
        }
      }
    }
  }
}

Open in new window

I reckon you want a take on Example 3:
Get-Content ServerList.txt | ForEach-Object {
  Get-InCertificate -StoreName My -HasPrivateKey -ComputerName $_
} | Select-Object ComputerName, Subject, Issuer, NotAfter, FriendlyName

Open in new window

But I have trouble with "cert purpose". I guess you mean how the certificate is used on the remote system? If so, it's not something you're going to get. But perhaps you can clarify?

Cheers,

Chris
btanExec Consultant
CERTIFIED EXPERT
Distinguished Expert 2019

Commented:
powershell is good as shared by expert and also check out this forum for some tested sample PS script too
https://social.technet.microsoft.com/Forums/windowsserver/en-US/eb3f8f93-07ff-4d30-8b0a-7a5bfbb9420e/how-to-retrieve-certificate-information-from-a-remote-server-with-powershell?forum=winserverManagement

besides PS, I am thinking of the Certificate Manager tool (Certmgr.exe) which manages certificates, certificate trust lists (CTLs), and certificate revocation lists (CRLs). ONe sample script to get all MY cert as below (likewise to "certutil -store my")
http://certificate.fyicenter.com/419_Windows_certmgr.exe_List_My_Personal_Certificates.html
(cmd line option for certmgr.exe) https://msdn.microsoft.com/en-us/library/e78byta0(v=vs.110).aspx
To display the certificates in the Local Machine certificate store
Syntax
certutil -store [-f] [-enterprise] [-user] [-gmt] [-seconds] [-silent] [-v] [-dc DCName] CertificateStoreName [CertID [OutFile]]]
(cmd line example for certutil) https://technet.microsoft.com/en-us/library/cc772898(WS.10).aspx

Author

Commented:
I tried this with -DC servername switch and also thought this would work....but didn't....

certutil -store [-f] [-enterprise] [-user] [-gmt] [-seconds] [-silent] [-v] [-dc DCName] CertificateStoreName [CertID [OutFile]]]

Fore the powershell script you posted I get the error that unrecongnized command.....Please note the servers that I want to get certificates in personal stores are all 2003. Would the powershell work for them?

Author

Commented:
et-InCertificate : The term 'Get-InCertificate' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At line:1 char:1
+ Get-InCertificate -StoreName My -HasPrivateKey -ComputerName DC01 | Se ...
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Get-InCertificate:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Author

Commented:
I have not tried this option yet -certmgr.exe. Do I need to install Visual Studio for this on the server?
btanExec Consultant
CERTIFIED EXPERT
Distinguished Expert 2019

Commented:
Can also try to  certutil -error <Code> to decode Windows errors. I believe for W2K3 the available option may be for this stated in though it did not mentioned -dc http://www.tekweb.dk/manuals/command/commands/c/certutit.htm
Regardless, -dc should be resolving as per your dns configuration else use the IP address. To be more specific, it is actually, FQDN is used ( fully-qualified host and domain name) for the Directory Server, such as ldap.example.com. Note that this name must be available for DNS and reverse DNS lookups to Directory Server clients because certificate validation may fail if the clients cannot properly resolve the FQDN.

PowerShell is included with Windows Server 2008 R2 but it has to be added to Windows Server 2003. You need to download and install “Windows Management Framework” (Which gives you Windows PowerShell 2.0, WinRM 2.0, and BITS 4.0) Details on this package as well as the download links can be found here http://support.microsoft.com/kb/968929 Pls see this for more info - http://blogs.technet.com/b/danstolts/archive/2011/03/07/how-to-install-powershell-on-windows-server-2003-and-enable-remote-powershell-management-all-servers-should-have-this-done.aspx

The Certificate Manager (certmgr.exe, not certmgr.msc) is automatically installed with Visual Studio. So yes you need to install VStudio.
The Certificate Manager tool (Certmgr.exe) is a command-line utility, whereas Certificates (Certmgr.msc) is a Microsoft Management Console (MMC) snap-in. Because Certmgr.msc is usually found in the Windows System directory, entering certmgr at the command line may load the Certificates MMC snap-in even if you have opened the Visual Studio Command Prompt. This occurs because the path to the snap-in precedes the path to the Certificate Manager tool in the PATH environment variable. If you encounter this problem, you can execute Certmgr.exe commands by specifying the path to the executable.
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:
You must paste the first bit of PowerShell code into the prompt before the second will work. This creates the command you can use.

It does work on 2003, I've used it to audit a thousand or so servers running operating systems between 2003 and 2012 R2.

Chris

Author

Commented:
i tried pasting one line by one like you said, but I still get error

get-InCertificate : The term 'get-InCertificate' is not recognized as the name
of a cmdlet, function, script file, or operable program. Check the spelling of
the name, or if a path was included, verify that the path is correct and try
again.
At line:1 char:1
+ get-InCertificate
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (get-InCertificate:String) [], C
   ommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:
Well, there's an alternative.

Save the attachment and rename it to Get-InCertificate.ps1 once you've downloaded it.

Then from the PowerShell window you can run:
Get-Content ServerList.txt | ForEach-Object {
  C:\Path\To\Script\Get-InCertificate.ps1 -StoreName My -HasPrivateKey -ComputerName $_
} | Select-Object ComputerName, Subject, Issuer, NotAfter, FriendlyName

Open in new window

If the first block of code can be copied in without error you shouldn't get the error message.

Chris
Get-InCertificate.txt

Author

Commented:
It is working now!!! :)

how would I get additional in the select statement?
Select-Object ComputerName, Subject, Issuer, NotAfter, FriendlyName

Need to add the following:
- CERT_KEY_PROV_INFO_PROP_ID(2):
-Subject Alternative Name
  -  Enhanced Key Usage

Also, I need to remove the filter to show all certificates and not just expired

Author

Commented:
Is there a way to get a subject alternative name also in the spreadsheet as well as CERT_KEY_PROV_INFO_PROP_ID(2)  ? I get this below. I checked with the certificate info and it had subject alternative name listed as below

SubjectName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName

EnhancedKeyUsageList
Blank



Below Is the output from certutil -store - v command
    Subject Alternative Name
        Other Name:
             1.3.6.1.4.1.311.25.1=04 10 5d 41 fd aa 6f 21 92 49 8c 82 d2 58 89 b9 7e da
        DNS Name=dcserver01.corp.domain.com

-  Enhanced Key Usage
        Server Authentication (1.3.6.1.5.5.7.3.1)

-- CERT_KEY_PROV_INFO_PROP_ID(2):
    Key Container = 5888c093bcfa376d2dd943e8bab17751_2168e4d8-46c7-4011-9c98-9ee2c4970af9
    Provider = Microsoft RSA SChannel Cryptographic Provider
btanExec Consultant
CERTIFIED EXPERT
Distinguished Expert 2019

Commented:

Author

Commented:
How do I combine it with the script that I already have? It is too complicated for me...sorry.
I made the script work that was first posted here and I just need to add SAN as well to this:

THis works for me. But SAN output name doesn't come into readable form in the excel

Get-Content ServerListPart3.txt | ForEach-Object {C:\Users\username\sslcerts\Get-InCertificate.ps1 -StoreName My -HasPrivateKey -ComputerName $_} | Select-Object ComputerName, Subject, Issuer, NotAfter, HasPrivateKey, SerialNumber, Thumbprint, FriendlyName | export-csv exportcert2.csv



With the script recommended, I do have to get a string for each cert such as this. $cert = get-childitem cert:\localmachine\my\73844B2206C170903185E777F65E969247462741
It is just as difficult as getting SAN name..
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:
The two interfaces (the one I worked with, and the cert: drive) provide exactly the same information in exactly the same format.

Subject Alternate Names needed a bit of work to add so I have a new script version for you which attempts to display it.

Your main script will only need SubjectAlternateNames adding to the output selection this once you've updated the script file:
Get-Content ServerListPart3.txt | 
  ForEach-Object {
    C:\Users\username\sslcerts\Get-InCertificate.ps1 -StoreName My -HasPrivateKey -ComputerName $_
  } |
  Select-Object ComputerName, Subject, SubjectAlternateNames, Issuer, NotAfter, HasPrivateKey, SerialNumber, Thumbprint, FriendlyName |
  export-csv exportcert2.csv -NoTypeInformation

Open in new window

The format of the field is a bit best effort. Please let me know how you get on.

HTH

Chris
Get-InCertificate.txt
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:
> The two interfaces (the one I worked with, and the cert: drive) provide exactly the same information
> in exactly the same format.

Sorry, I meant to add credit in here. The decode method I used was from the link btan posted so thank you for that :)

Chris

Author

Commented:
Hey,
I am still getting this in the excel file.

SubjectName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
System.Security.Cryptography.X509Certificates.X500DistinguishedName
PowerShell Developer
CERTIFIED EXPERT
Top Expert 2010
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION

Author

Commented:
Thank you so much, Chris! Your script provided an excellent excel spreadsheet and what I needed. I can't appreciate enough all the hard work you put into the answer for my question. Wish I could grant you more points. Thank you again!
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:
You're welcome :)

Chris
btanExec Consultant
CERTIFIED EXPERT
Distinguished Expert 2019

Commented:
I have learnt as well thanks folks :)
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:
For posterity, here's the updated code from Get-InCertificate from my Certificate Management module. It has a couple of tweaks over the original here:

Presents the EnhancedKeyUsages as a simple list on the return object.
No longer requests line breaks when decoding the SAN (.Format($false) vs .Format($true) or .Format(1))

Drop the "function Get-InCertificate {" line and the very last "}" in the code block and it can replace the content of the attachment posted above.
function Get-InCertificate {
  # .SYNOPSIS
  #   Get certificates from a local or remote certificate store.
  # .DESCRIPTION
  #   Get X509 certificates from a certificate store.
  # .PARAMETER ComputerName
  #   An optional ComputerName to use for this query. If ComputerName is not specified Get-InCertificate uses the current computer.
  # .PARAMETER Expired
  #   Filter results to only include expired certificates.
  # .PARAMETER ExpiresOn
  #   Filter restults to only include certificates which expire on the specified day (between 00:00:00 and 23:59:59).
  #
  #   This parameter may be used in conjunction with Expired to find certificates which expired on a specific day.
  # .PARAMETER HasPrivateKey
  #   Filter results to only include certificates which have a private key available.
  # .PARAMETER Request
  #   Show pending certificate requests.
  # .PARAMETER StoreLocation
  #   Get-InCertificate gets certificates from the LocalMachine store. The CurrentUser store may be specified.
  # .PARAMETER StoreName
  #   Get-InCertificate gets certificates from all stores. A specific store name, or list of store names, may be supplied if required.
  # .INPUTS
  #   System.Security.Cryptography.X509Certificates.StoreName
  #   System.Security.Cryptography.X509Certificates.StoreLocation
  #   System.String
  # .EXAMPLE
  #   Get-InCertificate -StoreName My -StoreLocation CurrentUser
  #
  #   Get all certificates from the Personal store for the CurrentUser (caller).
  # .EXAMPLE
  #   Get-InCertificate -StoreLocation LocalMachine -Request
  #
  #   Get pending certificate requests.
  # .NOTES
  #   Author: Chris Dent
  #   Team:   Core Technologies
  #
  #   Change log:
  #     03/03/2015 - Chris Dent - Changed Subject Alternate Names decode to drop line breaks. Modified EnhancedKeyUsages to correctly "ToString" from a remote machine.
  #     02/03/2015 - Chris Dent - Added EnhangedKeyUsages property to base object.
  #     27/02/2015 - Chris Dent - Merged store queries into a single statement. Added decode support for Subject Alternate Names.
  #     09/02/2015 - Chris Dent - BugFix: Parameter existence check for ExpiresOn.
  #     04/02/2015 - Chris Dent - Added Issuer and NotAfter parameters.
  #     22/01/2015 - Chris Dent - Added Request parameter.
  #     24/06/2014 - Chris Dent - Added HasPrivateKey and Expired parameters.
  #     12/06/2014 - Chris Dent - First release.  
  
  [CmdLetBinding(DefaultParameterSetName = 'Certificate')]
  param(
    [Parameter(ParameterSetName = 'Certificate')]
    [Security.Cryptography.X509Certificates.StoreName[]]$StoreName = [Enum]::GetNames([Security.Cryptography.X509Certificates.StoreName]),

    [Security.Cryptography.X509Certificates.StoreLocation]$StoreLocation = "LocalMachine",
  
    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [Alias('ComputerNameString', 'Name')]
    [String]$ComputerName = $env:ComputerName,
    
    [Switch]$HasPrivateKey,
    
    [Switch]$Expired,
    
    [ValidateScript( { Get-Date $_ } )]
    $ExpiresOn,
    
    [ValidateNotNullOrEmpty()]
    [String]$Issuer,
    
    [Parameter(ParameterSetName = 'Request')]
    [Switch]$Request
  )

  begin {
    if ($StoreLocation -ne [Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine -and $ComputerName -ne $env:ComputerName) {
      Write-Warning "Certificates in the CurrentUser location cannot be read remotely."
      break
    }
  
    $WhereStatementText = '$_'
    if ($HasPrivateKey) {
      $WhereStatementText = $WhereStatementText + ' -and $_.HasPrivateKey'
    }
    if ($Expired) {
      $WhereStatementText = $WhereStatementText + ' -and $_.NotAfter -lt (Get-Date)'
    }
    if ($psboundparameters.ContainsKey("ExpiresOn")) {
      $WhereStatementText = $WhereStatementText + ' -and $_.NotAfter -gt (Get-Date $ExpiresOn).Date -and $_.NotAfter -lt (Get-Date $ExpiresOn).Date.AddDays(1).AddSeconds(-1)'
    }
    if ($psboundparameters.ContainsKey("Issuer")) {
      $WhereStatementText = $WhereStatementText + ' -and $_.Issuer -like "*CN=$Issuer*"'
    }
    $WhereStatement = [ScriptBlock]::Create($WhereStatementText)
  }
  
  process {
    if ($Request) {
      $StoreNames = 'REQUEST'
    } else {
      $StoreNames = $StoreName
    }
  
    $StoreNames | ForEach-Object {
      if ($ComputerName -eq $env:ComputerName) {
        $StorePath = $_
      } else {
        $StorePath = "\\$ComputerName\$_"
      }
  
      $Store = New-Object Security.Cryptography.X509Certificates.X509Store($StorePath, $StoreLocation)
      $Store.Open([Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
      
      if ($?) {
        $Store.Certificates |
          Add-Member StorePath -MemberType NoteProperty -Value $StorePath -PassThru |
          Add-Member ComputerName -MemberType NoteProperty -Value $ComputerName -PassThru |
          Add-Member SubjectAlternativeNames -MemberType ScriptProperty -Value {
            if ($this.Extensions | Where-Object { $_.Oid.Value -eq '2.5.29.17' }) {
              $this.Extensions['2.5.29.17'].Format($false)
            }
          } -PassThru |
          Add-Member EnhancedKeyUsages -MemberType ScriptProperty -Value {
            if ($this.Extensions | Where-Object { $_.Oid.Value -eq '2.5.29.37' }) {
              $this.Extensions['2.5.29.37'].EnhancedKeyUsages |
                ForEach-Object {
                  $_ | Add-Member ToString -MemberType ScriptMethod -Force -Value {
                    "$($this.Value) ($($this.FriendlyName))"
                  }
                  
                  $_
              }
            }
          } -PassThru |
          Where-Object $WhereStatement
          
        $Store.Close()
      }
    }
  }
}

Open in new window

All the best.

Chris

Author

Commented:
Hello Chris,
Sorry to trouble you. I want to use this script for another project but the latest updated script here is giving me troubles. I am not sure how to update it.

What do you mean by the following?

Drop the "function Get-InCertificate {" line and the very last "}" in the code block and it can replace the content of the attachment posted above.

THank you very much.
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:
If you put this bit into a .ps1 file it can be used as a normal script. It'll still have help text and so on.
  # .SYNOPSIS
  #   Get certificates from a local or remote certificate store.
  # .DESCRIPTION
  #   Get X509 certificates from a certificate store.
  # .PARAMETER ComputerName
  #   An optional ComputerName to use for this query. If ComputerName is not specified Get-InCertificate uses the current computer.
  # .PARAMETER Expired
  #   Filter results to only include expired certificates.
  # .PARAMETER ExpiresOn
  #   Filter restults to only include certificates which expire on the specified day (between 00:00:00 and 23:59:59).
  #
  #   This parameter may be used in conjunction with Expired to find certificates which expired on a specific day.
  # .PARAMETER HasPrivateKey
  #   Filter results to only include certificates which have a private key available.
  # .PARAMETER Request
  #   Show pending certificate requests.
  # .PARAMETER StoreLocation
  #   Get-InCertificate gets certificates from the LocalMachine store. The CurrentUser store may be specified.
  # .PARAMETER StoreName
  #   Get-InCertificate gets certificates from all stores. A specific store name, or list of store names, may be supplied if required.
  # .INPUTS
  #   System.Security.Cryptography.X509Certificates.StoreName
  #   System.Security.Cryptography.X509Certificates.StoreLocation
  #   System.String
  # .EXAMPLE
  #   Get-InCertificate -StoreName My -StoreLocation CurrentUser
  #
  #   Get all certificates from the Personal store for the CurrentUser (caller).
  # .EXAMPLE
  #   Get-InCertificate -StoreLocation LocalMachine -Request
  #
  #   Get pending certificate requests.
  # .NOTES
  #   Author: Chris Dent
  #   Team:   Core Technologies
  #
  #   Change log:
  #     03/03/2015 - Chris Dent - Changed Subject Alternate Names decode to drop line breaks. Modified EnhancedKeyUsages to correctly "ToString" from a remote machine.
  #     02/03/2015 - Chris Dent - Added EnhangedKeyUsages property to base object.
  #     27/02/2015 - Chris Dent - Merged store queries into a single statement. Added decode support for Subject Alternate Names.
  #     09/02/2015 - Chris Dent - BugFix: Parameter existence check for ExpiresOn.
  #     04/02/2015 - Chris Dent - Added Issuer and NotAfter parameters.
  #     22/01/2015 - Chris Dent - Added Request parameter.
  #     24/06/2014 - Chris Dent - Added HasPrivateKey and Expired parameters.
  #     12/06/2014 - Chris Dent - First release.  
  
  [CmdLetBinding(DefaultParameterSetName = 'Certificate')]
  param(
    [Parameter(ParameterSetName = 'Certificate')]
    [Security.Cryptography.X509Certificates.StoreName[]]$StoreName = [Enum]::GetNames([Security.Cryptography.X509Certificates.StoreName]),

    [Security.Cryptography.X509Certificates.StoreLocation]$StoreLocation = "LocalMachine",
  
    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [Alias('ComputerNameString', 'Name')]
    [String]$ComputerName = $env:ComputerName,
    
    [Switch]$HasPrivateKey,
    
    [Switch]$Expired,
    
    [ValidateScript( { Get-Date $_ } )]
    $ExpiresOn,
    
    [ValidateNotNullOrEmpty()]
    [String]$Issuer,
    
    [Parameter(ParameterSetName = 'Request')]
    [Switch]$Request
  )

  begin {
    if ($StoreLocation -ne [Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine -and $ComputerName -ne $env:ComputerName) {
      Write-Warning "Certificates in the CurrentUser location cannot be read remotely."
      break
    }
  
    $WhereStatementText = '$_'
    if ($HasPrivateKey) {
      $WhereStatementText = $WhereStatementText + ' -and $_.HasPrivateKey'
    }
    if ($Expired) {
      $WhereStatementText = $WhereStatementText + ' -and $_.NotAfter -lt (Get-Date)'
    }
    if ($psboundparameters.ContainsKey("ExpiresOn")) {
      $WhereStatementText = $WhereStatementText + ' -and $_.NotAfter -gt (Get-Date $ExpiresOn).Date -and $_.NotAfter -lt (Get-Date $ExpiresOn).Date.AddDays(1).AddSeconds(-1)'
    }
    if ($psboundparameters.ContainsKey("Issuer")) {
      $WhereStatementText = $WhereStatementText + ' -and $_.Issuer -like "*CN=$Issuer*"'
    }
    $WhereStatement = [ScriptBlock]::Create($WhereStatementText)
  }
  
  process {
    if ($Request) {
      $StoreNames = 'REQUEST'
    } else {
      $StoreNames = $StoreName
    }
  
    $StoreNames | ForEach-Object {
      if ($ComputerName -eq $env:ComputerName) {
        $StorePath = $_
      } else {
        $StorePath = "\\$ComputerName\$_"
      }
  
      $Store = New-Object Security.Cryptography.X509Certificates.X509Store($StorePath, $StoreLocation)
      $Store.Open([Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
      
      if ($?) {
        $Store.Certificates |
          Add-Member StorePath -MemberType NoteProperty -Value $StorePath -PassThru |
          Add-Member ComputerName -MemberType NoteProperty -Value $ComputerName -PassThru |
          Add-Member SubjectAlternativeNames -MemberType ScriptProperty -Value {
            if ($this.Extensions | Where-Object { $_.Oid.Value -eq '2.5.29.17' }) {
              $this.Extensions['2.5.29.17'].Format($false)
            }
          } -PassThru |
          Add-Member EnhancedKeyUsages -MemberType ScriptProperty -Value {
            if ($this.Extensions | Where-Object { $_.Oid.Value -eq '2.5.29.37' }) {
              $this.Extensions['2.5.29.37'].EnhancedKeyUsages |
                ForEach-Object {
                  $_ | Add-Member ToString -MemberType ScriptMethod -Force -Value {
                    "$($this.Value) ($($this.FriendlyName))"
                  }
                  
                  $_
              }
            }
          } -PassThru |
          Where-Object $WhereStatement
          
        $Store.Close()
      }
    }
  }

Open in new window

Chris

Gain unlimited access to on-demand training courses with an Experts Exchange subscription.

Get Access
Why Experts Exchange?

Experts Exchange always has the answer, or at the least points me in the correct direction! It is like having another employee that is extremely experienced.

Jim Murphy
Programmer at Smart IT Solutions

When asked, what has been your best career decision?

Deciding to stick with EE.

Mohamed Asif
Technical Department Head

Being involved with EE helped me to grow personally and professionally.

Carl Webster
CTP, Sr Infrastructure Consultant
Empower Your Career
Did You Know?

We've partnered with two important charities to provide clean water and computer science education to those who need it most. READ MORE

Ask ANY Question

Connect with Certified Experts to gain insight and support on specific technology challenges including:

  • Troubleshooting
  • Research
  • Professional Opinions
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.