The Get-Credential cmdlet only shows the first certificate listed on the smart card in the dialog. This makes it impossible to use cards with multiple certificates.
Well, I'm a bit sad I didn't bother to test Get-Credential
before we finalized our new PKI setup that has our root domain and child domain on a single smart card. We use PowerShell religiously and this is going to be a huge problem. Is anyone aware of a workaround? I am exploring the .NET class System.Security.Cryptography.X509Certificates but haven't found any examples I can steal online.
I found this - but haven't figured out how to convert it into PSCredential class.
Add-Type -AssemblyName System.Security
$ValidCerts = [System.Security.Cryptography.X509Certificates.X509Certificate2[]](dir Cert:\CurrentUser\My)
$Cert = [System.Security.Cryptography.X509Certificates.X509Certificate2UI]::SelectfromCollection($ValidCerts,'Choose a certificate','Choose a certificate',0
Hitting the same bug in customer environment. Any updates on a solution or workaround would be highly appreciated
I have a solution, but it's not pretty. I punted on doing this solely in PS and opted for c# (although my c# mostly calls PS -- meaning that although I do this in c# it could be done in PS).
Basically, the process is to import CredUIPromptForWindowsCredentials and CredUnPackAuthenticationBuffer from credui.dll. CredUIPromptForWindowsCredentials will prompt for the smart card and you can pick the cert, then you can use CredUnPackAuthenticationBuffer to pull out username, password, etc. With this info, you can create a NetworkCredential object which can be used to create a PSCredential object, which can be used as any other PSCredential object.
jwilczek, I am working on the code solution you discussed. I made a DLL with C# using the native functions you mentioned. Unfortunately, I can get the smart card pin prompt to show, but not the prompt to select one of the certificates. Would you be willing to post your code, specifically the lines that allow the user to select one of the certificates? Regardless, thank you for the solid tip.
Hello, I came across this working on grabbing a credential from a smart card and it doesn't look like it has been completely fixed yet. Below is the code that I am using to accomplish grabbing any certificate that is on a smart card and creating a PSCredential object out of it. If you have any questions about it let me know.
Function Get-SmartCardCred{
<#
.SYNOPSIS
Get certificate credentials from the user's certificate store.
.DESCRIPTION
Returns a PSCredential object of the user's selected certificate.
.EXAMPLE
Get-SmartCardCred
UserName Password
-------- --------
@@BVkEYkWiqJgd2d9xz3-5BiHs1cAN System.Security.SecureString
.EXAMPLE
$Cred = Get-SmartCardCred
.OUTPUTS
[System.Management.Automation.PSCredential]
.NOTES
Author: Joshua Chase
Last Modified: 01 August 2018
C# code used from https://github.com/bongiovimatthew-microsoft/pscredentialWithCert
#>
[cmdletbinding()]
param()
$SmartCardCode = @"
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Management.Automation;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography.X509Certificates;
namespace SmartCardLogon{
static class NativeMethods
{
public enum CRED_MARSHAL_TYPE
{
CertCredential = 1,
UsernameTargetCredential
}
[StructLayout(LayoutKind.Sequential)]
internal struct CERT_CREDENTIAL_INFO
{
public uint cbSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] rgbHashOfCert;
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredMarshalCredential(
CRED_MARSHAL_TYPE CredType,
IntPtr Credential,
out IntPtr MarshaledCredential
);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CredFree([In] IntPtr buffer);
}
public class Certificate
{
public static PSCredential MarshalFlow(string thumbprint, SecureString pin)
{
//
// Set up the data struct
//
NativeMethods.CERT_CREDENTIAL_INFO certInfo = new NativeMethods.CERT_CREDENTIAL_INFO();
certInfo.cbSize = (uint)Marshal.SizeOf(typeof(NativeMethods.CERT_CREDENTIAL_INFO));
//
// Locate the certificate in the certificate store
//
X509Certificate2 certCredential = new X509Certificate2();
X509Store userMyStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
userMyStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certsReturned = userMyStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
userMyStore.Close();
if (certsReturned.Count == 0)
{
throw new Exception("Unable to find the specified certificate.");
}
//
// Marshal the certificate
//
certCredential = certsReturned[0];
certInfo.rgbHashOfCert = certCredential.GetCertHash();
int size = Marshal.SizeOf(certInfo);
IntPtr pCertInfo = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(certInfo, pCertInfo, false);
IntPtr marshaledCredential = IntPtr.Zero;
bool result = NativeMethods.CredMarshalCredential(NativeMethods.CRED_MARSHAL_TYPE.CertCredential, pCertInfo, out marshaledCredential);
string certBlobForUsername = null;
PSCredential psCreds = null;
if (result)
{
certBlobForUsername = Marshal.PtrToStringUni(marshaledCredential);
psCreds = new PSCredential(certBlobForUsername, pin);
}
Marshal.FreeHGlobal(pCertInfo);
if (marshaledCredential != IntPtr.Zero)
{
NativeMethods.CredFree(marshaledCredential);
}
return psCreds;
}
}
}
"@
Add-Type -TypeDefinition $SmartCardCode -Language CSharp
Add-Type -AssemblyName System.Security
$ValidCerts = [System.Security.Cryptography.X509Certificates.X509Certificate2[]](Get-ChildItem 'Cert:\CurrentUser\My')
$Cert = [System.Security.Cryptography.X509Certificates.X509Certificate2UI]::SelectFromCollection($ValidCerts, 'Choose a certificate', 'Choose a certificate', 0)
$Pin = Read-Host "Enter your PIN: " -AsSecureString
Write-Output ([SmartCardLogon.Certificate]::MarshalFlow($Cert.Thumbprint, $Pin))
}
@jimmy58663
I keep getting this error on Powershell 5.1 when I try to run Get-SmartCardCred: https://paste.ubuntu.com/p/PtqKqwbNgR/
Any help would be greatly appreciated
Most helpful comment
Hello, I came across this working on grabbing a credential from a smart card and it doesn't look like it has been completely fixed yet. Below is the code that I am using to accomplish grabbing any certificate that is on a smart card and creating a PSCredential object out of it. If you have any questions about it let me know.