SSH keys
I suppose that you use SSH
keys for password-less logins. I also assume
that you heard about RSA
, the most common public-key cryptosystem.
I use consciously RSA
keys mostly at work but they are used generally
in many systems. I think that it’s good idea to know much more about them than:
this is a private key – keep it and don’t share with any body, and this one
it’s a public key – you can send it to anybody.
In this post I’m going to find out more about keys. I will focus only on OpenSSH
solution widely used on Linux.
Create keys
Let’s start from the beginning. I create small RSA
keys – only 768
bits for
tests purpose and to reduce output. In real life you should not go down under
2048
bits.
dirdival@pld:/tmp/ssh$ ssh-keygen -b 768 -t rsa -C "writing to /dev/null" -f /tmp/ssh/id_rsa_wtdn
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /tmp/ssh/id_rsa_wtdn.
Your public key has been saved in /tmp/ssh/id_rsa_wtdn.pub.
The key fingerprint is:
af:42:a8:f9:c2:d6:80:1c:b0:45:21:7d:ae:8f:1b:89 writing to /dev/null
The key's randomart image is:
+--[ RSA 768]----+
|.oo. |
|..o . |
|.o o |
|.. . |
|.... . S |
|.oo.. . . |
|E.oB . . |
| B.o . . |
| ..+. .. |
+-----------------+
How you can see it’s easy and takes a few seconds. What we can find on the output of ssh-keygen
?
At least two things are obvious, we have here full paths to public and private keys.
dirdival@pld:/tmp/ssh$ file id_rsa_wtdn
id_rsa_wtdn: PEM RSA private key
dirdival@pld:/tmp/ssh$ file id_rsa_wtdn.pub
id_rsa_wtdn.pub: OpenSSH RSA public key
Let’s see them:
dirdival@pld:/tmp/ssh$ cat id_rsa_wtdn
-----BEGIN RSA PRIVATE KEY-----
MIIBywIBAAJhAL/faiDQ2Oy97TWTahzI7JNI3qRKaiQNyCpqhMlIVOy9FIQe+DDZ
1cLrsQYY/D9P/nJxnYLSMOkZLu/DDME2sKzBsRw09AQSRPREGylvnvx7Dp2xMkB7
/h6jCKPsJdruLQIDAQABAmB5HDVqB0mVjYCoG6eUCcNCaHGYNBxxK33YQCoWvyBT
2jmT99RjSWyjP5AasDSwZfW3KsG8a7kwRclty27aLE5OJksReDNhzryYQW41bNDz
OajMzjAx4lLd+NhaRCs0QmECMQDk0U8KJVIuE7EUQdT56ZFCebhEfUgoQIEO7Unr
apgbG2Vyz7x5idpX5n0nW78aQqUCMQDWqo4n/iu/Hk80cmR0rwcz8T0fNi1+169k
tGQSWnaNPkrO9n6raPRW7jY6ELkuTukCMQCRBuDz60e1EKIR1s/oPlPlMETMlCNh
79Bc56UMYxlZRPn91RD+b5NGVz5H7eyn9kkCMQDEWlmRh1IojObSCFiOypKCFoVc
CUhwH4WVTdPDXe/WnkX7LUkMLQJiiZ4cWrOoAhECMC1i2yvcbJ3FBHMSFVJ2GOG1
aBeIp3F+cBnbRyueHuGTAnAozSvzwSHa/SqocUppgg==
-----END RSA PRIVATE KEY-----
dirdival@pld:/tmp/ssh$ cat id_rsa_wtdn.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAYQC/32og0Njsve01k2ocyOyTSN6kSmokDcgqaoTJSFTsvRSEHvgw2dXC67EGGPw/T/5ycZ2C0jDpGS7vwwzBNrCswbEcNPQEEkT0RBspb578ew6dsTJAe/4eowij7CXa7i0= writing to /dev/null
If you are a programmer, I suppose that you can easily recognise here base64
format.
In both keys it’s used to cary data. First key (private) is stored in PEM
.
Second key (public) is written in OpenSSH format
, but you can transform it into PEM if you wish:
l@pld:/tmp/ssh$ ssh-keygen -f id_rsa_wtdn.pub -e -m pem > id_rsa_wtdn.pub.pem
dirdival@pld:/tmp/ssh$ cat id_rsa_wtdn.pub.pem
-----BEGIN RSA PUBLIC KEY-----
MGgCYQC/32og0Njsve01k2ocyOyTSN6kSmokDcgqaoTJSFTsvRSEHvgw2dXC67EG
GPw/T/5ycZ2C0jDpGS7vwwzBNrCswbEcNPQEEkT0RBspb578ew6dsTJAe/4eowij
7CXa7i0CAwEAAQ==
-----END RSA PUBLIC KEY-----
Moreover, on the output from the application, we have fingerprint of public key. The most common use of it is confirmation that you are going to use known machine. When you first time connect to some server you can see similar comment:
dirdival@pld:~$ ssh dirdival@localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
RSA key fingerprint is 78:1e:56:b2:fc:e8:d0:2e:38:04:2b:8d:77:a1:e4:38.
Are you sure you want to continue connecting (yes/no)?
After confirmation fingerprint of this machine will be stored in ~/.ssh/known_hosts
file.
The key point is: if somebody decide to make nasty things, for example attract you to
different machine, then you you can see this warning:
dirdival@pld:/tmp/ssh$ ssh dirdival@localhost
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
7e:f1:c3:c5:6f:9a:80:c5:d1:37:f8:87:be:ae:e7:cd.
Please contact your system administrator.
Add correct host key in /home/dirdival/.ssh/known_hosts to get rid of this message.
Offending RSA key in /home/dirdival/.ssh/known_hosts:44
remove with: ssh-keygen -f "/home/dirdival/.ssh/known_hosts" -R localhost
RSA host key for localhost has changed and you have requested strict checking.
Host key verification failed.
There is no magic with fingerprint, you can generate it manually. It’s ordinary md5sum of public key, so first we have to extract it from
base64
and after that make calculations. BTW, watch out on used data – I took them from oryginal (OpenSSH format)
not from PEM:
dirdival@pld:/tmp/ssh$ echo AAAAB3NzaC1yc2EAAAADAQABAAAAYQC/32og0Njsve01k2ocyOyTSN6kSmokDcgqaoTJSFTsvRSEHvgw2dXC67EGGPw/T/5ycZ2C0jDpGS7vwwzBNrCswbEcNPQEEkT0RBspb578ew6dsTJAe/4eowij7CXa7i0= | base64 -d | md5sum
af42a8f9c2d6801cb045217dae8f1b89 -
You can do it in more convenient way, using command:
dirdival@pld:/tmp/ssh$ ssh-keygen -l -f /tmp/ssh/id_rsa_wtdn.pub
768 af:42:a8:f9:c2:d6:80:1c:b0:45:21:7d:ae:8f:1b:89 writing to /dev/null (RSA)
The last crucial observation about fingerprints, because you can miss it. When you connect to a server
the md5 checksum
is taken from the server configuration not from your account. Default keys
are stored in /etc/ssh
dirdival@pld:~$ ls -lh /etc/ssh/ssh_host_rsa_key.pub
-rw-r--r-- 1 root root 390 cze 17 2012 /etc/ssh/ssh_host_rsa_key.pub
dirdival@pld:~$ ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
2048 78:1e:56:b2:fc:e8:d0:2e:38:04:2b:8d:77:a1:e4:38 root@pld (RSA)
Simpler, above fingerprint refers to my machine and was shown when first time
I connected to localhost
.
Finally, the last thing from the ssh-kegen
output is randomart image
:
+--[ RSA 768]----+
|.oo. |
|..o . |
|.o o |
|.. . |
|.... . S |
|.oo.. . . |
|E.oB . . |
| B.o . . |
| ..+. .. |
+-----------------+
Meaning of randomart is similar to fingerprint. For many people it’s easier to remember image than a set of characters. And again, the same remark – when you connect to a server then machine’s randomart is presented, not for your key.
Look deeper
We know the basics, the time has come to find out more about data stored in keys. When we extract data carried by base64 then we can see that real key has binary form.
dirdival@pld:/tmp/ssh$ cat id_rsa_wtdn | sed 's|^-.*||g' | base64 -d | xxd
0000000: 3082 01cb 0201 0002 6100 bfdf 6a20 d0d8 0.......a...j ..
0000010: ecbd ed35 936a 1cc8 ec93 48de a44a 6a24 ...5.j....H..Jj$
0000020: 0dc8 2a6a 84c9 4854 ecbd 1484 1ef8 30d9 ..*j..HT......0.
0000030: d5c2 ebb1 0618 fc3f 4ffe 7271 9d82 d230 .......?O.rq...0
0000040: e919 2eef c30c c136 b0ac c1b1 1c34 f404 .......6.....4..
0000050: 1244 f444 1b29 6f9e fc7b 0e9d b132 407b .D.D.)o..{...2@{
To be more specific, data are stored in ASN.1 format. Because it’s
a raw format it’s a little tricky to work with it. I tried find a library
in C/C++
or in python
which allows works with ASN.1 in simple way but I didn’t find any.
Thankfully, openssl
has build in option allows extract data:
dirdival@pld:/tmp/ssh$ openssl rsa -in id_rsa_wtdn -text
Private-Key: (768 bit)
modulus:
00:bf:df:6a:20:d0:d8:ec:bd:ed:35:93:6a:1c:c8:
ec:93:48:de:a4:4a:6a:24:0d:c8:2a:6a:84:c9:48:
54:ec:bd:14:84:1e:f8:30:d9:d5:c2:eb:b1:06:18:
fc:3f:4f:fe:72:71:9d:82:d2:30:e9:19:2e:ef:c3:
0c:c1:36:b0:ac:c1:b1:1c:34:f4:04:12:44:f4:44:
1b:29:6f:9e:fc:7b:0e:9d:b1:32:40:7b:fe:1e:a3:
08:a3:ec:25:da:ee:2d
publicExponent: 65537 (0x10001)
privateExponent:
79:1c:35:6a:07:49:95:8d:80:a8:1b:a7:94:09:c3:
42:68:71:98:34:1c:71:2b:7d:d8:40:2a:16:bf:20:
53:da:39:93:f7:d4:63:49:6c:a3:3f:90:1a:b0:34:
b0:65:f5:b7:2a:c1:bc:6b:b9:30:45:c9:6d:cb:6e:
da:2c:4e:4e:26:4b:11:78:33:61:ce:bc:98:41:6e:
35:6c:d0:f3:39:a8:cc:ce:30:31:e2:52:dd:f8:d8:
5a:44:2b:34:42:61
prime1:
00:e4:d1:4f:0a:25:52:2e:13:b1:14:41:d4:f9:e9:
91:42:79:b8:44:7d:48:28:40:81:0e:ed:49:eb:6a:
98:1b:1b:65:72:cf:bc:79:89:da:57:e6:7d:27:5b:
bf:1a:42:a5
prime2:
00:d6:aa:8e:27:fe:2b:bf:1e:4f:34:72:64:74:af:
07:33:f1:3d:1f:36:2d:7e:d7:af:64:b4:64:12:5a:
76:8d:3e:4a:ce:f6:7e:ab:68:f4:56:ee:36:3a:10:
b9:2e:4e:e9
exponent1:
00:91:06:e0:f3:eb:47:b5:10:a2:11:d6:cf:e8:3e:
53:e5:30:44:cc:94:23:61:ef:d0:5c:e7:a5:0c:63:
19:59:44:f9:fd:d5:10:fe:6f:93:46:57:3e:47:ed:
ec:a7:f6:49
exponent2:
00:c4:5a:59:91:87:52:28:8c:e6:d2:08:58:8e:ca:
92:82:16:85:5c:09:48:70:1f:85:95:4d:d3:c3:5d:
ef:d6:9e:45:fb:2d:49:0c:2d:02:62:89:9e:1c:5a:
b3:a8:02:11
coefficient:
2d:62:db:2b:dc:6c:9d:c5:04:73:12:15:52:76:18:
e1:b5:68:17:88:a7:71:7e:70:19:db:47:2b:9e:1e:
e1:93:02:70:28:cd:2b:f3:c1:21:da:fd:2a:a8:71:
4a:69:82
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
MIIBywIBAAJhAL/faiDQ2Oy97TWTahzI7JNI3qRKaiQNyCpqhMlIVOy9FIQe+DDZ
1cLrsQYY/D9P/nJxnYLSMOkZLu/DDME2sKzBsRw09AQSRPREGylvnvx7Dp2xMkB7
/h6jCKPsJdruLQIDAQABAmB5HDVqB0mVjYCoG6eUCcNCaHGYNBxxK33YQCoWvyBT
2jmT99RjSWyjP5AasDSwZfW3KsG8a7kwRclty27aLE5OJksReDNhzryYQW41bNDz
OajMzjAx4lLd+NhaRCs0QmECMQDk0U8KJVIuE7EUQdT56ZFCebhEfUgoQIEO7Unr
apgbG2Vyz7x5idpX5n0nW78aQqUCMQDWqo4n/iu/Hk80cmR0rwcz8T0fNi1+169k
tGQSWnaNPkrO9n6raPRW7jY6ELkuTukCMQCRBuDz60e1EKIR1s/oPlPlMETMlCNh
79Bc56UMYxlZRPn91RD+b5NGVz5H7eyn9kkCMQDEWlmRh1IojObSCFiOypKCFoVc
CUhwH4WVTdPDXe/WnkX7LUkMLQJiiZ4cWrOoAhECMC1i2yvcbJ3FBHMSFVJ2GOG1
aBeIp3F+cBnbRyueHuGTAnAozSvzwSHa/SqocUppgg==
-----END RSA PRIVATE KEY-----
Wide explanation, of all things, you can find in RFC3447, but for us the most important part of it is RSA private key syntax (ASN.1 format):
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
How you can see even names of variables were preserved literally from RSA
description.
If you wish work with RSA
keys I think that the best idea is to use python pyCrypto library
:
dirdival@pld:/tmp/ssh$ python
Python 2.7.8 (default, Sep 9 2014, 22:08:43)
[GCC 4.9.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from Crypto.PublicKey import RSA
>>> with open('id_rsa_wtdn', 'r') as f_key:
... key = RSA.importKey(f_key.read())
...
>>> print key
<_RSAobj @0x7f500c0dc7e8 n(768),e,d,p,q,u,private>
>>> print key.n
1163616635019420761386261995137901271228838614127186019741901603856804328038954590877898525874213409268054756033077874229189030905487038052163785842808263839009258009430984845545437464884708604868843164432625393806203016420246875693
>>> print key.e
65537
>>> print key.p
35218253818953980768506387638248335216461812827191381970186920298380955706090279876136514739796509906400604388475557
>>> print key.q
33040156987941811619529556650925749142469923966751395910821444032135576521646935164258543453311179838626615782166249
>>> print key.p * key.q
1163616635019420761386261995137901271228838614127186019741901603856804328038954590877898525874213409268054756033077874229189030905487038052163785842808263839009258009430984845545437464884708604868843164432625393806203016420246875693
I think that is enough if we are talking about basic RSA keys knowledge. I hope that you enjoyed it.