derwowusste
Goto Top

Smartcards und runas in Powershell

Achtung: Expertenfrage face-smile
Achtung, habe was korrigieren müssen

Servus Kollegen,

das ist ein seltenes und wie ich denke schwieriges Anliegen:
ich habe eine SmartCard zur Windowsanmeldung mit PIN. Ich kann per Rechtsklick "ausführen als" auch wunderbar damit arbeiten.
Ich möchte nun dieses Rechtsgeklicke loswerden und in einem Skript eben diesen Dialog anfordern.
Ich habe sogar ein Skript dazu, welches auch mit SmartCards, die die PC-Tastatur zur PIN-Eingabe nutzen, funktioniert - z.B. mit einem Yubikey 5.

Mit einem SmartCardreader mit eigener Tastatur funktioniert es leider nicht, weder wird die Tastatur des Readers selbst wird nicht angesteuert , noch kann ich die PC-Tastatur nutzen.
Was muss an folgendem Skript geändert werden, damit das geht?
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;
            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))
}
$cred=Get-SmartCardCred
$StartInfo = New-Object System.Diagnostics.ProcessStartInfo
$StartInfo.FileName = 'cmd.exe'  
$StartInfo.UseShellExecute = $false
$StartInfo.UserName = $Cred.Username
$StartInfo.Password = $Cred.Password
$StartInfo.WorkingDirectory = $env:windir
$Process = New-Object System.Diagnostics.Process
$Process.StartInfo = $StartInfo
$Process.Start()
$Cred = $null

(dieses Skript startet nach PIN-Eingabe cmd.exe als anderer Nutzer)

PS: mir ist bewusst, dass es auf der ollen Kommandozeile per runas /smartcard sehr wohl auch geht, jedoch hat das noch gravierendere Nachteile: es kommt nicht mit mehreren Credentials pro SmartCard klar.

Content-ID: 2434101143

Url: https://administrator.de/contentid/2434101143

Ausgedruckt am: 19.12.2024 um 12:12 Uhr

lcer00
lcer00 07.04.2022 um 20:36:08 Uhr
Goto Top
Hallo,

ich weiß nicht ob das hilft, aber des normale Vorgehen bei Smartcard Credentials mit dem Yubikey wäre ja folgendes:

$cred = get-Credential
Mache-irgendwas -Credential $cred

Wobei nach Zeile 1 der Auswahldialog kommt und erst beim ausführen von Zeile 2 der Yubikey blinkt und nach Tastendruck die Kryptographieberechnung erfolgt. Vielleicht funktioniert mit Deinem Skript bzw. Reader dieses „asynchrone“ Verhalten nicht.

Grüße

lcer
DerWoWusste
DerWoWusste 08.04.2022 um 10:30:10 Uhr
Goto Top
Hi.

Der Yubikey läuft doch. Es geht hier um Geräte mit eingebautem PIN-Pad, bei denen das PIN-Pad auf diese Weise nicht angesteuert werden kann.
DerWoWusste
Lösung DerWoWusste 13.04.2022 um 17:20:38 Uhr
Goto Top
LOL.

Lösung gefunden: einfach das obige Skript benutzen, PIN-Eingabe leer lassen und Enter drücken, dann aktiviert sich das PIN-Pad automatisch zur Benutzung!