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.