You may have heard that it's possible to sign/verify files using SSH keys. See also my previous blog post: Using HTTPS certificates to sign/encrypt arbitrary data.
In fact, it's possible to encrypt using SSH keys. Why not?
My toy SSH client can download public key from arbitrary SSH server, I added an option for it. Here I access my live webserver actually:
% ./toyssh_v5.py -h vps7.yurichev.org -save_serv_pub_key -server_host_algo rsa-sha2-256 Server's public key saved to vps7.yurichev.org.ssh_host_rsa_key.pub Fatal error: can't go further without username/passsword/pubkey/prikey. exiting.
(-server_host_algo option is important, otherwise ECDSA or ED25519 key would be saved instead.)
The resulting file is almost the same as on my webserver, excluding the last user@host part:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxWpYVDk6hDsgwSODF0jHx1SmGnhuGzswlkqyua5NN1t9u4k5zQJi0WI/iiZH8wE51u2htujxf7ie/DZ26fh0uuHfz6EA7qPdtCB+q4kT8tlXVAWzUw3TslnAKotk36N4euwt9vGSslBHE3GHP7uIOULajtSrfdFU7GpqQRogu0zs2jr3S5Sgmx0CVX9rS3ZurwZ816UJCCOmkoanmM1CSwVfL18iLcs+asxuPAZVu1ZvqWwv6MrM3vdmU92DANxIF4wrrwYiJhrIKVhDfNHxNIn2O5akad73JVzCgSzZil9GIagdIo0LO/dyH2r6BoaGjuqbK2vEBdS8tMrhL+BbD5EUUUN/sHopbHwl1Gska4fs7zIoh1clO+1bHfze/8U8lxezxPka+ctl5vzOYTUFc+BS8t8jCRqBCC5iN/YtAPmPWbfxA2VzXCh1Ny4a+e9HB6ZchNvUq5N8sfchCdIeasEtmQ2EVfaLbObjnMRVGDRiH5myiEQWzxO80ti1cf5k= root@vps7.yurichev.org
It's no secret. It's the same as on my webserver:
root@vps7:~# cat /etc/ssh/ssh_host_rsa_key.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxWpYVDk6hDsgwSODF0jHx1SmGnhuGzswlkqyua5NN1t9u4k5zQJi0WI/iiZH8wE51u2htujxf7ie/DZ26fh0uuHfz6EA7qPdtCB+q4kT8tlXVAWzUw3TslnAKotk36N4euwt9vGSslBHE3GHP7uIOULajtSrfdFU7GpqQRogu0zs2jr3S5Sgmx0CVX9rS3ZurwZ816UJCCOmkoanmM1CSwVfL18iLcs+asxuPAZVu1ZvqWwv6MrM3vdmU92DANxIF4wrrwYiJhrIKVhDfNHxNIn2O5akad73JVzCgSzZil9GIagdIo0LO/dyH2r6BoaGjuqbK2vEBdS8tMrhL+BbD5EUUUN/sHopbHwl1Gska4fs7zIoh1clO+1bHfze/8U8lxezxPka+ctl5vzOYTUFc+BS8t8jCRqBCC5iN/YtAPmPWbfxA2VzXCh1Ny4a+e9HB6ZchNvUq5N8sfchCdIeasEtmQ2EVfaLbObjnMRVGDRiH5myiEQWzxO80ti1cf5k= root@ubuntu-4gb-hel1-1
Now anyone can get my RSA public key. And I have corresponding RSA private key. Can anyone send me an encrypted message? Yes. But I don't know any standard tool for that. I wrote mine.
#!/usr/bin/env python3
# May need to be upgraded:
# python3 -m pip install --upgrade cryptography
import cryptography.hazmat.primitives.serialization
import cryptography.hazmat.primitives.asymmetric.padding
import cryptography.hazmat.primitives.hashes
import sys
# load pub key from server:
f=open(sys.argv[1], "rb")
contents=f.read()
f.close()
pub_key=cryptography.hazmat.primitives.serialization.load_ssh_public_key(contents)
plaintext=b"Hi, Dennis. The Secret Meetup is today, at The Venue 123. 1800 UTC."
# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#encryption
algorithm=cryptography.hazmat.primitives.hashes.SHA256()
mgf=cryptography.hazmat.primitives.asymmetric.padding.MGF1(algorithm=algorithm)
ciphertext=pub_key.encrypt(plaintext,
cryptography.hazmat.primitives.asymmetric.padding.OAEP(mgf, algorithm, label=None))
f=open("cipher.bin", "wb")
f.write(ciphertext)
f.close()
% ./ssh_encrypt.py vps7.yurichev.org.ssh_host_rsa_key.pub
The resulting cipher.bin file is of 384 bytes. I can decrypt it on my webserver with the following code:
#!/usr/bin/env python3
# May need to be upgraded:
# python3 -m pip install --upgrade cryptography
import cryptography.hazmat.primitives.serialization
import cryptography.hazmat.primitives.asymmetric.padding
import cryptography.hazmat.primitives.hashes
import sys
# load ciphertext:
f=open(sys.argv[1], "rb")
ciphertext=f.read()
f.close()
f=open("/etc/ssh/ssh_host_rsa_key", "rb")
contents=f.read()
f.close()
pri_key=cryptography.hazmat.primitives.serialization.load_ssh_private_key(contents, password=b"")
# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#decryption
algorithm=cryptography.hazmat.primitives.hashes.SHA256()
mgf=cryptography.hazmat.primitives.asymmetric.padding.MGF1(algorithm=algorithm)
plainext=pri_key.decrypt(ciphertext,
cryptography.hazmat.primitives.asymmetric.padding.OAEP(mgf, algorithm, label=None))
print (plainext)
root@vps7:~/1# ./ssh_decrypt.py cipher.bin b'Hi, Dennis. The Secret Meetup is today, at The Venue 123. 1800 UTC.'
Neat. But be warned. I did this as a coding exercise, as a proof-of-concept. As it happens in cryptography often, this solution may be insecure if implemented incorrectly.
Why ssh-keygen can sign/verify but not encrypt/decrypt? I don't know but maybe because all this is possible only with RSA keys. But today, some sysadmins disable RSA keys in favor of ECDSA and ED25519 keys, which only allows signing/verifying. ssh-keygen's feature of encryption, if implemented, would not be universal. (This is only my speculation.)
(UPD: 20240616 12:18:10 CEST. As seen on reddit: 1, 2.)

Yes, I know about these lousy Disqus ads. Please use adblocker. I would consider to subscribe to 'pro' version of Disqus if the signal/noise ratio in comments would be good enough.