none
Test-Certificate feedback

    General discussion

  • Hi!

    I'm PowerShell MVP and I'm interesting in PKI. Perhaps this may be interesting for someone here. Now I'm working in PKI task automation using PowerShell replacing and extending some functional from certutil tool. For example we need to check some certificates using certificate chaining engine. We can use certutil tool to do this. But what if we want to check certificates that are stored in current user/local machine store? As far as I know, here is no way to do this using certutil. However PowerShell can help us. Here is example code:

    function Test-Certificate {
    <#
    .Synopsis
        Tests specified certificate for certificate chain
    .Description
        Tests specified certificate for certificate chain and revokation status for each certificate in chain
        exluding Root certificates
    .Parameter Certificate
        Specifies the certificate to test certificate chain. This parameter may accept
        X509Certificate, X509Certificate2 objects or physical file path. this paramter accept
        pipeline input
    .Parameter Password
        Specifies PFX file password. Password must be passed as SecureString.
    .Parameter CRLMode
        Sets revocation check mode. May contain on of the following values:
        
        Online - perform revocation check downloading CRL from CDP extension ignoring cached CRLs. Default value
        Offline - perform revocation check using cached CRLs if they are already downloaded
        NoCheck - specified certificate will not checked for revocation status (not recommended)
    .Paramaeter CRLFlag
        Sets revocation flags for chain elements. May contain one of the following values:
        
        ExcludeRoot - perform revocation check for each certificate in chain exluding root. Default value
        EntireChain - perform revocation check for each certificate in chain including root. (not recommended)
        EndCertificateOnly - perform revocation check for specified certificate only.
    .Example
        Get-ChilItem cert:\CurrentUser\My | Test-Certificate -CRLMode "NoCheck"
        
        Will check certificate chain for each certificate in current user Personal container.
        Certificates will not checked for revocation status.
    .Example
        Test-Certificate C:\Certs\certificate.cer -CRLFlag "EndCertificateOnly"
        
        Will check certificate chain for certificate that is located in C:\Certs and named
        as Certificate.cer and revocation checking will be performed for specified certificate only
    .Outputs
        This script return general info about certificate chain status
    #>
    [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
            $Certificate,
            [System.Security.SecureString]$Password,
            [ValidateSet("NoCheck", "Online", "Offline")]
            [string]$CRLMode = "Online",
            [ValidateSet("EndCertificateOnly", "EntireChain", "ExcludeRoot")]
            [string]$CRLFlag = "ExcludeRoot",
            [ValidateSet("AllFlags", "AllowUnknownCertificateAuthority", "NoFlag", "IgnoreNotTimeValid",
                "IgnoreCtlNotTimeValid", "IgnoreNotTimeNested", "IgnoreInvalidBasicConstraints",
                "IgnoreWrongUsage", "IgnoreInvalidName", "IgnoreInvalidPolicy", "IgnoreEndRevocationUnknown",
                "IgnoreCtlSignerRevocationUnknown", "IgnoreCertificateAuthorityRevocationUnknown",
                "IgnoreRootRevocationUnknown")]
            [string[]]$VerificationFlags = "NoFlag"
        )
        
        begin {
            $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
            $chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
            $chain.ChainPolicy.RevocationFlag = $CRLFlag
            $chain.ChainPolicy.RevocationMode = $CRLMode
            $chain.ChainPolicy.VerificationFlags = $VerificationFlags
            function _getstatus_ ($status, $chain, $cert) {
                if ($status) {
                    Write-Host Current certificate $cert.SerialNumber chain and revokation status is valid -ForegroundColor Green
                } else {
                    Write-Warning "Current certificate $($cert.SerialNumber) chain is invalid due of the following errors:"
                    $chain.ChainStatus | %{Write-Host $_.StatusInformation.trim() -ForegroundColor Red}
                }
            }
        }
        
        process {
            if ($_ -is [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
                $status = $chain.Build($_)
                _getstatus_ $status $chain $_
            } else {
                if (!(Test-Path $Certificate)) {Write-Warning "Specified path is invalid"; return}
                else {
                    if ((Resolve-Path $Certificate).Provider.Name -ne "FileSystem") {
                        Write-Warning "Spicifed path is not recognized as filesystem path. Try again"; return
                    } else {
                        $Certificate = gi $(Resolve-Path $Certificate)
                        switch -regex ($Certificate.Extension) {
                        "\.CER|\.DER|\.CRT" {$cert.Import($Certificate.FullName)}
                        "\.PFX" {
                            if (!$Password) {$Password = Read-Host "Enter password for PFX file $certificate" -AsSecureString}
                                $cert.Import($Certificate.FullName, $password, "UserKeySet")
                            }
                        "\.P7B|\.SST" {
                            $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
                            $cert.Import([System.IO.File]::ReadAllBytes($Certificate.FullName))
                            }
                        default {Write-Warning "Looks like your specified file is not a certificate file"; return}
                        }
                        $cert | %{
                            $status = $chain.Build($_)
                            _getstatus_ $status $chain $_
                        }
                        $cert.Reset()
                        $chain.Reset()
                    }
                }
            }
        }
    }
    This script uses certificate chaining engine implementation in .NET Framework. How you may use this script? At first you must have PowerShell V2 installed. What next?

    1) Save this code in file with PS1 extension
    2) open Windows Powershell console and type:

    . path\Test-Certificate.ps1
    (first dot sign is required)

    3) I have added some examples how you may use this script to check certificates.

    this is only one of my function set for certificate/CA management. If you find this very useful, you can leave a feedback or questions here.

    thanks!
    [http://www.sysadmins.lv] As always enjoy the automation of tools within the Windows-based, .NET aware, WPF accessible, multi-processes on the same IP / Port usage, admin's automation tool, powershell.exe! © Flowering Weeds
    Sunday, October 18, 2009 10:36 AM

All replies

  • wouldn't you have any sample for requesting certificates from authority by using the PS? I would like to have something to ask for base smart card crypto provider smart cards.

    anyway, this looks as a good starting point but I never tried the CSP wrappers in NET so if you were slightly more experienced...

    ondrej.

    Sunday, October 18, 2009 2:07 PM
  • At this time I haven't sample code for requesting certificates. As far as I know, here is only one way to do this - CAPICOM interfaces:
    http://msdn.microsoft.com/en-us/library/ee373776(VS.85).aspx

    since this is unmanaged code (looks like it is used in certutil/certreq), you cannot use them directly. However you can wrap this code into C# and wrap C# code into PowerShell.

    Here is discussion about unmanaged code uasge in .NET applications: http://www.codeproject.com/KB/mcpp/usingcppdll.aspx
    This will be next step in my work with PKI using PowerShell.
    [http://www.sysadmins.lv] As always enjoy the automation of tools within the Windows-based, .NET aware, WPF accessible, multi-processes on the same IP / Port usage, admin's automation tool, powershell.exe! © Flowering Weeds
    Sunday, October 18, 2009 2:35 PM
  • Hi,

    you can verify certificates in local store by exporting them and running certutil -urlfetch -verify. Here is a sample script.

    @echo off
    SETLOCAL
    mkdir temp
    IF %ERRORLEVEL% NEQ 0 GOTO err
    cd temp

    certutil -split -store my 1>nul

    for %%c in ("*.crt") do (
     for /f "tokens=3 delims==" %%G IN ('certutil -urlfetch -verify %%c ^|findstr -i CertContext.*dwErrorStatus') DO (
      IF %%G NEQ 0 (
       echo Certificate in %%c CERT_TRUST_STATUS.dwErrorStatus %%G
       echo Certificate in %%c CERT_TRUST_STATUS.dwErrorStatus %%G >> ..\err.txt
       certutil -dump %%c >> ..\err.txt
       echo ------------------------------------------------------------ >> ..\err.txt
      ) 
     ) 
     erase /q %%c
    )
    cd ..

    rmdir /q /s temp
    GOTO end

    :err
    echo Could not create temp

    :end
    ENDLOCAL
    @echo on



    I guess the scripts still needs some polishing but in general it should work. However I still like more your solution.  

    Regards

    Martin

    Monday, October 19, 2009 9:14 AM
  • Interesting solution. This is one of reason why PowerShell. In most cases you just get simple CLI and readable commands. :)
    [http://www.sysadmins.lv] As always enjoy the automation of tools within the Windows-based, .NET aware, WPF accessible, multi-processes on the same IP / Port usage, admin's automation tool, powershell.exe! © Flowering Weeds
    Monday, October 19, 2009 9:57 AM
  • Hi,

     

    Thanks for your great sharing. :)


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Tuesday, October 20, 2009 2:21 AM
    Moderator
  • Ondrej, I have figured out some of these interfaces. So, if you're intersing in this you can email me your question with explanations.


    [http://www.sysadmins.lv] As always enjoy the automation of tools within the Windows-based, .NET aware, WPF accessible, multi-processes on the same IP / Port usage, admin's automation tool, powershell.exe! © Flowering Weeds
    Thursday, October 22, 2009 9:01 AM