How to use WSMan config provider for certificate authentication

WSMan Client certificate authentication is primarily used in non-domain cases: client can specify certain certificate as credential, after authentication each other, it’s mapped to a local account on the server, meaning the WinRM service runs under the context of the mapped account when processing the request. When using client certificates, multiple client certificates can be mapped to a single account using pattern matching rules

 

In this blog, we discuss three topics:

• Enable config settings for certificate auth

• Configure cert mapping entry on the server machine

• Perform remote operation using certificate auth on the client machine

 

1) Enable config settings for certificate auth

There’re specific config settings that control whether WinRM client and service will support Certificate Based Authentication

1.a) Client stack support for certificate auth is enabled by default, so you don’t need to change it if Certificate Based Authentication is desired

PS C:\Windows\system32> dir WSMan:\localhost\Client\Auth\Certificate

   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Client\Auth

Name Value Type

---- ----- ----

Certificate true System.String

 

1.b) Service support for certificate auth is disabled by default to reduce attack surface, so you’ll have to explicitly enable it if Certificate Based Authentication is desired

PS C:\Windows\system32> dir WSMan:\localhost\Service\Auth\Certificate

   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Service\Auth

Name Value Type

---- ----- ----

Certificate false System.String

PS C:\Windows\system32> set-item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true

 

2) Configure cert mapping entry

There are no entries in the certificate mapping table by default, but you can use certificate mapping resource URI "https://schemas.microsoft.com/wbem/wsman/1/config/service/certmapping" to perform get, put, create, delete, and enumerate operations using WSMan-related PS cmdlet such as “Get-WSManInstance, Set-WSManInstance, New-WSManInstance, Remove-WSManInstance”, but here in this blog I show examples using WSMan config provider instead as demonstrated by the following traversal.

PS C:\Windows\system32> cd WSMan:\localhost\ClientCertificate

PS WSMan:\localhost\ClientCertificate>

 

Before configuring cert mapping entries on the server machine, let’s look at some of the key properties in the cert mapping entry

Subject : it’s the Subject field or Subject Alternative Name (SAN) field of client certificate, which contains the name of the entity that the certificate was issued to, first “*” character could match anything. For example:

o If the certificate is issued to a user: then it contains the username in the form “Principal Name=user@domain.com”

o If the certificate is issued to a machine: then it contains the name of the machine in the form “DNS Name=machine.foo.com”

Issuer: it’s the certificate thumbprint of the CA that issued the certificate. The certificate identified by the thumbprint must be present in the server machine “CA” store. The certificate must have key usage that allows it to sign other certificates (CERT_KEY_CERT_SIGN_KEY_USAGE), unless it’s a self-signed certificate in which case the key usage is not required

URI: it’s the URI or URI prefix for which this cert mapping will apply, last “*” character could match anything

 

2.1) Create cert mapping entry if the entry does not already exist, following PS script automates this process: first find user certificate which has the usage “Client Authentication (1.3.6.1.5.5.7.3.2)” as this is required for subsequent client cert auth; then get ISSUER name for the above user certificate, get the ISSUER thumbprint, which was used in the last line to create cert mapping entry, so remote access to WinRM using client certificates is mapped to user “certAdminAccount1” on the server machine. Please note if this local user is not in “administrators” group, you need to configure security to allow non-admin through but this is out of the scope of this blog, We’ll discuss “How to configure security for different URI” in a separate blog.

foreach ($cert in get-childitem cert:\CurrentUser\My)

{

    foreach ($ext in $cert.Extensions)

    {

        if ($ext.Oid.FriendlyName -eq "Enhanced Key Usage")

        {

            $ekey = [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]$ext

            $oids = $ekey.EnhancedKeyUsages

            foreach ($oid in $oids)

            {

                if ($oid.FriendlyName -eq "Client Authentication")

          {

                    $usercerts=$cert

                }

            }

        }

    }

}

if ($usercerts.count -eq $null) {$issuername = (, $usercerts)[0].Issuer} else {$issuername = $usercerts[0].Issuer}

$IssuerThumbprint=(get-childitem -path cert:\CurrentUser\ca | where-object { $_.Subject -eq $issuername }).Thumbprint

net user certAdminAccount1 /delete

net user certAdminAccount1 Dummy_pswd /add

net localgroup administrators certAdminAccount1 /add

$password = ConvertTo-SecureString Dummy_pswd -AsPlainText –Force

$adminuser = New-Object System.Management.Automation.PSCredential certAdminAccount1,$password

New-Item -Path WSMan:\localhost\ClientCertificate -URI https://microsoft.test.mig.wsman/\* -Subject *.com -Issuer $IssuerThumbprint -Credential $adminuser -force

 

Output is similar to the following

   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\ClientCertificate

Name Type Keys

---- ---- ----

ClientCertificate_6047... Container {URI=https://microsoft.test.mig.wsman/*, Issuer=1B3FD224D66C6413FE20D2...

2.2) Get cert mapping entry with specific keys if the entry exists

PS C:\Windows\System32> Get-Item -Path WSMan:\localhost\ClientCertificate\ClientCertificate_604778647\* -force

   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\ClientCertificate\ClientCertificate_604778647

Name Value Type

---- ----- ----

URI https://microsoft.test.mig.wsman/\* System.String

Subject *.com System.String

Issuer 1B3FD224D66C6413FE20D21E38B304226D192DFE System.String

UserName certAdminAccount1 System.String

Enabled true System.String

Password System.String

2.3) change any non-keys of cert mapping entry if the entry already exists [The Issuer, Subject, and URI properties are keys for the purposes of WS-Transfer operations.]

PS C:\Windows\System32> Set-Item WSMan:\localhost\ClientCertificate\ClientCertificate_604778647\Enabled $true -force

2.4) Enumerate will list all existing cert mapping entries

PS C:\Windows\System32> Get-Item -Path WSMan:\localhost\ClientCertificate\* -force

   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\ClientCertificate

Name Type Keys

---- ---- ----

ClientCertificate_6047... Container {URI=https://microsoft.test.mig.wsman/*, Issuer=1B3FD224D66C6413FE20D2...

2.5) Delete cert mapping entry if the entry exists

PS C:\Windows\System32> Remove-Item -Path WSMan:\localhost\ClientCertificate\ClientCertificate_* -Recurse -force

 

3) Perform remote operation using certificate auth on the client machine

In the above first step, you enable both client and server config settings for certificate auth. In the second step, you configure cert mapping entry on the server machine that defines the actual mapping behavior. So here’s the final step on how to perform remote operation using certificate auth. These demo scripts in this blog may need to be changed a little bit to make it work for yourself such as valid URI, valid server name etc…

3.1) On the server machine you must have https listener and port is opened for through traffic, as client certificate authentication is allowed only when connecting to a https address.

PS D:\Windows\system32> Set-WSManQuickConfig -UseSSL -Force

WinRM already is set up to receive requests on this machine.

WinRM has been updated for remote management.

Created a WinRM listener on HTTPS://* to accept WS-Man requests to any IP on this machine.

Configured CertificateThumbprint setting for the service.

PS D:\Windows\system32> netsh advfirewall firewall add rule name="Port 443" dir=in action=allow protocol=TCP localport=443

Ok.

3.2) On the client machine, following PS script demonstrates the process to perform a remote GET operation using certificate auth: first find user certificate which has the usage “Client Authentication (1.3.6.1.5.5.7.3.2)” as this is required for client cert auth; its thumbprint was used in the last line as the client credential

foreach ($cert in get-childitem cert:\CurrentUser\My)

{

    foreach ($ext in $cert.Extensions)

    {

        if ($ext.Oid.FriendlyName -eq "Enhanced Key Usage")

        {

            $ekey = [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]$ext

            $oids = $ekey.EnhancedKeyUsages

            foreach ($oid in $oids)

            {

                if ($oid.FriendlyName -eq "Client Authentication")

                {

                    $usercertThumbprint=$cert.Thumbprint

                }

            }

        }

    }

}

winrm g https://microsoft.test.mig.wsman/test -r:https://machine.STBTEST.MICROSOFT.COM -a:certificate -certificate:$usercertThumbprint