How to enumerate all certificates on a smart card (PowerShell)

Hi all,

 

Some time ago I assisted my colleague Jeff Bowles with the development of a PowerShell script which enumerates all certificates on a smart card. Basically the replacement to CAPICOM.Store.Open CAPICOM_SMART_CARD_USER_STORE.

He developed a sample that returnsSystem.Security.Cryptography.X509Certificates.X509Store object with the certificates in the card. The sample tries to emulate what logonUI.exe does during smart card logon as documented
at Certificate Enumeration

Note that this code is also a great example that shows how we can use PowerShell to call Win32 API the same way we do it with any .NET application through P/Invoke mechanism:

 

 function Get-SCUserStore {



[string]$providerName ="Microsoft Base Smart Card Crypto Provider"



# import CrytoAPI from advapi32.dll

$signature = @"

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

[return : MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptGetProvParam(

   IntPtr hProv,

   uint dwParam,

   byte[] pbProvData,

   ref uint pdwProvDataLen, 

   uint dwFlags); 



[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

[return : MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptDestroyKey(

   IntPtr hKey);   

   

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

[return : MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptAcquireContext(

   ref IntPtr hProv,

   string pszContainer,

   string pszProvider,

   uint dwProvType,

   long dwFlags);

      

[DllImport("advapi32.dll", CharSet=CharSet.Auto)]

[return : MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptGetUserKey(

   IntPtr hProv, 

   uint dwKeySpec,

   ref IntPtr phUserKey);

   

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptGetKeyParam(

   IntPtr hKey,

   uint dwParam,

   byte[] pbData,

   ref uint pdwDataLen,

   uint dwFlags);

   

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

[return : MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptReleaseContext(

   IntPtr hProv,

   uint dwFlags);

"@



$CryptoAPI = Add-Type -member $signature -name advapiUtils -Namespace CryptoAPI -passthru



# set some constants for CryptoAPI

$AT_KEYEXCHANGE = 1

$AT_SIGNATURE = 2

$PROV_RSA_FULL = 1

$KP_CERTIFICATE = 26

$PP_ENUMCONTAINERS = 2

$PP_CONTAINER = 6

$PP_USER_CERTSTORE = 42

$CRYPT_FIRST = 1

$CRYPT_NEXT = 2

$CRYPT_VERIFYCONTEXT = 0xF0000000



[System.IntPtr]$hProvParent=0

$contextRet = $CryptoAPI::CryptAcquireContext([ref]$hprovParent,$null,$providerName,$PROV_RSA_FULL,$CRYPT_VERIFYCONTEXT)



[Uint32]$pdwProvDataLen = 0

[byte[]]$pbProvData = $null

$GetProvParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_CONTAINER,$pbProvData,[ref]$pdwProvDataLen,0)



if($pdwProvDataLen -gt 0) 

  {

    $ProvData = new-Object byte[] $pdwProvDataLen

    $GetKeyParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_CONTAINER,$ProvData,[ref]$pdwProvDataLen,0)

   }

   

$enc = new-object System.Text.UTF8Encoding($null)

$keyContainer = $enc.GetString($ProvData)

 

 write-host " The Default User Key Container:" $keyContainer



[Uint32]$pdwProvDataLen = 0

[byte[]]$pbProvData = $null

$GetProvParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_USER_CERTSTORE,$pbProvData,[ref]$pdwProvDataLen,0)



if($pdwProvDataLen -gt 0) 

  {

    $ProvData = new-Object byte[] $pdwProvDataLen

    $GetKeyParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_USER_CERTSTORE,$ProvData,[ref]$pdwProvDataLen,0)

    [uint32]$provdataInt = [System.BitConverter]::ToUInt32($provdata,0)

    [System.IntPtr]$hwStore = $provdataInt

   }

      

 $store = new-object System.Security.Cryptography.X509Certificates.X509Store($hwStore)

         

# release smart card

$ReleaseContextRet = $CryptoAPI::CryptReleaseContext($hprovParent,0)



return $store

}



write-host ((get-WmiObject win32_PnPSignedDriver|where{$_.deviceID -like "*smartcard*"}).devicename) "reports the following certificates;" 



# returns System.Security.Cryptography.X509Certificates.X509Store object representing PP_USER_CERTSTORE on Smart Card

$SCcertStore = Get-SCuserSTore



# enumerate certificates

$SCcertStore.certificates

 

 

 

I hope this helps. And THANKS A LOT JEFF for sharing the final sample with me and the community!!! Kudos!!!

Regards,

 

Alex (Alejandro Campos Magencio)

Comments

  • Anonymous
    December 16, 2010
    Awesome!I would love to see more examples on accessing CryptoAPI unmanaged APIs via Windows PowerShell.p.s. 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 tools, PowerShell! © Flowering Weeds
  • Anonymous
    February 18, 2014
    HOw could i get the thumbprint from these?
  • Anonymous
    February 24, 2014
    Great Script, works like a charm if you type the proper CSP provider of your card.If you avoid using a function it's really fast otherwise it takes a couple of seconds to show the certificate.
  • Anonymous
    December 09, 2014
    The script works great. I have a need to get the FriendlyName from the certs as well that is not provided in this script. How difficult is it to get the FriendlyName name from the SmartCard Cert?