Что записано в файле .ssh-known_hosts

в 18:39, , рубрики: known_hosts, ssh, информационная безопасность, криптография, Сетевые технологии
Что записано в файле .ssh-known_hosts - 1

Каждый раз, когда мы подключаемся по протоколу ssh к серверу, ssh клиент проверяет совпадает ли публичный ключ для этого сервера с тем, который был прошлый раз (по крайней мере так рекомендует делать стандарт ssh). В OpenSSH список известных ключей серверов хранится в файле known_hosts. Под катом коротко о том, что и как конкретно там хранится.
Все эксперименты проводились на Linux (Debian/Mint/Ubuntu). За расположение и содержание файлов в других ОС не ручаюсь.
Подключаясь первый раз к ssh серверу, мы видим примерно такое сообщение:

The authenticity of host '192.168.0.2 (192.168.0.2)' can't be established.
RSA key fingerprint is SHA256:kd9mRkEGLo+RBBNpxKp7mInocF3/Yl/0fXRsGJ2JfYg.
Are you sure you want to continue connecting (yes/no)?

Если согласиться, то в файл ~/.ssh/known_hosts добавится такая строка:

|1|CuXixZ+EWfgz40wpkMugPHPalyk=|KNoVhur7z5NAZmNndtwWq0kN1SQ= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeiF4OOOUhWvOYrh/e4q91+iz+i9S0s3M2LPq+GAhRlhKt5vKyEVd6x6m26cc98Y+SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x+mPvO9FLSBk/Al2GbH5q6F+hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd+c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti+7xGwT8DF+tIyLFcU+zxd0QnwJIbNvewkHs0LsMOWFVPz/Nd0XiVXimX+ugCDBZ/4q8NUwH9SGzCMAvnnr+D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV

Тут через пробел записаны три элемента: хэш от имени сервера, название используемого ассиметричного алгоритма и публичный ключ сервера. Разберём их по очереди.

А если почитать инструкцию

На самом деле согласно мануалу к Убунте там могут быть ещё 2 поля, так же отделённые пробелами:

  • в начале строки может находиться пометка "@cert-authority" или “@revoked”, означающие, соответственно, что в этой строке записан публичный ключ ЦА или что этот ключ был отозван и не может быть использован.
  • в конце строки может быть произвольный комментарий

Имя сервера

В примере хэш от имени сервера (хоста) выглядит так:

|1|CuXixZ+EWfgz40wpkMugPHPalyk=|KNoVhur7z5NAZmNndtwWq0kN1SQ=

На самом деле тут может быть записано и имя хоста в открытом виде или маска, задающая множество допустимых имён. Но у меня по умолчанию сохраняется хэшированое имя. Запись разделена на 3 части символом "|". Первая часть — алгоритм хэширования. «1» соответствует HMAC-SHA1 (других не видел). Вторая часть — соль (ключ для HMAC). Третья часть — собственно хэш (вывод HMAC).

Проверяем

from base64 import b64decode
import hmac

salt = b64decode("CuXixZ+EWfgz40wpkMugPHPalyk=")
host = b'192.168.0.2'
hash = hmac.HMAC(salt, host, 'sha1').digest()
print(b64encode(hash).decode())

> 'KNoVhur7z5NAZmNndtwWq0kN1SQ='

Ассиметричный алгоритм

В RFC-4253 перечислены 4 ассиметричных алгоритма: ssh-dss (обязательный), ssh-rsa (рекомендуемый), pgp-sign-rsa (опциональный), pgp-sign-dss (опциональный). По умолчанию в Linux генерируются ключи первых двух видов и для не упомянутых в RFC алгоритмов на эллиптических кривых. Предпочтение отдаётся последним, однако клиент может выбрать алгоритм опцией HostKeyAlgorithms.

Как проверить нужный (не по-умолчанию) отпечаток ключа
Это может быть полезно если, например, при первом заходе на сервер вы хотите проверить отпечаток ключа, а знаете только отпечаток ключа ssh-rsa. Тогда можно подключиться такой командой:

ssh root@192.168.0.2 -o HostKeyAlgorithms=ssh-rsa

Если нужно задать ещё и алгоритм хэширования ключа, то можно использовать опцию FingerprintHash. Например, если известен только md5 от ssh-rsa можно подключиться так:

ssh root@192.168.0.2 -o HostKeyAlgorithms=ssh-rsa -o FingerprintHash=md5

Публичный ключ

Публичный ключ в known_hosts совпадает с тем, который записан в файле /etc/ssh/ssh_host_rsa_key.pub на сервере (вместо rsa подставить название используемого алгоритма). Если снять Base64 кодирование, то внутри будет ещё раз название алгоритма и собственно компоненты ключа.

А чего бы и не снять Base64

b'x00x00x00x07ssh-rsax00x00x00x03x01x00x01x00x00x01x01x00x9ex88^x0e8xe5!Zxf3x98xaex1fxdexe2xafuxfa,xfex8bxd4xb4xb3s6,xfaxbex18x08Qx96x12xadxe6xf2xb2x11Wzxc7xa9xb6xe9xc7=xf1x8fx92Ayxc2x07xd1x96yV$xf29Ex1cExe7cx86x16ybxc3xc1r!x1cx12Xxb7x9cxb3xde>Vx92x0fyxc7xe9x8fxbcxefE- dxfctvx19xb1xf9xabxa1~x85x92%.cxbar"x12x99~x13xb5xc1xb5xb3x0ex12xc2x84xc0x0exbaxe1txcdx0e%xdfx9cxe4%<x8axa0x9bsxa3xf3x9dx86xcbepxafxa8xf6SYxe4x9bLxb5x1cRxedSx8bxeexf1x1bx04xfcx0c_xad#"xc5qOxb3xc5xddx10x9fx02Hlxdbxdexc2Axecxd0xbbx0c9aU??xcdwExe2Uxxa6_xebxa0x080Yxffx8axbc5Lx07xf5!xb3x08xc0/x9ezxfex0fR<_kxe1Jxe4lNxc4x17/x93xf7xbdxffx1ex94<Ot:xcc'3mx94x10x9b-lxd5'

Видно, что идут 4 байта, в которые записана длина поля, потом само поле и т.д. Первое поле — название алгоритма, остальные зависят от конкретного алгоритма. В приведённом выше ключе 3 поля:

b'ssh-rsa' - название 
b'x01x00x01' - публичная экспонента
b'x00x9ex88^x0e8xe5!Zxf3x98xaex1fxdexe2xafuxfa,xfex8bxd4xb4xb3s6,xfaxbex18x08Qx96x12xadxe6xf2xb2x11Wzxc7xa9xb6xe9xc7=xf1x8fx92Ayxc2x07xd1x96yV$xf29Ex1cExe7cx86x16ybxc3xc1r!x1cx12Xxb7x9cxb3xde>Vx92x0fyxc7xe9x8fxbcxefE- dxfctvx19xb1xf9xabxa1~x85x92%.cxbar"x12x99~x13xb5xc1xb5xb3x0ex12xc2x84xc0x0exbaxe1txcdx0e%xdfx9cxe4%<x8axa0x9bsxa3xf3x9dx86xcbepxafxa8xf6SYxe4x9bLxb5x1cRxedSx8bxeexf1x1bx04xfcx0c_xad#"xc5qOxb3xc5xddx10x9fx02Hlxdbxdexc2Axecxd0xbbx0c9aU??xcdwExe2Uxxa6_xebxa0x080Yxffx8axbc5Lx07xf5!xb3x08xc0/x9ezxfex0fR<_kxe1Jxe4lNxc4x17/x93xf7xbdxffx1ex94<Ot:xcc'3mx94x10x9b-lxd5' - модуль N (0x101 * 8 = 2048 бит)

Отпечаток ключа (Fingerprint)

Отпечаток ключа, который предлагается сверить при первом подключении — это соответствующий хэш (в примере — SHA256) от публичного ключа из прошлого пункта и из /etc/ssh/ssh_host_rsa_key.pub, закодированный в base64 для хэш функций семейства SHA или в hex для MD5.

Считаем

from hashlib import sha256
from base64 import b64decode, b64encode
pub_key_bin = b64decode("AAAAB3NzaC1yc2EAAAADAQABAAABAQCeiF4OOOUhWvOYrh/e4q91+iz+i9S0s3M2LPq+GAhRlhKt5vKyEVd6x6m26cc98Y+SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x+mPvO9FLSBk/Al2GbH5q6F+hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd+c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti+7xGwT8DF+tIyLFcU+zxd0QnwJIbNvewkHs0LsMOWFVPz/Nd0XiVXimX+ugCDBZ/4q8NUwH9SGzCMAvnnr+D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV")
hash = sha256(pub_key_bin).digest()
fingerprint = b64encode(hash)
print(fingerprint)

> b'kd9mRkEGLo+RBBNpxKp7mInocF3/Yl/0fXRsGJ2JfYg='

Видим, что хэш и правда совпадает с отпечатком, показанным при первом подключении (цитата в начале статьи), с точностью до символа "=" в конце.

Тут небольшая программка для поиска хостов в файле known_hosts, появившаяся в процессе экспериментов.

Автор: Nokta_strigo

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js