SauerTrout
SauerTrout

Reputation: 499

Identifying Certificate by "Certificate Template Name" in PowerShell

I need to use a PowerShell script to pick the certificate with "Certificate Template Name" as "Machine." In certmgr.msc, this has "Certificate Template" with value of "Computer." In Details, the same one has "Certificate Template Name" as "Machine."

How can I use either of these values in a PowerShell script?

Thus far, I have:

get-childitem cert:\localmachine\my | where-object {$_.}

I've tried just about every method that intellisense loads, but have not been able to find anything that meets my needs.

Thank You,

Upvotes: 9

Views: 40524

Answers (6)

stackprotector
stackprotector

Reputation: 13588

All current answers assume, that you can parse the template name from one of the following OIDs:

OID FriendlyName
1.3.6.1.4.1.311.20.2 Certificate Template Name
1.3.6.1.4.1.311.21.7 Certificate Template Information

In my case, the certificate does not contain the extension "Certificate Template Name". It does contain the extension "Certificate Template Information" though, but I cannot parse a name from it, because it does only contain the OID of the template. So, as Mathias R. Jessen already mentioned in the comments, you have to look this OID up in your AD to get the template name.

As a workaround (not every machine has access to the Get-ADObject cmdlet), you can use certutil to do the lookup for you:

certutil -store My "Computer*" | Select-String -Pattern '\(sha1\): ' | ForEach-Object {$_ -split ' ' | Select-Object -Last 1 | ForEach-Object {Get-ChildItem -Path "Cert:\LocalMachine\*$_" -Recurse}}

certutil will print all certificates of the "My" certificate store which use a template that begins with Computer. From the output, I parse the thumbprint of the certificate and use that to look the certificate up again in the store.

Upvotes: 1

Leon Bouquiet
Leon Bouquiet

Reputation: 4382

In addition to the answers already posted, let me share what works for me:

# Example usage:
#   ls 'Cert:\LocalMachine\My' | ForEach-Object { Get-CertificateTemplateName $_ }
#
function Get-CertificateTemplateName($certificate)
{
    # The template name is stored in the Extension data. 
    # If available, the best is the extension named "Certificate Template Name", since it contains the exact name.
    $templateExt = $certificate.Extensions | Where-Object{ ( $_.Oid.FriendlyName -eq 'Certificate Template Name') } | Select-Object -First 1   
    if($templateExt) {
        return $templateExt.Format(1)
    }
    else {
        # Our fallback option is the "Certificate Template Information" extension, it contains the name as part of a string like:
        # "Template=Web Server v2(1.3.6.1.4.1.311.21.8.2499889.12054413.13650051.8431889.13164297.111.14326010.6783216)"
        $templateExt = $certificate.Extensions | Where-Object{ ( $_.Oid.FriendlyName -eq 'Certificate Template Information') } | Select-Object -First 1   
        if($templateExt) {
            $information = $templateExt.Format(1)

            # Extract just the template name in $Matches[1]
            if($information -match "^Template=(.+)\([0-9\.]+\)") {
                return $Matches[1]
            } else {
                # No regex match, just return the complete information then
                return $information
            }
        } else {
            # No template name found
            return $null
        }
    }
}

Upvotes: 3

SauerTrout
SauerTrout

Reputation: 499

Here is a solution sans-modules:

$templateName = 'Super Cool *'
Get-ChildItem 'Cert:\LocalMachine\My' | Where-Object{ $_.Extensions | Where-Object{ ($_.Oid.FriendlyName -eq 'Certificate Template Information') -and ($_.Format(0) -match $templateName) }}

Upvotes: 9

Mark Stanfill
Mark Stanfill

Reputation: 46

$cert.Extension.format(0) and format(1) will return these attributes in a human-readable manner.

     # Retrieves CRL distribution point if present
    function Get-CRLDistPoint {
        Param ($cert)
                $extension = $cert.Extensions | where {$_.OID.FriendlyName -eq "CRL Distribution Points"}
                if ($extension) {
                    $crlURL = $extension.Format(0)
                    # trim header
                    $crlURL = $crlURL -replace "^.*URL=", ""
                    }
                $crlURL
    }

Get-ChildItem Cert:\LocalMachine\my | %{ Get-CRLDistPoint}

Upvotes: 1

Here's a native PowerShell solution:

Thanks go to the PowerShell Gallery

<#
.SYNOPSIS
 Outputs an object consisting of the template name (Template), an OID (OID), the minor version (MinorVersion), and the major version (MajorVersion).

.DESCRIPTION
 Outputs an object consisting of the template name (Template), an OID (OID), the minor version (MinorVersion), and the major version (MajorVersion).
 This information is derived from the Certificate Extensions.

.PARAMETER Certificate
 A X509Certificate2 object

.EXAMPLE
 Get-ChildItem "Cert:\LocalMachine\My" | Get-CertificateTemplate

.EXAMPLE
 Get-ChildItem "Cert:\LocalMachine\My" | Select-Object Name,Thumbprint,@{Name="Template";Expression={Get-CertificateTemplate $_}}

.INPUTS
 Any X509Certificate2 object

.OUTPUTS
 [PSCustomObject] @{Template=<template name; OID=<oid string>; MajorVersion=<major version num>; MinorVersion=<minor version num> }
#>
function Get-CertificateTemplate {
  [CmdletBinding(SupportsShouldProcess=$false)]
  [OutputType([string])]
  Param([Parameter(Mandatory=$true, ValueFromPipeline=$true)] [ValidateNotNull()] [Security.Cryptography.X509Certificates.X509Certificate2]$Certificate)

  Process {
    $regExPrimary=[System.Text.RegularExpressions.Regex]::new("Template=([\w\s\d\.]+)\(((?:\d+.)+)\), Major Version Number=(\d+), Minor Version Number=(\d+)",[System.Text.RegularExpressions.RegexOptions]::None)
    $regExSecondary=[System.Text.RegularExpressions.Regex]::new("Template=((?:\d+.)+), Major Version Number=(\d+), Minor Version Number=(\d+)",[System.Text.RegularExpressions.RegexOptions]::None)

    $temp = $Certificate.Extensions | Where-Object { $_.Oid.FriendlyName -eq "Certificate Template Name" }
    if ($temp -eq $null) {
      Write-Verbose "Did not find 'Certificate Template Name' extension"
      $temp=$Certificate.Extensions | Where-Object { $_.Oid.Value -eq "1.3.6.1.4.1.311.21.7" }
    }
    else { Write-Verbose "Found 'Certificate Template Name' extension" }

    $Matches=$regExPrimary.Matches($temp.Format($false))
    if ($Matches.Count -gt 0) {
      $object=@{Template=$Matches[0].Groups[1].Value; OID=$Matches[0].Groups[2].Value; 
                MajorVersion=$Matches[0].Groups[3].Value; MinorVersion=$Matches[0].Groups[4].Value;
                Thumbprint=$Certificate.Thumbprint }
    }
    else {
      $Matches=$regExSecondary.Matches($temp.Format($false))
      if ($Matches.Count -gt 0) {
        Write-Verbose "Found certificate without a valid Template Name"
        $object=@{Template=$Matches[0].Groups[1].Value; OID=$Matches[0].Groups[1].Value; 
                  MajorVersion=$Matches[0].Groups[2].Value; MinorVersion=$Matches[0].Groups[3].Value;
                  Thumbprint=$Certificate.Thumbprint }

      }
      else {
        Write-Verbose "Found root certificate"
        $object=@{Template="Root Certificate"; OID=""; MajorVersion=""; MinorVersion=""; Thumbprint=$Certificate.Thumbprint }
      }
    }
    return [PSCustomObject]$object
  }
}

Upvotes: 1

Alex Sarafian
Alex Sarafian

Reputation: 674

Try this powershell module CertificatePS. Inside there is this cmdlet Get-CertificateTemplate that does exactly what you need. I developed it and I use it myself to distinguish between machine and web template certificates.

This is an example of use although there are other possibilities such as adding a PSNoteProperty into each return object

# With Select-Object
Get-ChildItem "Cert:\LocalMachine\My" | Select-Object Name,Thumbprint,@{Name="Template";Expression={Get-CertificateTemplate $_}}

# With Where-Object
Get-ChildItem "Cert:\LocalMachine\My" | Where-Object {Get-CertificateTemplate $_ -eq "Template"}}

Check out more examples about this module here.

The module is not perfect so if you have any feedback or contributions please do so on the github project.

Upvotes: 0

Related Questions