v1.0.0: generate ssh-keygen compatible fingerprints
This commit is contained in:
		
							parent
							
								
									6b6fd5e01d
								
							
						
					
					
						commit
						bf95bbb91c
					
				
							
								
								
									
										67
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								README.md
									
									
									
									
									
								
							@ -1,7 +1,7 @@
 | 
			
		||||
# SSH to JWK (for node.js)
 | 
			
		||||
# SSH Fingerprint (for node.js)
 | 
			
		||||
 | 
			
		||||
A minimal library to parse an SSH public key (`id_rsa.pub`)
 | 
			
		||||
and convert it into a public JWK.
 | 
			
		||||
A minimal library to read an SSH public key (`id_rsa.pub`)
 | 
			
		||||
and generate its fingerprint.
 | 
			
		||||
 | 
			
		||||
Works for RSA and ECDSA public keys.
 | 
			
		||||
 | 
			
		||||
@ -10,72 +10,53 @@ Features
 | 
			
		||||
 | 
			
		||||
< 100 lines of code | <1kb gzipped | 1.8kb minified | 3.1kb with comments
 | 
			
		||||
 | 
			
		||||
* [x] SSH Public Keys
 | 
			
		||||
  * fingerprint
 | 
			
		||||
* [x] RSA Public Keys
 | 
			
		||||
* [x] EC Public Keys
 | 
			
		||||
  * P-256 (prime256v1, secp256r1)
 | 
			
		||||
  * P-384 (secp384r1)
 | 
			
		||||
* [x] SSH Public Key SHA256 Fingerprints
 | 
			
		||||
  * RSA (2048-bit, 3072-bit, 4096-bit)
 | 
			
		||||
  * EC / ECDSA
 | 
			
		||||
    * P-256 (prime256v1, secp256r1)
 | 
			
		||||
    * P-384 (secp384r1)
 | 
			
		||||
* [x] Browser Version
 | 
			
		||||
  * [Bluecrypt SSH to JWK](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-to-jwk.js)
 | 
			
		||||
 | 
			
		||||
### Need JWK to SSH? SSH to PEM?
 | 
			
		||||
 | 
			
		||||
Try one of these:
 | 
			
		||||
 | 
			
		||||
* [jwk-to-ssh.js](https://git.coolaj86.com/coolaj86/jwk-to-ssh.js) (RSA + EC)
 | 
			
		||||
* [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js) (more EC utils)
 | 
			
		||||
* [Rasha.js](https://git.coolaj86.com/coolaj86/eckles.js) (more RSA utils)
 | 
			
		||||
 | 
			
		||||
### Need SSH Private Keys?
 | 
			
		||||
 | 
			
		||||
SSH private keys are just normal PEM files,
 | 
			
		||||
so you can use Eckles or Rasha, as mentioned above.
 | 
			
		||||
  * [Bluecrypt SSH Fingerprint](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-fingerprint.js)
 | 
			
		||||
 | 
			
		||||
# CLI
 | 
			
		||||
 | 
			
		||||
You can install `ssh-to-jwk` and use it from command line:
 | 
			
		||||
You can install `ssh-fingerprint` and use it from command line:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm install -g ssh-to-jwk
 | 
			
		||||
npm install -g ssh-fingerprint
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
ssh-to-jwk ~/.ssh/id_rsa.pub
 | 
			
		||||
ssh-fingerprint ~/.ssh/id_rsa.pub
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
2048 SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko root@localhost (RSA)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
(the output is the same as `ssh-keygen -lf ~/.ssh/id_rsa.pub`)
 | 
			
		||||
 | 
			
		||||
# Usage
 | 
			
		||||
 | 
			
		||||
You can also use it from JavaScript:
 | 
			
		||||
 | 
			
		||||
**SSH to JWK**
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
var sshtojwk = require('ssh-to-jwk');
 | 
			
		||||
 | 
			
		||||
var pub = fs.readFileSync("./id_rsa.pub");
 | 
			
		||||
var ssh = sshtojwk.parse({ pub: pub });
 | 
			
		||||
 | 
			
		||||
console.info(ssh.jwk);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**SSH Fingerprint**
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
var sshtojwk = require('ssh-to-jwk');
 | 
			
		||||
var SSH = require('ssh-fingerprint');
 | 
			
		||||
var pub = fs.readFileSync("./id_rsa.pub");
 | 
			
		||||
 | 
			
		||||
sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) {
 | 
			
		||||
  console.info(fingerprint);
 | 
			
		||||
  // SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko
 | 
			
		||||
SSH.fingerprint({ pub: pub }).then(function (fing) {
 | 
			
		||||
  console.info('The key fingerprint is:');
 | 
			
		||||
  console.info(fing.fingerprint, fing.comment);
 | 
			
		||||
  // SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko root@localhost
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Legal
 | 
			
		||||
 | 
			
		||||
[ssh-to-jwk.js](https://git.coolaj86.com/coolaj86/ssh-to-jwk.js) |
 | 
			
		||||
[ssh-fingerprint.js](https://git.coolaj86.com/coolaj86/ssh-fingerprint.js) |
 | 
			
		||||
MPL-2.0 |
 | 
			
		||||
[Terms of Use](https://therootcompany.com/legal/#terms) |
 | 
			
		||||
[Privacy Policy](https://therootcompany.com/legal/#privacy)
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,9 @@ if (!pubfile) {
 | 
			
		||||
 | 
			
		||||
var buf = fs.readFileSync(pubfile);
 | 
			
		||||
var pub = buf.toString('ascii');
 | 
			
		||||
var ssh = sshtojwk.parse({ pub: pub });
 | 
			
		||||
 | 
			
		||||
// Finally! https://superuser.com/a/714195
 | 
			
		||||
sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) {
 | 
			
		||||
  console.warn('The key fingerprint is:\n' + fingerprint + ' ' + ssh.comment);
 | 
			
		||||
  console.info(JSON.stringify(ssh.jwk, null, 2));
 | 
			
		||||
sshtojwk.fingerprint({ pub: pub }).then(function (fing) {
 | 
			
		||||
  var kty = ('RSA' === fing.kty ? 'RSA' : 'ECDSA');
 | 
			
		||||
  console.info((fing.size*8).toString(), fing.fingerprint, fing.comment, '(' + kty + ')');
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										1
									
								
								fixtures/pub-ec-p256.ssh.pub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								fixtures/pub-ec-p256.ssh.pub
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOWdeo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGMo= P-256@localhost
 | 
			
		||||
							
								
								
									
										1
									
								
								fixtures/pub-ec-p384.ssh.pub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								fixtures/pub-ec-p384.ssh.pub
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNsxFNGygmu3oyiyCfKDxpy4aoccor+P8N/CtmtEjwunbEnff4JTSfJXKr9LH3+Rm1Q+I57vN0urHJ+v03gI9xGTkRBmzrOnc6FaWroJ/l0DDMgvTuFS2wwxRgWUyZTLGw== P-384@localhost
 | 
			
		||||
							
								
								
									
										1
									
								
								fixtures/pub-rsa-2048.ssh.pub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								fixtures/pub-rsa-2048.ssh.pub
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCba21UHE+VbDTpmYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/RJSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZtYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYNIP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Eamz/nT4I3 rsa@localhost
 | 
			
		||||
							
								
								
									
										2
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								index.js
									
									
									
									
									
								
							@ -1 +1 @@
 | 
			
		||||
module.exports = require('./lib/ssh-parser.js');
 | 
			
		||||
module.exports = require('./lib/ssh-fingerprint.js');
 | 
			
		||||
 | 
			
		||||
@ -3,14 +3,6 @@
 | 
			
		||||
var SSH = module.exports;
 | 
			
		||||
var Enc = require('./encoding.js');
 | 
			
		||||
 | 
			
		||||
SSH.parse = function (opts) {
 | 
			
		||||
  var pub = opts.pub || opts;
 | 
			
		||||
  var ssh = SSH.parseBlock(pub);
 | 
			
		||||
  ssh = SSH.parseElements(ssh);
 | 
			
		||||
  //delete ssh.bytes;
 | 
			
		||||
  return SSH.parsePublicKey(ssh);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
SSH.fingerprint = function (opts) {
 | 
			
		||||
  var ssh;
 | 
			
		||||
@ -19,10 +11,23 @@ SSH.fingerprint = function (opts) {
 | 
			
		||||
  } else {
 | 
			
		||||
    ssh = SSH.parseBlock(opts.pub);
 | 
			
		||||
  }
 | 
			
		||||
  ssh = SSH.parseElements(ssh);
 | 
			
		||||
 | 
			
		||||
  // for browser compat
 | 
			
		||||
  return Promise.resolve().then(function () {
 | 
			
		||||
    return 'SHA256:' + require('crypto').createHash('sha256')
 | 
			
		||||
      .update(ssh.bytes).digest('base64').replace(/=+$/g, '');
 | 
			
		||||
    var digest = require('crypto').createHash('sha256')
 | 
			
		||||
      .update(ssh.bytes).digest();
 | 
			
		||||
    // 2048 SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko rsa@localhost (RSA)
 | 
			
		||||
    // 256 SHA256:wcH95nkL7ZkeRURHpLuoThIBEIIKkYgf9etD18PIx40 P-256@localhost (ECDSA)
 | 
			
		||||
    ssh = SSH.parseKeyType(ssh);
 | 
			
		||||
    return {
 | 
			
		||||
      type: 'sha256'
 | 
			
		||||
    , digest: digest
 | 
			
		||||
    , fingerprint: 'SHA256:' + digest.toString('base64').replace(/=+$/g, '')
 | 
			
		||||
    , size: ssh.size
 | 
			
		||||
    , comment: ssh.comment
 | 
			
		||||
    , kty: ssh.kty
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -71,44 +76,29 @@ SSH.parseElements = function (ssh) {
 | 
			
		||||
  return ssh;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SSH.parsePublicKey = function (ssh) {
 | 
			
		||||
SSH.parseKeyType = function (ssh) {
 | 
			
		||||
  var els = ssh.elements;
 | 
			
		||||
  var typ = Enc.bufToBin(els[0]);
 | 
			
		||||
  var len;
 | 
			
		||||
 | 
			
		||||
  // RSA keys are all the same
 | 
			
		||||
  if (SSH.types.rsa === typ) {
 | 
			
		||||
    ssh.jwk = {
 | 
			
		||||
      kty: 'RSA'
 | 
			
		||||
    , n: Enc.bufToUrlBase64(els[2])
 | 
			
		||||
    , e: Enc.bufToUrlBase64(els[1])
 | 
			
		||||
    };
 | 
			
		||||
    ssh.kty = 'RSA';
 | 
			
		||||
    ssh.size = (ssh.elements[2].byteLength || ssh.lements[2].length);
 | 
			
		||||
    return ssh;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // EC keys are each different
 | 
			
		||||
  if (SSH.types.p256 === typ) {
 | 
			
		||||
    len = 32;
 | 
			
		||||
    ssh.jwk = { kty: 'EC', crv: 'P-256' };
 | 
			
		||||
    ssh.kty = 'EC';
 | 
			
		||||
    ssh.size = 32;
 | 
			
		||||
  } else if (SSH.types.p384 === typ) {
 | 
			
		||||
    len = 48;
 | 
			
		||||
    ssh.jwk = { kty: 'EC', crv: 'P-384' };
 | 
			
		||||
    ssh.kty = 'EC';
 | 
			
		||||
    ssh.size = 48;
 | 
			
		||||
  } else {
 | 
			
		||||
    throw new Error("Unsupported ssh public key type: "
 | 
			
		||||
      + Enc.bufToBin(els[0]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // els[1] is just a repeat of a subset of els[0]
 | 
			
		||||
  var x = els[2].slice(1, 1 + len);
 | 
			
		||||
  var y = els[2].slice(1 + len, 1 + len + len);
 | 
			
		||||
 | 
			
		||||
  // I don't think EC keys use 0x00 padding, but just in case
 | 
			
		||||
  while (0x00 === x[0]) { x = x.slice(1); }
 | 
			
		||||
  while (0x00 === y[0]) { y = y.slice(1); }
 | 
			
		||||
 | 
			
		||||
  ssh.jwk.x = Enc.bufToUrlBase64(x);
 | 
			
		||||
  ssh.jwk.y = Enc.bufToUrlBase64(y);
 | 
			
		||||
 | 
			
		||||
  return ssh;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								package.json
									
									
									
									
									
								
							@ -1,11 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "ssh-to-jwk",
 | 
			
		||||
  "version": "1.1.0",
 | 
			
		||||
  "description": "💯 SSH to JWK in a lightweight, zero-dependency library.",
 | 
			
		||||
  "homepage": "https://git.coolaj86.com/coolaj86/ssh-to-jwk.js",
 | 
			
		||||
  "name": "ssh-fingerprint",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "💯 SSH Fingerprint in a lightweight, zero-dependency VanillaJS node library.",
 | 
			
		||||
  "homepage": "https://git.coolaj86.com/coolaj86/ssh-fingerprint.js",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "bin": {
 | 
			
		||||
    "ssh-to-jwk": "bin/ssh-to-jwk.js"
 | 
			
		||||
    "ssh-fingerprint": "bin/ssh-fingerprint.js"
 | 
			
		||||
  },
 | 
			
		||||
  "files": [
 | 
			
		||||
    "bin",
 | 
			
		||||
@ -21,17 +21,14 @@
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "https://git.coolaj86.com/coolaj86/ssh-to-jwk.js"
 | 
			
		||||
    "url": "https://git.coolaj86.com/coolaj86/ssh-fingerprint.js"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "zero-dependency",
 | 
			
		||||
    "SSH-to-JWK",
 | 
			
		||||
    "RSA",
 | 
			
		||||
    "EC",
 | 
			
		||||
    "SSH",
 | 
			
		||||
    "fingerprint",
 | 
			
		||||
    "JWK",
 | 
			
		||||
    "ECDSA"
 | 
			
		||||
    "Fingerprint",
 | 
			
		||||
    "rsa",
 | 
			
		||||
    "ec",
 | 
			
		||||
    "ecdsa"
 | 
			
		||||
  ],
 | 
			
		||||
  "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
			
		||||
  "license": "MPL-2.0"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								test.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										23
									
								
								test.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
echo "Test RSA key"
 | 
			
		||||
ssh-keygen -lf fixtures/pub-rsa-2048.ssh.pub | tee fingerprint.txt
 | 
			
		||||
node bin/ssh-fingerprint.js fixtures/pub-rsa-2048.ssh.pub > fingerprint.1.txt
 | 
			
		||||
diff fingerprint.txt fingerprint.1.txt
 | 
			
		||||
echo ""
 | 
			
		||||
 | 
			
		||||
echo "Test EC P-256 key"
 | 
			
		||||
ssh-keygen -lf fixtures/pub-ec-p256.ssh.pub | tee fingerprint.txt
 | 
			
		||||
node bin/ssh-fingerprint.js fixtures/pub-ec-p256.ssh.pub > fingerprint.1.txt
 | 
			
		||||
diff fingerprint.txt fingerprint.1.txt
 | 
			
		||||
echo ""
 | 
			
		||||
 | 
			
		||||
echo "Test EC P-384 key"
 | 
			
		||||
ssh-keygen -lf fixtures/pub-ec-p384.ssh.pub | tee fingerprint.txt
 | 
			
		||||
node bin/ssh-fingerprint.js fixtures/pub-ec-p384.ssh.pub > fingerprint.1.txt
 | 
			
		||||
diff fingerprint.txt fingerprint.1.txt
 | 
			
		||||
echo ""
 | 
			
		||||
 | 
			
		||||
echo "All Tests Passed"
 | 
			
		||||
echo "• generated fingerprints of fixtures match ssh-keygen output"
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user