Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A command to display a a key's randomart image from a fingerprint?

I have a ssh-key fingerprint:

16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48

I would like to see the randomart image of this fingerprint. Is there a command that take this fingerprint as input and that output the randomart image?

PS: I'm not asking for the -o VisualHostKey option coming with the SSH command.

like image 864
tmangin Avatar asked May 06 '15 16:05

tmangin


People also ask

What is SSH key Randomart for?

SSH is everywhere in the development or operations world now. For development it's what allows you to push to GitHub. For operations it's what allows you to reasonably securely log into Linux servers.

How do I see SSH fingerprint?

Use ssh-keygen The -l option lists the fingerprint, and the -f /etc/ssh/ssh_host_rsa_key. pub option gives the location of the public key file of the host. That location is the default for Linux® servers, but you might need to find it in a different location.

What is SSH key fingerprint?

Key fingerprints are special checksums generated based on the public SSH key. Run against the same key, ssh-keygen command will always generate the same fingerprint. Because of this property, you can use SSH key fingerprints for three things: Identify SSH key – fingerprint will stay the same even if you rename the file.


1 Answers

No. What you have provided is only the MD5 fingerprint of the key. SSH randomart displays the encryption algorithms and the hashing algorithm, and the visual art created from the fingerprint. OpenSSH does not seem to come with a tool to generate the ASCII visual art from the fingerprint itself, but that fingerprint is generated from a public key that you probably do have access to. If that is the case, you can put that public key in a file and run ssh-keygen -l on it.

For specific key(s):

ssh-keygen -lvf ~/.ssh/<id_whatever_name>

e.g., For all entries in known_hosts (Probably not practical but useful for demonstration)

ssh-keygen -lvf ~/.ssh/known_hosts

For the default key:

ssh-keygen -lv

Command Synopsis:

ssh-keygen -l [-v] [-E <fingerprint_hash>] [-f <input_keyfile>]
  • -l
    • Fingerprint of specified public key file
    • With -v prints both fingerprint and visual ASCII art of the key
  • -E <hash_algorithm>
    • Specify the hash algorithm used when displaying key fingerprints
    • Valid options:
      • sha256 (default)
      • md5 (Older systems only use md5)
  • -f <key file>
    • Specify the ssh key the fingerprint is going to be made from
      • Anything with a valid public key format
        • Includes authorized_keys, known_hosts
    • <key file> may contain a private or a public ssh key
      • Public key can be derived from the private key file by the user with the -y option
        • e.g. ssh-keygen -yf ~/.ssh/id_asghar
    • Man page: https://linux.die.net/man/1/ssh-keygen

Note:

You can obtain the ssh key of an active SSH server with ssh-keyscan <host>


Command Synopsis:

ssh-keyscan [-4|-6] [-f -|<file>] [-H] [-p <port>] [-T <timeout>] [-t <key type>] [-v]
  • -4
    • Only connect to IPv4 hosts
  • -6
    • Only connect to IPv6 hosts
  • -f
    • Read hostnames or <addrlist> <namelist> pair
    • -f -
      • Read from stdin
    • -f <file>
      • Read from file
    • Format
      • <host_address>[,<host_address>...] [<host_name>,[<host_name>...]]
      • One entry per line
      • e.g.

1.2.3.6 someother.fqdn,1.2.3.7,1.2.3.8```

  • -H
    • Hash hostnames in the output
      • A security option
      • Hashes can be used by ssh and sshd
  • -p <port>
    • Port the ssh server is listening on
      • Default: 22
  • -T <timeout>
    • Wait for timeout seconds before giving up
    • Default: 5
  • -t
    • Type(s) of key to get from the ssh server.
      • Multiple types separated with comma
    • Default: display all available keys
    • Valid options:
      • rsa1 (version 1 only)
      • rsa
      • dsa
      • ecdsa
      • ed25519
  • -v
    • Verbose output
    • Can be repeated several times to increase verbosity level as desired
  • Man page: https://linux.die.net/man/1/ssh-keyscan

e.g., Get the fingerprint and ASCII visual art for the RSA key of github.com

% ssh-keygen -lv -E md5 -f <(ssh-keyscan -t rsa github.com)
# github.com:22 SSH-2.0-babeld-7bdc42c4
2048 MD5:16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48 github.com (RSA)
+---[RSA 2048]----+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+------[MD5]------+

However, if you insist on getting the randomart solely from the fingerprint, you probably have to generate it yourself. As I understand, OpenSSH generates the ASCII visual art from the fingerprint using the Drunken Bishop algorithm. Implementing this algorithm seems to be trivial, and in the case of your host with the MD5 fingerprint of 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48, the ASCII visual art is:

+---[   n/a  ]----+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+------[MD5]------+

Here is the script:

#!/usr/bin/env python

# NOTE: Requires Python 3+
# 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
try:
    import numpy as np
except ImportError as ex:
    import sys
    print(
        str(ex) + '. Make sure that numpy is installed and available.\n',
        file=sys.stderr)
    sys.exit(1)


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):
    field = np.zeros((9, 17), dtype=np.int8)
    start_position = (4, 8)

    def walk(start_position):
        def get_step_direction(b):
            direction_map = {
                (0, 0): (-1, -1),
                (0, 1): (-1, 1),
                (1, 0): (1, -1),
                (1, 1): (1, 1)
            }
            return direction_map[tuple(b)]

        def adjust_for_walls(pos, field_dims):
            return np.maximum(np.minimum(pos, field_dims), (0, 0))

        pos = np.array(start_position)
        for step in steps:
            field[tuple(pos)] += 1
            pos += get_step_direction(step)
            pos = adjust_for_walls(pos, field.shape)
        return tuple(pos)

    end_position = walk(start_position)

    field[start_position] = 15
    field[end_position] = 16

    return field


def print_randomart(atrium, hash_mode):
    # Symbols for the number of times a position is visited by the bishop
    values = {
        0: ' ',  # Initial value
        1: '.', 2: 'o', 3: '+', 4: '=', 5: '*', 6: 'B', 7: 'O', 8: 'X',
        9: '@', 10: '%', 11: '&', 12: '#', 13: '/', 14: '^',
        15: 'S',  # Start position
        16: 'E'  # End position
    }

    print('+---[   n/a  ]----+')
    for row in atrium:
        symbolic_row = [values[visit_count] for visit_count in row]
        print('|{}|'.format(''.join(symbolic_row)))
    if hash_mode == 'md5':
        print('+------[MD5]------+')
    elif hash_mode == 'sha256':
        print('+----[SHA256]-----+')
    else:
        raise RuntimeError('Unsupported hashing mode: {}'.format(hash_mode))


def get_bits(fingerprint, hash_mode):
    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)])

    if hash_mode == 'md5':
        return get_md5_bits(fingerprint)
    elif hash_mode == 'sha256':
        return get_sha256_bits(fingerprint)
    raise RuntimeError('Unsupported hashing mode: {}'.format(hash_mode))


def get_argparser():
    parser = argparse.ArgumentParser(
        description='Generate randomart from fingerprint')
    parser.add_argument(
        '--mode', '-m', choices=['md5', 'sha256'], default='sha256')
    parser.add_argument('fingerprint', type=str)
    return parser


def main():
    parser = get_argparser()
    args = parser.parse_args()

    bits = get_bits(args.fingerprint, args.mode)
    steps = get_steps(bits)
    atrium = drunken_bishop(steps)
    print_randomart(atrium, args.mode)


if __name__ == '__main__':
    main()

like image 157
Parsa Avatar answered Oct 17 '22 17:10

Parsa