Summary: It's not a very well-known feature, but the PSCredential object, and the PowerShell Get-Credential cmdlet, both support certificate credentials (including PIN-protected certificates). In this post, we take a look at how a certificate credential is marshaled inside a PSCredential object, how you can do this marshaling yourself, and how you can retrieve the original certificate from a PSCredential object supplied to you.
Most Win32 APIs that support the PSCredential object for credential validation already support certificates. However, if your code currently consumes a PSCredential, and you use the user name and password without expecting a certificate credential, you can make the necessary adjustments yourself.
All the code for this walkthrough can be found here.
Get a certificate inside a PSCredential object
The PSCredential object has only two properties, 'UserName' and 'Password'. To wedge a certificate into this format, you must use the CredMarshalCredential API. This API takes a credential type, and a credential struct, and it produces a string representing the credential. As of the time of this writing, the credential types that are supported are CertCredential and UsernameTargetCredential.
This means we can generate a string from a certificate credential, and then set the 'UserName' field of the PSCredential object to this string. If the certificate is PIN protected, the PIN can be wrapped in a SecureString, and set as the Password property on the PSCredential.
This process is exactly what the Get-Credential cmdlet does in PowerShell (on Windows). If you run Get-Credential, you will get the standard credential dialog box.
Select the down arrow on the right side. From the drop-down list, you can select certificates that match the User Certificate criteria. (Generally, the dialog box shows certificates in the Personal and Trusted Root stores of the current user.)
Selecting my smart card results in the following:
When I'm all done, the resulting output looks like this:
As you can see, the 'UserName' field doesn't really look like a user name. It is, in fact, the encoded certificate data returned from CredMarshalCredential.
This marshaled credential can now be used with many standard Windows authentication APIs, including LogonUser. However, if you're building a .NET application, and you want to support PSCredentials that use a certificate, you will have to unpack this special UserName property. (This is true unless you only ever intend to pass along the marshaled credential to supporting APIs.) Let's see how we can do that.
Get a certificate from a PSCredential.UserName blob
If you want to locate the certificate represented by a PSCredential.UserName data blob, you can use the CredUnmarshalCredential API, which is the logical inverse of our trusty CredMarshalCredential. You can pass your UserName string, and receive the CERT_CREDENTIAL_INFO struct back, which has the SHA-1 hash of the original certificate. Your application can then do any certificate lookup you want (assuming your application or service has the correct permissions).
For full details on how to perform this call, see the ReverseMarshal function in the example code here.
Software engineer, Active Directory Fabric team