Status: DRAFT

Windows Client Configuration

The steps that must be run on the Windows client must be run by the user(s) that need to connect to the server.

Making Sure the Server Hostname and Server Certificate Subject Match

It is important that the server hostname that you use matches the subject of the certificate. If they don not match the client will refuse to make a connection.

If the server hostname cannot be mapped to an IP address by the DNS or WINS server on your network, you need to add a mapping to an IP address to C:\Windows\System32\drivers\etc\hosts manually. This file can only be changed by an administrator.

Ensuring the Client Uses Secure Authentication Methods

This step changes machine-global settings that affects all users of the machine. Make sure you do not break somebody else's setup.

By default almost all authentication methods are enabled for the WinRM client. Basic authentication sends the password to the server, which is always undesirable as a malicious or hacked server can use the password for other purposes. Credential security support provider (CredSSP) authentication behaves similarly to basic authentication and will send your credentials to the server, so it should be left disabled if possible. Digest authentication does not provide the strong security of certificates and is not supported by (modern) WinRM servers.

The minimal set of authentication methods to enable is certificate and negotiate authentication. Certificate authentication is needed to allow clients to authenticate using certificates. Negotiate authentication is needed to be able to (amongst others) configure WinRM using the winrm command.

If you have an environment with Active Directory Domain Services, you very likely want to leave Kerberos authentication enabled.

The settings can only be changed if the WinRM service is running. Check if it is already running with the following command:

PS > Get-Service WinRM

If the WinRM service is not running, start it using:

PS > (Get-Service WinRM).Start()

Now set the authentication that the client is allowed to use:

PS > winrm set WinRM/Config/Client/Auth '@{Basic="false";Digest="false";Kerberos="false";Negotiate="true";Certificate="true";CredSSP="false"}'

Install Certificate Authority Certificate of the Server

In this step the server certificate is retrieved, but it cannot be verified using the existing known certificate authorities. Verify the thumbprint manually to ensure that you are installing the correct server certificate as a certificate authority. After installing the certificate as a certificate authority, all certificates signed by that certificate will be accepted without user notification or confirmation.

Note that the installed certificate is not the only certificate that can identify the server hostname. If somebody can get a certificate authority (that is installed in the client certificate store) to create a certificate for the same server hostname, the client will successfully verify a malicious server that uses the server hostname. If you don not trust certificate authorities (there are reasons to not trust them), you need to pin the server certificate. This is possible but quite cumbersome, and will not be covered by this manual.

The certificate of the server can simply be downloaded by sending an HTTP request to the WinRM HTTPS endpoint on the server. The request will fail (the error is caught and ignored) because the server certificate cannot be verified, but the request will contain the unverifiable certificate afterwards. The thumbprint of the retrieved certificate is verified manually below:

PS > $webRequest = [Net.WebRequest]::Create("https://<server hostname>:5986/wsman")
PS > try { $webRequest.GetResponse() } catch {} # Catch and ignore the certificate error
PS > $serverCert = $webRequest.ServicePoint.Certificate

Output the thumbprint of the certificate and verify that it matches the thumbprint of the certificate generated in the server configuration chapter:

PS > $serverCert.GetCertHashString()

If the server certificate is correct, you can add it as a certificate authority to your certificate store. The PowerShell path of the certificate store location where the certificate will be stored is Cert:\CurrentUser\Root\. A confirmation dialog will appear for the third command:

PS > $store = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList "Root", "CurrentUser"
PS > $store.Open('ReadWrite')
PS > $store.Add($serverCert)
PS > $store.Close()

Create Certificate for the Client

Just like the server the client needs a certificate, so the server can verify the identity of the client. Unlike on the server, the standard PowerShell cmdlet can be used to generate the client certificate.

The following text extension will be set in the created certificate:

Every certificate identifies a subject. The subject name can be anything. To make identifying certificates easier, you can, for example, include the client hostname or the certificate purpose. A good value for the subject name is your Windows username or Microsoft Account email address:

PS > $winClientCert = New-SelfSignedCertificate -Type Custom -Subject <subject> -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2","2.5.29.17={text}upn=<subject>") -KeyUsage DigitalSignature,KeyEncipherment -CertStoreLocation Cert:\CurrentUser\My\

The thumbprint is going to be needed to verify whether the correct public key has been downloaded by the server, so display it and copy it somewhere:

PS > $winClientCert

This certificate can be used to authenticate against multiple servers. There is no need to create a client certificate for each server to be accessed using WinRM.

Export the (public part) of the certificate to a file, so it can be transferred to the server. For <fingerprint> you can use $($winClientCert.Thumbprint):

PS > Export-Certificate -Cert Cert:\CurrentUser\My\<fingerprint> -FilePath <export file path>.crt

Install Certificate of the Client

In this step the client certificate is copied to the server, but it cannot be verified that the certificate was not modified during transit. Verify the thumbprint manually to ensure that you are installing the correct client certificate as a certificate authority and trusted person. After installing the certificate as a certificate authority, all certificates signed by that certificate will be accepted without user notification or confirmation.

Copy the exported client certificate to the server. You can use insecure methods to do this, because the certificate only contains a public key. As the name implies, having other people know this key does not pose a security risk.

Once the client certificate is on the server, load it using:

PS > $winClientCert = Get-PfxCertificate <certificate file path>

Output the thumbprint of the certificate and verify that it matches the thumbprint of the certificate generated above:

PS > $winClientCert.GetCertHashString()

If the client certificate is correct, you can add it as a certificate authority and a trusted person to the certificate store of the server. The reason the certificate also has to be installed as a certificate authority, is that this enables the server to verify the copy of the certificate installed as a trusted person:

PS > Import-Certificate -FilePath <certificate file path> -CertStoreLocation Cert:\LocalMachine\Root
PS > Import-Certificate -FilePath <certificate file path> -CertStoreLocation Cert:\LocalMachine\TrustedPeople

Attach Client Certificate to User

Now the server only needs to know as which user the WinRM session must run when it is authenticated using the client certificate. You can use an existing user or create a user specifically for WinRM sessions.

Add the client certificate to the WinRM client certificate store. The subject that the client will use during WinRM authentication, and the credentials of the local user to use on successful authentication, will be associated with the certificate.

In the dialog asking for credentials, enter the credentials of the local user as which the WinRM sessions must be run:

PS > New-Item -Path WSMan:\localhost\ClientCertificate -Subject '<subject>' -URI * -Issuer ((Get-PfxCertificate <certificate file path>).Thumbprint) -Credential (Get-Credential)

Test Connection on the Client

Everything should be set up correctly now. There is a cmdlet specifically for testing if everything is working as expected. By using the parameter -Authentication ClientCertificate we force the use of HTTPS:

PS > Test-WSMan -ComputerName <server hostname> -Authentication ClientCertificate -CertificateThumbprint <client certificate thumbprint>

You should see output similar to:

wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor   : Microsoft Corporation
ProductVersion  : OS: 10.0.14393 SP: 0.0 Stack: 3.0

If the previous command was successful you can open a PowerShell session on the server:

PS > $session = New-PSSession -ComputerName <server hostname> -CertificateThumbprint <client certificate thumbprint>
PS > Enter-PSSession -Session $session

The prompt should change and should now have a prefix containing the server hostname. Execute any command you want. When you are done, exit the session using exit:

[<server hostname>]: PS > exit

Finally clean up the PowerShell session on the client machine:

PS > Remove-PSSession -Session $session

Congratulations, you have successfully set up a fully certificate-based WinRM system.