pythonpowershellsslcertificatewinrm

WinRM Certificate Authentication Fails with 401 Error on Windows Server 2022


I'm encountering an issue with WinRM certificate authentication on a Windows Server 2022 CIS STIGs image. The server is running the WinRM service, and I'm trying to connect from an Ubuntu Pro 20.04 FIPS client using pywinrm (v0.5.0). Both certificates are self-signed (ACM PCA) and the CA cert is added to the trusted stores on both Windows and Ubuntu.

Commands and Configuration:

On Windows Server 2022:

PS C:\Windows\system32> Get-ChildItem -Path Cert:\CurrentUser\Root | Where-Object { $_.Thumbprint -eq "5A2F4E63BEBEDB186ED84BF722B54207E6664469" } | Select-Object Subject, Thumbprint

Subject                                                                    Thumbprint
-------                                                                    ----------
L=TA, CN=example.com, S=TA, OU=RnD, O=Company, C=IL 5A2F4E63BEBEDB186ED84BF722B54207E6664469

PS C:\Windows\system32> winrm enumerate winrm/config/service/certmapping
CertMapping
    URI = *
    Subject = 50612F90702F2FEF1B777E987B7CD974DC99CE51
    Issuer = 5A2F4E63BEBEDB186ED84BF722B54207E6664469
    UserName = Administrator
    Enabled = true
    Password

PS C:\Windows\system32> winrm g winrm/config
Config
    MaxEnvelopeSizekb = 500
    MaxTimeoutms = 1800000
    MaxBatchItems = 32000
    MaxProviderRequests = 4294967295
    Client
        NetworkDelayms = 5000
        URLPrefix = wsman
        AllowUnencrypted = false [Source="GPO"]
        Auth
            Basic = false [Source="GPO"]
            Digest = false [Source="GPO"]
            Kerberos = true
            Negotiate = true
            Certificate = true
            CredSSP = false
        DefaultPorts
            HTTP = 5985
            HTTPS = 5986
        TrustedHosts
    Service
        RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
        MaxConcurrentOperations = 4294967295
        MaxConcurrentOperationsPerUser = 1500
        EnumerationTimeoutms = 240000
        MaxConnections = 300
        MaxPacketRetrievalTimeSeconds = 120
        AllowUnencrypted = false [Source="GPO"]
        Auth
            Basic = false [Source="GPO"]
            Kerberos = true
            Negotiate = true
            Certificate = true
            CredSSP = true
            CbtHardeningLevel = Relaxed
        DefaultPorts
            HTTP = 5985
            HTTPS = 5986
        IPv4Filter = *
        IPv6Filter = *
        EnableCompatibilityHttpListener = false
        EnableCompatibilityHttpsListener = true
        CertificateThumbprint
        AllowRemoteAccess = true
    Winrs
        AllowRemoteShellAccess = true
        IdleTimeout = 7200000
        MaxConcurrentUsers = 2147483647
        MaxShellRunTime = 2147483647
        MaxProcessesPerShell = 2147483647
        MaxMemoryPerShellMB = 1024
        MaxShellsPerUser = 2147483647

PS C:\Windows\system32> winrm g winrm/config/service
Service
    RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
    MaxConcurrentOperations = 4294967295
    MaxConcurrentOperationsPerUser = 1500
    EnumerationTimeoutms = 240000
    MaxConnections = 300
    MaxPacketRetrievalTimeSeconds = 120
    AllowUnencrypted = false [Source="GPO"]
    Auth
        Basic = false [Source="GPO"]
        Kerberos = true
        Negotiate = true
        Certificate = true
        CredSSP = true
        CbtHardeningLevel = Relaxed
    DefaultPorts
        HTTP = 5985
        HTTPS = 5986
    IPv4Filter = *
    IPv6Filter = *
    EnableCompatibilityHttpListener = false
    EnableCompatibilityHttpsListener = true
    CertificateThumbprint = 13F3C1844B7617270D1331BEB02AD347FAB74D9C
    AllowRemoteAccess = true

On Ubuntu Pro 20.04:

$ cert_path="/etc/nginx/certs/controller.example.com.crt.pem"
$ thumbprint=$(openssl x509 -in "$cert_path" -noout -fingerprint -sha1 | sed 's/://g' | awk -F= '{print $2}')
$ echo $thumbprint
50612F90702F2FEF1B777E987B7CD974DC99CE51

$ issuer_thumbprint=$(tac /etc/ssl/certs/ca-certificates.crt | awk 'BEGIN {c=0} /END CERTIFICATE/ {c++} {if (c==1) print}' | tac | openssl x509 -in /dev/stdin  -noout -fingerprint -sha1 | sed 's/://g' | awk -F= '{print $2}')
$ echo $issuer_thumbprint
5A2F4E63BEBEDB186ED84BF722B54207E6664469

Connection Test:

I'm testing the connection using pywinrm with HTTPS and certificate authentication. Here is the command and the error message:

$ python -c "import winrm; winrm.Session('https://winrm.example.com:5986/wsman', auth=(None, None), transport='certificate', cert_key_pem='/etc/nginx/certs/controller.example.com.key.pem', cert_pem='/etc/nginx/certs/controller.example.com.crt.pem', server_cert_validation='validate', ca_trust_path='/etc/ssl/certs/ca-certificates.crt').run_cmd('ipconfig', ['/all']).std_out.decode()"
Traceback (most recent call last):
  File "/opt/venv3.11/lib/python3.11/site-packages/winrm/transport.py", line 342, in _send_message_request
    response.raise_for_status()
  File "/opt/venv3.11/lib/python3.11/site-packages/requests/models.py", line 1024, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error:  for url: https://winrm.example.com:5986/wsman

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/opt/venv3.11/lib/python3.11/site-packages/winrm/__init__.py", line 44, in run_cmd
    shell_id = self.protocol.open_shell()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv3.11/lib/python3.11/site-packages/winrm/protocol.py", line 193, in open_shell
    res = self.send_message(xmltodict.unparse(req))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv3.11/lib/python3.11/site-packages/winrm/protocol.py", line 263, in send_message
    resp = self.transport.send_message(message)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv3.11/lib/python3.11/site-packages/winrm/transport.py", line 336, in send_message
    response = self._send_message_request(session, prepared_request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv3.11/lib/python3.11/site-packages/winrm/transport.py", line 346, in _send_message_request
    raise InvalidCredentialsError("the specified credentials were rejected by the server")
winrm.exceptions.InvalidCredentialsError: the specified credentials were rejected by the server

WinRM Service Logs: On the WinRM service, I see the following log entry:

PS C:\Windows\system32> Get-WinEvent -LogName "Microsoft-Windows-WinRM/Operational" -MaxEvents 1 | Format-List *

Message              : The authorization of the user failed with error 5
Id                   : 192
Version              : 0
Qualifiers           :
Level                : 4
Task                 : 8
Opcode               : 0
Keywords             : 4611686018427387916
RecordId             : 5435
ProviderName         : Microsoft-Windows-WinRM
ProviderId           : a7975c8f-ac13-49f1-87da-5a984a4ab417
LogName              : Microsoft-Windows-WinRM/Operational
ProcessId            : 3320
ThreadId             : 3744
MachineName          : winrm
UserId               : S-1-5-20
TimeCreated          : 4/8/2025 8:44:41 PM
ActivityId           : a3ddc236-a84c-0004-90ed-dda34ca8db01
RelatedActivityId

Please help me understand why do I fail connect using certificate auth option (TLS handshake looks good. also confirmed directly with openssl s_client ... getting verification OK).

Any suggestions / ideas please! Thanks!!


Solution

  • Found the bug...

    The CertMapping.Subject should include the actual Subject CN of the client certificate, and not the fingerprint.