During our recent infrastructure audit for a mid-sized fintech firm in Bengaluru, we observed a recurring vulnerability: the proliferation of "orphan" SSH keys. In environments where developers and third-party contractors frequently rotate, the manual management of ~/.ssh/authorized_keys files inevitably fails. We found instances where former employees still held active public keys on production servers, a direct violation of the DPDP Act 2023 requirements for strict team SSH access management.
What is SSH Certificate Authentication?
SSH Certificate Authentication is not the same as SSL/TLS X.509 certificates used for web servers. While both use asymmetric cryptography, SSH certificates utilize a proprietary OpenSSH format that is significantly more lightweight. In this model, we introduce a Certificate Authority (CA)—a trusted key pair used to sign other keys.
Instead of copying a user's public key to every target server, we configure the server to trust the CA's public key. When a user presents a signed certificate, the server verifies the CA's signature and grants access based on the metadata embedded within the certificate, such as the username and expiration date.
SSH Key Authentication vs. SSH Certificate Authentication: Key Differences
- Trust Mechanism: Standard keys require the public key to be pre-positioned on every target. Certificates require only the CA's public key to be present on the target.
- Expiration: Standard keys are valid until manually deleted. Certificates have built-in "Valid After" and "Valid Before" timestamps.
- Identity: Certificates contain an identity string (e.g., an email or employee ID), making logs significantly more useful for forensic audits and SIEM monitoring required by CERT-In.
- Revocation: Revoking a standard key requires modifying every server. Revoking a certificate can be handled via a centralized Key Revocation List (KRL).
The Benefits of Using Certificates Over Standard SSH Pubkey Authentication
We recommend moving to certificates primarily to eliminate the "authorized_keys bloat." In a cluster of 500 Ubuntu 22.04 instances, updating a single user's access via standard keys requires an Ansible playbook run across the entire fleet. With certificates, we simply stop signing new keys for that user. For more details on securing these environments, see our guide on Hardening SSH and Terminal Access.
Furthermore, certificates mitigate the risk of "Host Key Verification Failed" warnings. By issuing host certificates, we ensure that when a developer connects to a new instance, the authenticity of the server is verified against the CA, preventing Man-in-the-Middle (MITM) attacks without requiring the user to manually accept fingerprints.
The Role of the Certificate Authority (CA)
The CA is a standard SSH key pair (we prefer Ed25519 for its performance and security). The private key of the CA must be kept offline or within a Hardware Security Module (HSM). If this key is compromised, the entire trust chain for your infrastructure is broken.
In our internal lab, we use a dedicated, hardened Ubuntu 22.04 instance as a signing node, isolated from the public internet. This node runs a small Python wrapper around the ssh-keygen utility to enforce signing policies.
Step-by-Step: How the Authentication Flow Works
- The user generates a standard SSH key pair on their local machine.
- The user sends their public key (
id_ed25519.pub) to the CA signing server. - The CA verifies the user's identity (e.g., via OIDC or LDAP).
- The CA signs the public key and generates a certificate (
id_ed25519-cert.pub). - The user connects to the target server, presenting both their private key and the certificate.
- The target server verifies the signature using the CA's public key and checks the certificate's constraints (expiration, principals).
Trust Models: User Certificates vs. Host Certificates
We distinguish between user certificates, which identify the person connecting, and host certificates, which identify the server. Host certificates are critical for preventing the "TOFU" (Trust On First Use) issue. When a host is provisioned, its public host key should be signed by the CA.
Signing a host key on the CA server
ssh-keygen -s /etc/ssh/ssh_ca -I "storage_node_01" -h -n "storage.internal.net" -V +52w /tmp/ssh_host_ed25519_key.pub
The -h flag specifies this is a host certificate, and -n defines the valid hostnames. This ensures that users only connect to verified infrastructure.
Setting Up a Certificate Authority on Linux
To begin, we generate the CA key pair on a secure machine. We avoid RSA and exclusively use Ed25519 to ensure resistance against modern cryptanalysis and to keep the certificate size small.
Generate the CA key pair
Use a strong passphrase for the private key
ssh-keygen -t ed25519 -f /etc/ssh/ssh_ca -C "Infrastructure CA 2024"
The resulting ssh_ca.pub is what we will distribute to all target servers. The ssh_ca private key must be protected with the highest level of security, as it is the root of trust for your SSH environment.
SSH Certificate Authentication for Ubuntu Systems
Once the CA is established, we sign a user's public key. In this example, we grant the user "ubuntu" access for one week. The -z flag adds a serial number, which is vital for tracking and revocation.
Sign a user public key
ssh-keygen -s /etc/ssh/ssh_ca \ -I "dev_user_01" \ -n ubuntu \ -V +1w \ -z 1001 \ id_ed25519.pub
We then verify the contents of the generated id_ed25519-cert.pub to ensure the constraints are correct:
Inspect the certificate
ssh-keygen -L -f id_ed25519-cert.pub
The output will display the public key, the signing CA, the identity (dev_user_01), the valid principals (ubuntu), and the validity period.
Configuring the SSH Daemon (sshd_config) for Certificate Support
On every Ubuntu 22.04 target server, we must inform sshd to trust our CA. We first copy the ssh_ca.pub to /etc/ssh/ and then modify the configuration.
Append the trust directive to sshd_config
echo "TrustedUserCAKeys /etc/ssh/ssh_ca.pub" >> /etc/ssh/sshd_config
We also recommend enforcing certificate use by disabling password auth
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config systemctl restart ssh
If you are using host certificates, you must also add the following line to /etc/ssh/sshd_config on the server to point to the signed host certificate:
Define the signed host certificate
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
Configuring SSH Certificate Authentication on Windows
Windows 10 and 11 now ship with a native OpenSSH client based on Win32-OpenSSH. The process for using certificates is identical to Linux. The client automatically looks for a file named [keyname]-cert.pub in the same directory as the private key.
If your private key is at C:\Users\Admin\.ssh\id_ed25519, simply place the signed certificate at C:\Users\Admin\.ssh\id_ed25519-cert.pub. The ssh command will automatically pick it up during the handshake.
Using SSH Certificate Authentication with PuTTY
PuTTY does not natively support OpenSSH certificates in the same way the command-line client does. To use certificates with PuTTY, we recommend using the Pageant agent from the PuTTY suite, or better yet, switching to KiTTY or the modern Windows Terminal with the native ssh.exe.
For teams strictly bound to PuTTY, you must use a version that supports the "SSH-2 public key with certificate" type, which often requires manual conversion of the certificate format or using a helper like ssh-agent.
Integrating with Windows OpenSSH Client
For enterprise-wide deployment on Windows, we use PowerShell to automate the placement of CA keys. This is particularly useful for Indian IT departments managing large fleets of developer laptops.
PowerShell snippet to trust a host CA
$caKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..." $knownHostsPath = "$env:USERPROFILE\.ssh\known_hosts" Add-Content -Path $knownHostsPath -Value "@cert-authority * $caKey"
This command tells the Windows SSH client that for any host (*), the provided CA key is a valid authority, eliminating host key prompts.
Centralizing Identity with SSH Certificate Authentication and FreeIPA
In larger Indian enterprises, we often integrate SSH CAs with FreeIPA or Active Directory. While FreeIPA manages the user identity, it can also act as the orchestrator for the CA. By linking the SSH certificate's -I (Identity) field to the LDAP uid, we create an immutable audit trail.
When a user authenticates, the sshd logs on Ubuntu will record the certificate identity rather than just the local username. This is essential for compliance with the DPDP Act, where demonstrating "who" accessed "what" data is a legal requirement.
Managing SSH Key Authentication for SFTP Workloads
Automated SFTP workloads are notorious for using static, non-passphrase-protected keys. We observed several Indian logistics firms where SFTP keys had not been rotated in over three years. By switching these workloads to certificates, we can enforce a 30-day rotation policy.
The automation script generates a new key, requests a signature from the CA with a 31-day validity, and replaces the old key. If the rotation script fails, the access naturally expires on day 31, preventing a "zombie" connection from persisting.
Automating Certificate Issuance and Rotation
We use a simple Python script to interface with our CA. This script validates the user's session against our internal SSO before calling ssh-keygen. Implementing such workflows is a core part of Hardening the Developer Terminal.
import subprocess import datetime
def sign_key(user_pub_key_path, identity, principals): # Set expiration to 8 hours for a standard workday validity = "+8h" cert_path = user_pub_key_path.replace(".pub", "-cert.pub")
cmd = [ "ssh-keygen", "-s", "/etc/ssh/ssh_ca", "-I", identity, "-n", principals, "-V", validity, user_pub_key_path ]
result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode == 0: print(f"Certificate generated at {cert_path}") else: print(f"Error: {result.stderr}")
Usage
sign_key("/home/engineer/.ssh/id_ed25519.pub", "[email protected]", "ubuntu,devops")
Setting Expiration Dates for SSH Certificates
The -V flag in ssh-keygen is your most powerful tool for risk mitigation. In the context of CVE-2024-6387 (regreSSHion), documented in the NIST NVD, while certificates do not patch the race condition, they significantly limit the window of opportunity for an attacker using stolen credentials.
We recommend the following expiration policies:
- Developers: +12h (requires daily re-authentication).
- CI/CD Pipelines: +1h (short-lived tokens for specific jobs).
- Third-party Vendors: +4h (strict window for scheduled maintenance).
- Infrastructure (Host Keys): +52w (annual rotation).
Revocation Lists and Security Auditing
If a private key is lost or a certificate is misused, we use a Key Revocation List (KRL). Unlike standard pubkey auth where you must delete the key from all servers, with a KRL, you update one file and distribute it.
Create a KRL and add a serial number to it
ssh-keygen -k -f /etc/ssh/revoked_keys -z 1001
Update sshd_config to use the KRL
echo "RevokedKeys /etc/ssh/revoked_keys" >> /etc/ssh/sshd_config systemctl restart ssh
To audit active certificates, we monitor /var/log/auth.log. A certificate-based login will look like this:
Accepted publickey for ubuntu from 192.168.1.50 port 54322 ssh2: ED25519-CERT ID [email protected] (serial 1001) CA ED25519 SHA256:...
Transitioning from Legacy SSH Pubkey Authentication
We do not recommend a "big bang" migration. Start by deploying the TrustedUserCAKeys directive to a subset of non-critical servers. Allow users to log in with either their old keys or new certificates.
Once you have verified that the CA workflow is stable, you can begin removing entries from authorized_keys and eventually set AuthorizedKeysFile /dev/null in sshd_config to enforce certificate-only access. This final step ensures that no rogue keys can be added by users, effectively locking down the authentication surface to your CA.
For organizations operating in India, this transition directly supports the "Security by Design" principles encouraged by the MeitY (Ministry of Electronics and Information Technology). It moves the security burden from individual server hygiene to a centralized, auditable process.
Check the current serial number of your CA's issued certificates:
View the KRL content to ensure serials are blocked
ssh-keygen -Q -f /etc/ssh/revoked_keys
