SSH Authentication by Certificate Authority

(July 2018)

SSH offers three forms of authentication, in ascending order of desirability: password-based, key-based, and certificate authority (CA).

How is key-based authentication better than password-based authentication?

Users don’t always choose passwords strong enough to prevent guessing by attackers. Keys are consistently stronger. Also, using an ssh-agent saves users from having to frequently type passwords.

How does key-based authentication work?

Key-based auth both authenticates the client to the server, and the server to the client.

With symmetric encryption, single key (a “shared secret”) can encrypt and decrypt data. The SSH client and server agree this key without publicly exposing the secret (using Diffie-Hellman or a similar method). SSH uses symmetric encryption (e.g., AES) to secure exchanged session data, but not for authentication.

For authentication, SSH uses asymmetric encryption.

  1. A client connects to the server.
  2. The server tells the client which protocol versions it supports, and identifies itself by sending its public key.
  3. Client and server negotiate a symmetric session key using Diffie-Hellman.
  4. With the session encrypted, authentication begins. The client sends its public key.
  5. The server checks if the key sent by the client is found in the user account’s authorized_keys file.
  6. If the server finds that the key is authorized, it uses the key to encrypt a random number, which it sends to the client.
  7. If the client legitimately has the matching private key, it decrypts the random number, combines it with the session key, and returns a hash of the result to the server.
  8. The server also hashes the combined session key and random number. It compares this with the value returned by the client. If they hashes match, authentication has succeeded.

How is CA-based authentication better than key-based authentication?

Key-based auth requires copying every user key to every server. That doesn’t scale well. Using a certificate authority simplifies key management across a multitude of servers and/or for many users. The client also doesn’t get asked to accept server keys.

When we talk about CA certs for SSH, we don’t mean the type of x509 certs used with web certificate authorities. SSH certs are simpler.

We create a CA key pair, and use the CA private key to generate certs for hosts and users by signing the public keys of the hosts and users. Distribute the CA public key on all servers and clients.

Some sources suggest using two CA key pairs: one for hosts and another for users. OK, but why? I guess it splits risk in case one key get compromised. It may also be practical if the admin group signing user certs is different from the admins signing host certs.

When signing user or host certs, ssh-keygen can limit the resulting certs in a number ways, like restricting them to various principals (users or hosts) or setting an expiration time for the cert.

Each generated user and host cert gets a serial number. These serial numbers can be added to a Key Revocation List (specified with RevokedKeys in sshd_config).

Note: as of 2017, PuTTY couldn’t use OpenSSH certificates.

How does CA-based authentication work?

Much like key-based authentication, except principals exchange certificates instead of keys.

Set Up the SSH Certificate Authority

Generate two CA key — one for hosts and one for users:

$  ssh-keygen -t rsa -b 4096 -f ./ca_user_key
$  ssh-keygen -t rsa -b 4096 -f ./ca_host_key
$  chmod 600 ./ca_*_key
$  chmod 644 ./ca_*
#  chown root:root ./ca_*_key*

To each host that will use the CA to authenticate users, copy the public user key:

$  scp

…and add this to the host’s /etc/ssh/sshd_config (and restart sshd to activate the change):

TrustedUserCAKeys /etc/ssh/

(Individual accounts can instead add a cert-authority directive to their ~/.ssh/authorized_keys file.)

The user also needs to trust the host key. Add a line like this with the to either ~/.ssh/known_hosts or /etc/ssh/ssh_known_hosts:

@cert-authority * ssh-rsa AAAAB3Nm1yc2EAAAADAQABAAACAQDct…

Generate Host Certificates

Generate a certificate for each host, like:

$  ssh-keygen -s ca_host_key -h \
	-I \
	-n "$(hostname),$(hostname --fqdn),$(hostname -I|tr ' ' ',')" \
	-V +56w \

This produces the cert file

Think carefully about the whether or not to set an expiration on the certificate with -V. Specifying principals with -n is a good practice, but not required.

Copy the cert file to the hosts /etc/ssh/ directory. Configure the SSH daemon on the host to serve the new certificate by editing /etc/ssh/sshd_config (and restart sshd):

$  HostCertificate /etc/ssh/

Generate User Certificates

Generate a user certificate like:

$  ssh-keygen -s ca_user_key \
	-I paulgorman \
	-n paulgorman \
	-V +1d \

…and add the resulting file to the user’s ~/.ssh/ directory.

Remember that the user need only supply their public key for signing. Keep the private key private!

As with host certificates, we can set an expiration on users certificates, or even a window in the future (e.g., allow access for two hours next Tuesday). Consider also a workflow with very short-lived certificates, perhaps one day.

Examine a Certificate

$  ssh-keygen -L -f ./
        Type: user certificate
        Public key: RSA-CERT SHA256:3i3d/WMBr3bYSluMXcHi58+5P/H9qc0ySEoFPuG8e0s
        Signing CA: RSA SHA256:2vYPWzDg0c2sYA+4niNtAFfCYsENi+XcWbzTD0bafW4
        Key ID: "paulgorman"
        Serial: 0
        Valid: from 2018-07-05T21:31:00 to 2018-07-12T21:32:33
        Critical Options: (none)

Revoking a Certificate

First, in some cases, we don’t have to be as worried about revoking certs if we create them with short expiration times.

If we do need to quickly revoke a cert, the SSH daemon takes a RevokedKeys directive in sshd_config. The revocation file uses one of two possible formats: either a text file with one key per line, or a binary KRL (Key Revocation List) generated by ssh-keygen. Certificates can be revoked by serial number (-z).

$  ssh-keygen -k -u -f revoked_keys
$  ssh-keygen -Q -f revoked_keys ( REVOKED

Of course, the revocation file must be distributed to each host.