Skip to content

Instantly share code, notes, and snippets.

@parsa
Last active July 18, 2023 05:26
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save parsa/b37dd02033718729304ff33af261a67f to your computer and use it in GitHub Desktop.
Save parsa/b37dd02033718729304ff33af261a67f to your computer and use it in GitHub Desktop.
Generate MD5 and SHA256 fingerprint of an OpenSSH host

Generate MD5 and SHA256 fingerprint of an OpenSSH host

MD5

# ssh-keyscan -p 8000 -t rsa rostam.cct.lsu.edu 2>/dev/null | awk '{ print $3 }' | base64 -d | md5sum | awk '{ print $1 }' | fold -w2 | paste -sd':' -
71:5a:1d:bb:1f:1e:fa:e6:34:98:3d:48:f5:ce:9d:68
# ssh-keygen -l -E md5 -f <(ssh-keyscan -p 8000 -t rsa rostam.cct.lsu.edu 2>/dev/null)
2048 MD5:71:5a:1d:bb:1f:1e:fa:e6:34:98:3d:48:f5:ce:9d:68 [rostam.cct.lsu.edu]:8000 (RSA)

SHA256

# ssh-keyscan -p 8000 -t rsa rostam.cct.lsu.edu 2>/dev/null | awk '{ print $3 }' | base64 -d | sha256sum | awk '{ print $1 }' | xxd -r -p | base64
Ht2XIO1XUu0diiH2V5PSiEAS0TgR/P0J1RAysk/9yXU=
# ssh-keyscan -p 8000 -t rsa rostam.cct.lsu.edu 2>/dev/null | awk '{ print $3 }' | base64 -d | md5sum | awk '{ print $1 }' | sed 's/.\{2\}/&:/g'
71:5a:1d:bb:1f:1e:fa:e6:34:98:3d:48:f5:ce:9d:68:
# ssh-keygen -l -E sha256 -f <(ssh-keyscan -p 8000 -t rsa rostam.cct.lsu.edu 2>/dev/null)
2048 SHA256:Ht2XIO1XUu0diiH2V5PSiEAS0TgR/P0J1RAysk/9yXU [rostam.cct.lsu.edu]:8000 (RSA)

RandomArt

# ssh-keygen -lv -E md5 -f <(ssh-keyscan -p 8000 -t rsa rostam.cct.lsu.edu 2>/dev/null)
2048 MD5:71:5a:1d:bb:1f:1e:fa:e6:34:98:3d:48:f5:ce:9d:68 [rostam.cct.lsu.edu]:8000 (RSA)
+---[RSA 2048]----+
|            .    |
|           . o   |
|        . o o .  |
|         =   o . |
|        S   o o .|
|           . O.=o|
|            =EB.+|
|            .o.o |
|             oo  |
+------[MD5]------+
# python3 ssh_fingerprint_to_randomart.py 71:5a:1d:bb:1f:1e:fa:e6:34:98:3d:48:f5:ce:9d:68
+---[   n/a  ]----+
|            .    |
|           . o   |
|        . o o .  |
|         =   o . |
|        S   o o .|
|           . O.=o|
|            =EB.+|
|            .o.o |
|             oo  |
+-----------------+
# ssh-keygen -lv -E sha256 -f <(ssh-keyscan -p 8000 -t rsa rostam.cct.lsu.edu 2>/dev/null)
2048 SHA256:Ht2XIO1XUu0diiH2V5PSiEAS0TgR/P0J1RAysk/9yXU [rostam.cct.lsu.edu]:8000 (RSA)
+---[RSA 2048]----+
|      .*O+.o.o* +|
|       +.+o+++ O.|
|        +.=.*.=.E|
|         ooO +o+*|
|        S ..* =+ |
|       . .   =   |
|        .        |
|                 |
|                 |
+----[SHA256]-----+
# python3 ssh_fingerprint_to_randomart.py -m sha256 Ht2XIO1XUu0diiH2V5PSiEAS0TgR/P0J1RAysk/9yXU
+---[   n/a  ]----+
|      .*O+.o.o* +|
|       +.+o+++ O.|
|        +.=.*.=.E|
|         ooO +o+*|
|        S ..* =+ |
|       . .   =   |
|        .        |
|                 |
|                 |
+-----------------+
#!/usr/bin/env python
# usage: drunken_bishop.py [-h] [--mode {md5,sha256}] fingerprint
#
# Generate randomart from fingerprint
#
# positional arguments:
# fingerprint
#
# optional arguments:
# -h, --help show this help message and exit
# --mode {md5,sha256}, -m {md5,sha256}
import argparse
import base64
import numpy as np
def get_steps(bits):
bits_grouped = np.array(bits, dtype=np.int8).reshape((-1, 4, 2))
bits_grouped_reordered = np.flip(bits_grouped, axis=1)
return bits_grouped_reordered.reshape((-1, 2))
def drunken_bishop(steps):
positions = np.zeros((9, 17), dtype=np.int8)
current_position = np.array([4, 8])
def move(b0, b1):
if (b0, b1) == (0, 0):
return (-1, -1)
elif (b0, b1) == (0, 1):
return (-1, 1)
elif (b0, b1) == (1, 0):
return (1, -1)
elif (b0, b1) == (1, 1):
return (1, 1)
raise Exception('Impossible move: ({}, {})'.format(b0, b1))
for step in steps:
positions[tuple(current_position)] += 1
current_position += move(step[0], step[1])
if current_position[0] >= positions.shape[0]:
current_position[0] = positions.shape[0] - 1
elif current_position[0] <= 0:
current_position[0] = 0
if current_position[1] >= positions.shape[1]:
current_position[1] = positions.shape[1] - 1
elif current_position[1] <= 0:
current_position[1] = 0
positions[(4, 8)] = 15
positions[tuple(current_position)] = 16
return positions
def print_randomart(atrium):
values = {
0: ' ', 1: '.', 2: 'o', 3: '+', 4: '=', 5: '*', 6: 'B', 7: 'O', 8: 'X',
9: '@', 10: '%', 11: '&', 12: '#', 13: '/', 14: '^', 15: 'S', 16: 'E'
}
print('+---[ n/a ]----+')
for r in atrium:
print('|', end='')
for c in r:
print(values[c], end='')
print('|')
print('+-----------------+')
def get_md5_bits(fingerprint):
return np.array([list('{:08b}'.format(int(i, 16))) for i in fingerprint.split(':')])
def get_sha256_bits(fingerprint):
missing_padding = 4 - (len(fingerprint) % 4)
fingerprint += '=' * missing_padding
return np.array([list('{:08b}'.format(i)) for i in base64.b64decode(fingerprint)])
def main():
parser = argparse.ArgumentParser(
description='Generate randomart from fingerprint')
parser.add_argument('--mode', '-m', choices=['md5', 'sha256'], default='sha256')
parser.add_argument('fingerprint', type=str)
args = parser.parse_args()
bits = None;
if args.mode == 'md5':
bits = get_md5_bits(args.fingerprint)
elif args.mode == 'sha256':
bits = get_sha256_bits(args.fingerprint)
else:
raise Exception('Unsupported hashing mode: {}'.format(args.mode))
steps = get_steps(bits)
atrium = drunken_bishop(steps)
print_randomart(atrium)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment