maxgoedjen / secretive

Store SSH keys in the Secure Enclave
MIT License
7.24k stars 159 forks source link

Feature Request: Support OpenSSH Certificates #269

Closed unreality closed 2 years ago

unreality commented 2 years ago

It would be great if secretive supported OpenSSH Certificates

I'm unsure how this would work, perhaps a location on disk where secretive looks for certificates, and adds them to the list of identities if a matching public key is found?

Unless its already supported? I could not find any mention of them in the docs or code...

maxgoedjen commented 2 years ago

@unreality hey, thanks for the suggestion! Secretive is mostly about providing easy access to keys backed by secure hardware (ie, the SEP on Macs with Touch ID and security keys, like YubiKeys) – I'm not sure there's a ton of value-add for just accessing certs that already live on disk (unless I'm misunderstanding this request).

unreality commented 2 years ago

I feel like you may have misunderstood what happens with the certificates?

The key is still backed by hardware crypto (SmartCard, secure enclave, etc), but using certificates allows an org to issue credentials that dont require installing public-keys on machines. The authentication flow requires both the certificate and the private key in order to proceed. Check out https://smallstep.com/blog/use-ssh-certificates/ for some details on how it all works.

The additional functionality would be to somehow attach a certificate (or certificates) to a private key, and to support the agent signing requests using those certificates.

maxgoedjen commented 2 years ago

Sounds like I might've ;) Lemme read up on it.

maxgoedjen commented 2 years ago

Just after reading a bit about it I'm not 100% sure this will be feasible – the SEP doesn't allow import or export of private keys, which my initial reading of this (again, could be wrong here) suggests would be required. This flow probbbbbbably would work with a YubiKey though?

unreality commented 2 years ago

Import or export of private keys isn't required, as long as a public key can be exported. The CA is sent the public key and will send back a signed certificate which includes the public key the cert is valid for.

When authenticating the ssh client presents the certificate. The SSH server checks the validity of the certificate, then asks the connecting client to sign a request using the private key that corresponds to the public key in the cert. If the connecting client returns a valid signature, the connection is allowed.

unreality commented 2 years ago

Having a look at some other agent implementations, it seems that the way this is implemented is essentially 'if a certificate is available, use that instead of the public key.' (https://github.com/golang/crypto/blob/ae814b36b87190c757eede9bc2d32ed77df88551/ssh/agent/keyring.go#L146)

henryhchchc commented 2 years ago

Thanks for the great app.

I think adding support for certificates is feasible: it doesn't matter where the private key is stored.


First I would like to explain a bit on how SSH certificates work.

In the world without SSH certificates, the server trusts the key directly (i.e., the key is put in authorized_keys). When logging in, the client offers a list of keys it has, and the server accepts the one that is trusted. Finally the client signs a message passed by the server to show that it holds the private key.

When SSH certificate come into play, the trust is built in two steps:

In this case, when logging in, instead of presenting the keys directly, the client presents the certificates to the server. The server then accepts the certificate that is issued (i.e., signed) by the trusted CA.



Other information that may be useful:

Thank you again for the great app. I would like to help if you plan to support this feature (but I don't have enough knowledge of how the source code is organized).

Wyzard256 commented 2 years ago

As a workaround, it's possible to use certificates with Secretive already, by either putting the certificate file in ~/.ssh with a filename that OpenSSH looks for by default (such as id_ecdsa-cert.pub), or by putting the file anywhere and adding a CertificateFile line in .ssh/config that points to it. I've tried that and it works. But it would be helpful to have a way to load a certificate into Secretive so that the agent can provide it, instead of having to configure OpenSSH separately.

(When OpenSSH looks for the default key files like id_rsa and id_ecdsa and their associated .pub files, it also looks for associated -cert.pub files, and apparently it loads certificate files even if no corresponding private or .pub file exists. Then, having loaded the certificate from the file, it's able to recognize that it matches the private key obtained from the agent, so it can use them together. But when using an agent, it's more typical for the agent to provide the certificate along with the key, so OpenSSH doesn't have to load it separately from a file.)


SSH certificates are very useful, btw: they change key distribution from an M*N problem to an M+N one. Instead of your authorized_keys file needing separate lines for each client that you want to log in from (e.g. desktop and laptop), it can have one line for the key that you sign your certificates with, and that'll accept all your certificates from all your client machines.

It's a particularly good fit for Secretive, since you can't copy keys from one machine to another. Without certificates, if you get a new Mac and create a new key, you have to copy it into authorized_keys on all your remote servers — which is tedious, and you might forget some. If you're using certificates, you just make a certificate for your new key, and you're done: the new machine can log into everywhere that the old one could.

henryhchchc commented 2 years ago

either putting the certificate file in ~/.ssh with a filename that OpenSSH looks for by default (such as id_ecdsa-cert.pub), or by putting the file anywhere and adding a CertificateFile line in .ssh/config that points to it.

Thanks for sharing the workaround. It works when I am using the agent locally. When the agent is forwarded to another server, the certificates does not work because the agent is not aware of the existence of the certificates.