{ "metadata": { "name": "ssh_fingerprint" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "**Purpose**: to figure out exactly how to calculate the various (two?) ssh fingerprints used in configuring and running Amazon EC2\n", "\n", "http://blog.jbrowne.com/?p=23#comment-3393\n", "\n", "> `openssl pkey -in ~/.ssh/id_rsa -pubout -outform DER | openssl md5 -c`\n", "\n", "does work to calculate the fingerprint of my key:\n", "\n", "> `ac:89:8d:0c:df:31:56:ee:6e:78:1d:1e:93:00:52:2d`\n", "\n", "gets us from the **private key** to the fingerprint. Question is how to go from the public key to the fingerprint." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Documentation I used\n", "\n", "* http://stackoverflow.com/questions/1193529/how-to-store-retreieve-rsa-public-private-key/13104466#13104466\n", "* http://blog.oddbit.com/post/converting-openssh-public-keys" ] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Working solution using pycrypto" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from Crypto.PublicKey import RSA\n", "import hashlib\n", "import binascii\n", "\n", "#ID_RSA_PUB = \"/Users/raymondyee/.ssh/id_rsa.pub\"\n", "ID_RSA_PUB = \"/Users/raymondyee/Downloads/bitnami_id_rsa.pub\"\n", "\n", "f = open(ID_RSA_PUB,'r')\n", "key = RSA.importKey(f.read())\n", "\n", "m = hashlib.md5()\n", "m.update(key.exportKey(format='DER'))\n", "\n", "print \":\".join([binascii.hexlify(x) for x in m.digest()])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "ac:89:8d:0c:df:31:56:ee:6e:78:1d:1e:93:00:52:2d\n" ] } ], "prompt_number": 1 }, { "cell_type": "code", "collapsed": false, "input": [ "# a public key is composed for exponent and the modulus\n", "\n", "key.key.e, key.key.n" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 2, "text": [ "(35L,\n", " 30114135277653090823710484015151796817301617075400240733128040544798698312920588403463603981769886992554796191996568892528138169118348632322047720739395956375745657272649606919479336601099168464203766012976604168428018544914226059685223932713785609109017712489953083792700211168583351462038902385714547623938709628695910501393773400681613556201339040761453113553103960871996644827110383060821448637902191317722721525710636601653187146323206401122522925096379961686500366444811043089699319022690405787790577166939257312013881740342345310346038558933025283068006936847503423602798229680773428578097891321345458334787503L)" ] } ], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": [ "# https://github.com/dlitz/pycrypto/blob/v2.6/lib/Crypto/PublicKey/RSA.py#L308\n", "\n", "key.exportKey('PEM')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 17, "text": [ "'-----BEGIN PUBLIC KEY-----\\nMIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA7ozGYIwEEXj10hAICsy+\\nqdBHYbV95cs7rSR8ZG+6te2aPL1Pg0e5lUJkyMao0T//LfFNBRwg0wqWsb7b6yUv\\nXmJAD3r5hjTCWlKJ/54APxInT/YMi08GVwSnwgVnib8zmjmfJ+6zFj5MsPTNPsdL\\n/DWxheyZ0oHDxL0rpcL192sdgu4//5j0oyk+w/rrm+jUMNpuOOGPINMZxEd6+OMX\\nr239ZLGSkWAavtMW1FKOXc/qrLHn03rNdz4cHZ1Gsx2++d8lk4jf8BzC0t3XXVuR\\nZNu8lhlzCMW6sUEG6uACnXYlnmQg597fcVUFXmdQ4I79PGndjy35FwdbmiLLe+bn\\nrwIBIw==\\n-----END PUBLIC KEY-----'" ] } ], "prompt_number": 17 }, { "cell_type": "code", "collapsed": false, "input": [ "print key.exportKey('OpenSSH')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7ozGYIwEEXj10hAICsy+qdBHYbV95cs7rSR8ZG+6te2aPL1Pg0e5lUJkyMao0T//LfFNBRwg0wqWsb7b6yUvXmJAD3r5hjTCWlKJ/54APxInT/YMi08GVwSnwgVnib8zmjmfJ+6zFj5MsPTNPsdL/DWxheyZ0oHDxL0rpcL192sdgu4//5j0oyk+w/rrm+jUMNpuOOGPINMZxEd6+OMXr239ZLGSkWAavtMW1FKOXc/qrLHn03rNdz4cHZ1Gsx2++d8lk4jf8BzC0t3XXVuRZNu8lhlzCMW6sUEG6uACnXYlnmQg597fcVUFXmdQ4I79PGndjy35FwdbmiLLe+bnrw==\n" ] } ], "prompt_number": 4 }, { "cell_type": "code", "collapsed": false, "input": [ "import base64" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "id_rsa_pub = open(ID_RSA_PUB).read()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "id_rsa_pub.split(None)[1]" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 7, "text": [ "'AAAAB3NzaC1yc2EAAAABIwAAAQEA7ozGYIwEEXj10hAICsy+qdBHYbV95cs7rSR8ZG+6te2aPL1Pg0e5lUJkyMao0T//LfFNBRwg0wqWsb7b6yUvXmJAD3r5hjTCWlKJ/54APxInT/YMi08GVwSnwgVnib8zmjmfJ+6zFj5MsPTNPsdL/DWxheyZ0oHDxL0rpcL192sdgu4//5j0oyk+w/rrm+jUMNpuOOGPINMZxEd6+OMXr239ZLGSkWAavtMW1FKOXc/qrLHn03rNdz4cHZ1Gsx2++d8lk4jf8BzC0t3XXVuRZNu8lhlzCMW6sUEG6uACnXYlnmQg597fcVUFXmdQ4I79PGndjy35FwdbmiLLe+bnrw=='" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "s = base64.b64decode(id_rsa_pub.split(None)[1])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 8 }, { "cell_type": "code", "collapsed": false, "input": [ "s" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 9, "text": [ "'\\x00\\x00\\x00\\x07ssh-rsa\\x00\\x00\\x00\\x01#\\x00\\x00\\x01\\x01\\x00\\xee\\x8c\\xc6`\\x8c\\x04\\x11x\\xf5\\xd2\\x10\\x08\\n\\xcc\\xbe\\xa9\\xd0Ga\\xb5}\\xe5\\xcb;\\xad$|do\\xba\\xb5\\xed\\x9a<\\xbdO\\x83G\\xb9\\x95Bd\\xc8\\xc6\\xa8\\xd1?\\xff-\\xf1M\\x05\\x1c \\xd3\\n\\x96\\xb1\\xbe\\xdb\\xeb%/^b@\\x0fz\\xf9\\x864\\xc2ZR\\x89\\xff\\x9e\\x00?\\x12\\'O\\xf6\\x0c\\x8bO\\x06W\\x04\\xa7\\xc2\\x05g\\x89\\xbf3\\x9a9\\x9f\\'\\xee\\xb3\\x16>L\\xb0\\xf4\\xcd>\\xc7K\\xfc5\\xb1\\x85\\xec\\x99\\xd2\\x81\\xc3\\xc4\\xbd+\\xa5\\xc2\\xf5\\xf7k\\x1d\\x82\\xee?\\xff\\x98\\xf4\\xa3)>\\xc3\\xfa\\xeb\\x9b\\xe8\\xd40\\xdan8\\xe1\\x8f \\xd3\\x19\\xc4Gz\\xf8\\xe3\\x17\\xafm\\xfdd\\xb1\\x92\\x91`\\x1a\\xbe\\xd3\\x16\\xd4R\\x8e]\\xcf\\xea\\xac\\xb1\\xe7\\xd3z\\xcdw>\\x1c\\x1d\\x9dF\\xb3\\x1d\\xbe\\xf9\\xdf%\\x93\\x88\\xdf\\xf0\\x1c\\xc2\\xd2\\xdd\\xd7][\\x91d\\xdb\\xbc\\x96\\x19s\\x08\\xc5\\xba\\xb1A\\x06\\xea\\xe0\\x02\\x9dv%\\x9ed \\xe7\\xde\\xdfqU\\x05^gP\\xe0\\x8e\\xfdI', keydata[:4])[0]\n", "\n", " # read in bytes\n", " data, keydata = keydata[4:dlen+4], keydata[4+dlen:]\n", "\n", " parts.append(data)\n", " \n", "e_val = eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in\n", " parts[1]]))\n", "n_val = eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in\n", " parts[2]]))\n", "\n", "\n", "\n", "pkcs1_seq = univ.Sequence()\n", "pkcs1_seq.setComponentByPosition(0, univ.Integer(n_val))\n", "pkcs1_seq.setComponentByPosition(1, univ.Integer(e_val))\n", "\n", "\n", "\n", "print '-----BEGIN RSA PUBLIC KEY-----'\n", "print base64.encodestring(der_encoder.encode(pkcs1_seq))\n", "print '-----END RSA PUBLIC KEY-----'" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "-----BEGIN RSA PUBLIC KEY-----\n", "MIIBCAKCAQEA7ozGYIwEEXj10hAICsy+qdBHYbV95cs7rSR8ZG+6te2aPL1Pg0e5lUJkyMao0T//\n", "LfFNBRwg0wqWsb7b6yUvXmJAD3r5hjTCWlKJ/54APxInT/YMi08GVwSnwgVnib8zmjmfJ+6zFj5M\n", "sPTNPsdL/DWxheyZ0oHDxL0rpcL192sdgu4//5j0oyk+w/rrm+jUMNpuOOGPINMZxEd6+OMXr239\n", "ZLGSkWAavtMW1FKOXc/qrLHn03rNdz4cHZ1Gsx2++d8lk4jf8BzC0t3XXVuRZNu8lhlzCMW6sUEG\n", "6uACnXYlnmQg597fcVUFXmdQ4I79PGndjy35FwdbmiLLe+bnrwIBIw==\n", "\n", "-----END RSA PUBLIC KEY-----\n" ] } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "n_val == key.key.n, e_val == key.key.e" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 11, "text": [ "(True, True)" ] } ], "prompt_number": 11 }, { "cell_type": "code", "collapsed": false, "input": [ "import hashlib\n", "m = hashlib.md5()\n", "m.update(base64.encodestring(key.exportKey(format='DER')))\n", "m.hexdigest()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 21, "text": [ "'bd3d18661b234de70ec52f8a1b558bdd'" ] } ], "prompt_number": 21 }, { "cell_type": "code", "collapsed": false, "input": [ "der_encoder.encode(pkcs1_seq, defMode=True)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 13, "text": [ "'0\\x82\\x01\\x08\\x02\\x82\\x01\\x01\\x00\\xee\\x8c\\xc6`\\x8c\\x04\\x11x\\xf5\\xd2\\x10\\x08\\n\\xcc\\xbe\\xa9\\xd0Ga\\xb5}\\xe5\\xcb;\\xad$|do\\xba\\xb5\\xed\\x9a<\\xbdO\\x83G\\xb9\\x95Bd\\xc8\\xc6\\xa8\\xd1?\\xff-\\xf1M\\x05\\x1c \\xd3\\n\\x96\\xb1\\xbe\\xdb\\xeb%/^b@\\x0fz\\xf9\\x864\\xc2ZR\\x89\\xff\\x9e\\x00?\\x12\\'O\\xf6\\x0c\\x8bO\\x06W\\x04\\xa7\\xc2\\x05g\\x89\\xbf3\\x9a9\\x9f\\'\\xee\\xb3\\x16>L\\xb0\\xf4\\xcd>\\xc7K\\xfc5\\xb1\\x85\\xec\\x99\\xd2\\x81\\xc3\\xc4\\xbd+\\xa5\\xc2\\xf5\\xf7k\\x1d\\x82\\xee?\\xff\\x98\\xf4\\xa3)>\\xc3\\xfa\\xeb\\x9b\\xe8\\xd40\\xdan8\\xe1\\x8f \\xd3\\x19\\xc4Gz\\xf8\\xe3\\x17\\xafm\\xfdd\\xb1\\x92\\x91`\\x1a\\xbe\\xd3\\x16\\xd4R\\x8e]\\xcf\\xea\\xac\\xb1\\xe7\\xd3z\\xcdw>\\x1c\\x1d\\x9dF\\xb3\\x1d\\xbe\\xf9\\xdf%\\x93\\x88\\xdf\\xf0\\x1c\\xc2\\xd2\\xdd\\xd7][\\x91d\\xdb\\xbc\\x96\\x19s\\x08\\xc5\\xba\\xb1A\\x06\\xea\\xe0\\x02\\x9dv%\\x9ed \\xe7\\xde\\xdfqU\\x05^gP\\xe0\\x8e\\xfd