[{"data":1,"prerenderedAt":337},["ShallowReactive",2],{"post-why-rdp-cant-authenticate-like-ssh":3,"post-next-why-rdp-cant-authenticate-like-ssh":325,"post-nav-why-rdp-cant-authenticate-like-ssh":326},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"heading":8,"date":10,"minutes":11,"tag":12,"summary":13,"short":14,"body":15,"_type":319,"_id":320,"_source":321,"_file":322,"_stem":323,"_extension":324},"\u002Fblog\u002Fwhy-rdp-cant-authenticate-like-ssh","blog",false,"","Why RDP can't authenticate like SSH","SSH solved passwordless, short-lived, identity-attributed access a decade ago, and the server side of the entire solution is one line of sshd_config. RDP can be pushed to roughly the same place. I spent real time mapping out how, and the map is the point of this post: the path exists, every step of it is documented, and the sheer stack of steps explains why almost nobody walks it, and why the direction that actually wins does not involve fixing RDP authentication at all.","2026-07-03",6,"Protocols","SSH made passwordless, short-lived, identity-attributed access a one-line server config. RDP can get there too, through a chain of Active Directory machinery that explains why brokered access is winning instead.","SSH certificates give you passwordless, short-lived, identity-attributed access with one sshd_config directive. RDP can reach roughly the same place, but only via PKINIT, an enterprise CA in the NTAuth store, SID-mapped certificates, and smartcard emulation. The asymmetry is structural, and it is why the broker that terminates the protocol is the model that actually wins.",{"type":16,"children":17,"toc":311},"root",[18,35,42,47,65,78,84,89,94,100,105,126,138,269,274,280,285,291,296,301,306],{"type":19,"tag":20,"props":21,"children":22},"element","p",{},[23,26,33],{"type":24,"value":25},"text","SSH solved passwordless, short-lived, identity-attributed access a decade ago, and the server side of the entire solution is one line of ",{"type":19,"tag":27,"props":28,"children":30},"code",{"className":29},[],[31],{"type":24,"value":32},"sshd_config",{"type":24,"value":34},". RDP can be pushed to roughly the same place. I spent real time mapping out how, and the map is the point of this post: the path exists, every step of it is documented, and the sheer stack of steps explains why almost nobody walks it, and why the direction that actually wins does not involve fixing RDP authentication at all.",{"type":19,"tag":36,"props":37,"children":39},"h2",{"id":38},"what-ssh-got-right",[40],{"type":24,"value":41},"What SSH got right",{"type":19,"tag":20,"props":43,"children":44},{},[45],{"type":24,"value":46},"Authentication in SSH is a clean protocol phase, a distinct exchange that completes before anything else exists, owned entirely by the protocol. That is what made it replaceable. The certificate model that grew out of it works like this: you run a user CA, a single keypair whose public half every server trusts. When an engineer needs access, something they have already authenticated to (in practice, your identity provider fronting a signing service) issues a certificate valid for a few minutes, listing the principals they may log in as. The server checks the signature, the validity window, and the principal against the account.",{"type":19,"tag":20,"props":48,"children":49},{},[50,56,58,63],{"type":19,"tag":27,"props":51,"children":53},{"className":52},[],[54],{"type":24,"value":55},"TrustedUserCAKeys",{"type":24,"value":57}," in ",{"type":19,"tag":27,"props":59,"children":61},{"className":60},[],[62],{"type":24,"value":32},{"type":24,"value":64}," is the entire server-side deployment. No per-user key distribution, no authorized_keys sprawl, and revocation stops being a process because expiry is the revocation, the certificate dies before the meeting where you would have discussed revoking it ends.",{"type":19,"tag":20,"props":66,"children":67},{},[68,70,76],{"type":24,"value":69},"The audit story falls out for free. The certificate carries a key ID naming the human, so even when five engineers share the ",{"type":19,"tag":27,"props":71,"children":73},{"className":72},[],[74],{"type":24,"value":75},"deploy",{"type":24,"value":77}," account, the server log says which of the five connected. Short-lived, passwordless, identity-attributed. One page of config.",{"type":19,"tag":36,"props":79,"children":81},{"id":80},"the-login-before-the-login",[82],{"type":24,"value":83},"The login before the login",{"type":19,"tag":20,"props":85,"children":86},{},[87],{"type":24,"value":88},"RDP's problem starts with what RDP is. It is a display protocol, its job is to ship a Windows desktop over the wire, and classic RDP did the natural thing: connect, spin up a session, let winlogon render the credential prompt inside the remote desktop, and send you the pixels. The remote host built a full session for a user who had not yet proven anything. That was a resource problem, session setup is expensive and anonymous connections could exhaust it, and it was an attack surface, everything involved in session creation was reachable before authentication.",{"type":19,"tag":20,"props":90,"children":91},{},[92],{"type":24,"value":93},"Network Level Authentication fixed it. NLA, carried by CredSSP, requires the client to authenticate before the session exists, the login before the login. It was the right fix and it came with a bill: CredSSP does not do authentication itself, it wraps Windows authentication, which means passwords, NTLM, or Kerberos. The moment RDP got serious about authenticating early, it bound itself to the operating system's identity machinery.",{"type":19,"tag":36,"props":95,"children":97},{"id":96},"the-passwordless-path-rdp-does-have",[98],{"type":24,"value":99},"The passwordless path RDP does have",{"type":19,"tag":20,"props":101,"children":102},{},[103],{"type":24,"value":104},"Windows does have certificate logon, and it predates the SSH CA fashion: the smartcard model, Kerberos PKINIT underneath. So in principle you can build the same thing, a service that mints a short-lived certificate after the user authenticates to your identity provider, and the user logs into RDP with no password ever typed. Here is what \"in principle\" expands to.",{"type":19,"tag":20,"props":106,"children":107},{},[108,110,116,118,124],{"type":24,"value":109},"You need an enterprise CA that the domain trusts for logon, which means its certificate published into the forest's NTAuth store, not just the usual root stores. The client certificate needs the user's UPN in the subject alternative name, as the ",{"type":19,"tag":27,"props":111,"children":113},{"className":112},[],[114],{"type":24,"value":115},"szOID_NT_PRINCIPAL_NAME",{"type":24,"value":117}," otherName. Since the KB5014754 enforcement changes it also needs the user's SID embedded in a dedicated extension, ",{"type":19,"tag":27,"props":119,"children":121},{"className":120},[],[122],{"type":24,"value":123},"szOID_NTDS_CA_SECURITY_EXT",{"type":24,"value":125},", because UPN mapping alone is now classed as weak and enforcement mode rejects certificates without a strong mapping. The certificate needs the Smartcard Logon EKU. And then the client has to present all of this as a smartcard, because Windows certificate logon is smartcard logon; that means a physical card and reader, or smartcard emulation, a virtual card behind a minidriver, so that the credential stack sees something card-shaped.",{"type":19,"tag":20,"props":127,"children":128},{},[129,131,136],{"type":24,"value":130},"Every step in that chain is documented. None of it is exotic, organizations that issue physical smartcards run all of it today. But put the whole stack next to ",{"type":19,"tag":27,"props":132,"children":134},{"className":133},[],[135],{"type":24,"value":55},{"type":24,"value":137},":",{"type":19,"tag":139,"props":140,"children":141},"table",{},[142,164],{"type":19,"tag":143,"props":144,"children":145},"thead",{},[146],{"type":19,"tag":147,"props":148,"children":149},"tr",{},[150,154,159],{"type":19,"tag":151,"props":152,"children":153},"th",{},[],{"type":19,"tag":151,"props":155,"children":156},{},[157],{"type":24,"value":158},"SSH",{"type":19,"tag":151,"props":160,"children":161},{},[162],{"type":24,"value":163},"RDP",{"type":19,"tag":165,"props":166,"children":167},"tbody",{},[168,192,215,233,251],{"type":19,"tag":147,"props":169,"children":170},{},[171,177,187],{"type":19,"tag":172,"props":173,"children":174},"td",{},[175],{"type":24,"value":176},"Trust anchor",{"type":19,"tag":172,"props":178,"children":179},{},[180,182],{"type":24,"value":181},"One CA public key in ",{"type":19,"tag":27,"props":183,"children":185},{"className":184},[],[186],{"type":24,"value":32},{"type":19,"tag":172,"props":188,"children":189},{},[190],{"type":24,"value":191},"Enterprise CA published to the NTAuth store",{"type":19,"tag":147,"props":193,"children":194},{},[195,200,205],{"type":19,"tag":172,"props":196,"children":197},{},[198],{"type":24,"value":199},"Identity in the credential",{"type":19,"tag":172,"props":201,"children":202},{},[203],{"type":24,"value":204},"Principals list in the cert",{"type":19,"tag":172,"props":206,"children":207},{},[208,210],{"type":24,"value":209},"UPN in the SAN plus SID in ",{"type":19,"tag":27,"props":211,"children":213},{"className":212},[],[214],{"type":24,"value":123},{"type":19,"tag":147,"props":216,"children":217},{},[218,223,228],{"type":19,"tag":172,"props":219,"children":220},{},[221],{"type":24,"value":222},"Certificate profile",{"type":19,"tag":172,"props":224,"children":225},{},[226],{"type":24,"value":227},"Anything the CA signs",{"type":19,"tag":172,"props":229,"children":230},{},[231],{"type":24,"value":232},"Smartcard Logon EKU, strong-mapping rules",{"type":19,"tag":147,"props":234,"children":235},{},[236,241,246],{"type":19,"tag":172,"props":237,"children":238},{},[239],{"type":24,"value":240},"Client presentation",{"type":19,"tag":172,"props":242,"children":243},{},[244],{"type":24,"value":245},"A file the ssh client reads",{"type":19,"tag":172,"props":247,"children":248},{},[249],{"type":24,"value":250},"A smartcard, physical or emulated",{"type":19,"tag":147,"props":252,"children":253},{},[254,259,264],{"type":19,"tag":172,"props":255,"children":256},{},[257],{"type":24,"value":258},"What validates it",{"type":19,"tag":172,"props":260,"children":261},{},[262],{"type":24,"value":263},"sshd",{"type":19,"tag":172,"props":265,"children":266},{},[267],{"type":24,"value":268},"The KDC, LSA, winlogon, and the credential provider stack",{"type":19,"tag":20,"props":270,"children":271},{},[272],{"type":24,"value":273},"That table is why \"SSH certificates for RDP\" is a whiteboard sentence and a quarter-long project.",{"type":19,"tag":36,"props":275,"children":277},{"id":276},"structural-not-negligence",[278],{"type":24,"value":279},"Structural, not negligence",{"type":19,"tag":20,"props":281,"children":282},{},[283],{"type":24,"value":284},"It is tempting to read the chain as negligence. It is not. SSH is one protocol owned end to end, authentication included, so OpenSSH could add certificates in a single release (5.4, in 2010) and be done. RDP made the opposite choice at birth: it delegates authentication to the operating system's identity system, and in exchange it gets single sign-on into the whole desktop, the session token, the file shares, everything Kerberos reaches. The price is that changing how you log in to RDP means changing Active Directory's mind, not the protocol's. There is no per-host escape hatch, no one-line directive for a single server, because the server was never the thing doing the deciding.",{"type":19,"tag":36,"props":286,"children":288},{"id":287},"where-the-access-actually-goes",[289],{"type":24,"value":290},"Where the access actually goes",{"type":19,"tag":20,"props":292,"children":293},{},[294],{"type":24,"value":295},"Now the flip. If parity requires a directory-wide PKI project, the winning move is not to run that project against every desktop, it is to run it once, behind a broker.",{"type":19,"tag":20,"props":297,"children":298},{},[299],{"type":24,"value":300},"A broker sits between the user and the host and terminates the protocol, and increasingly it renders the session into a browser tab, which is the shape the ZTNA reference implementations all converged on. The user authenticates to the broker with modern identity: SSO, hardware-backed MFA, short sessions. The broker carries the baroque part centrally, it does the PKINIT dance against the domain or holds the session credential and injects it, and the host sees a perfectly ordinary smartcard or NLA logon. The user gets the SSH experience, click, authenticated, in. And the audit log gets the real human identity, recorded at the broker, no matter which shared local admin account the session lands on.",{"type":19,"tag":20,"props":302,"children":303},{},[304],{"type":24,"value":305},"This is not hypothetical for me. I shipped browser-based SSH access built on exactly the CA-signed short-lived certificate model above, and sitting down to map the same design onto RDP is what made the asymmetry concrete: on the SSH side the design was mostly finished when the introduction was, on the RDP side the introduction was where the project started.",{"type":19,"tag":20,"props":307,"children":308},{},[309],{"type":24,"value":310},"So the practical version. If you already run a real smartcard deployment, RDP certificate logon is closer than you think, finish the KB5014754 strong-mapping work and use what you have. If you do not, do not start an enterprise PKI project just to get rid of RDP passwords. Put a broker in front, terminate the protocol there, let the broker hold the Windows machinery, and spend your effort on the part that generalizes: the identity of the human, attested once, attached to every session in the log.",{"title":7,"searchDepth":312,"depth":312,"links":313},2,[314,315,316,317,318],{"id":38,"depth":312,"text":41},{"id":80,"depth":312,"text":83},{"id":96,"depth":312,"text":99},{"id":276,"depth":312,"text":279},{"id":287,"depth":312,"text":290},"markdown","content:blog:why-rdp-cant-authenticate-like-ssh.md","content","blog\u002Fwhy-rdp-cant-authenticate-like-ssh.md","blog\u002Fwhy-rdp-cant-authenticate-like-ssh","md",null,{"newer":327,"older":332},{"_path":328,"title":329,"heading":330,"date":331},"\u002Fblog\u002Fintroducing-memd","Introducing memd: memory your agents don't own","Introducing memd","2026-07-16",{"_path":333,"title":334,"heading":335,"date":336},"\u002Fblog\u002Fextension-ceiling-4-dlp-page-vs-wire","The extension ceiling, part 4: DLP on the page vs the wire","DLP on the page vs the wire","2026-06-20",1784222877864]