sshpk-conv (5738B)
1 #!/usr/bin/env node 2 // -*- mode: js -*- 3 // vim: set filetype=javascript : 4 // Copyright 2018 Joyent, Inc. All rights reserved. 5 6 var dashdash = require('dashdash'); 7 var sshpk = require('../lib/index'); 8 var fs = require('fs'); 9 var path = require('path'); 10 var tty = require('tty'); 11 var readline = require('readline'); 12 var getPassword = require('getpass').getPass; 13 14 var options = [ 15 { 16 names: ['outformat', 't'], 17 type: 'string', 18 help: 'Output format' 19 }, 20 { 21 names: ['informat', 'T'], 22 type: 'string', 23 help: 'Input format' 24 }, 25 { 26 names: ['file', 'f'], 27 type: 'string', 28 help: 'Input file name (default stdin)' 29 }, 30 { 31 names: ['out', 'o'], 32 type: 'string', 33 help: 'Output file name (default stdout)' 34 }, 35 { 36 names: ['private', 'p'], 37 type: 'bool', 38 help: 'Produce a private key as output' 39 }, 40 { 41 names: ['derive', 'd'], 42 type: 'string', 43 help: 'Output a new key derived from this one, with given algo' 44 }, 45 { 46 names: ['identify', 'i'], 47 type: 'bool', 48 help: 'Print key metadata instead of converting' 49 }, 50 { 51 names: ['fingerprint', 'F'], 52 type: 'bool', 53 help: 'Output key fingerprint' 54 }, 55 { 56 names: ['hash', 'H'], 57 type: 'string', 58 help: 'Hash function to use for key fingeprint with -F' 59 }, 60 { 61 names: ['spki', 's'], 62 type: 'bool', 63 help: 'With -F, generates an SPKI fingerprint instead of SSH' 64 }, 65 { 66 names: ['comment', 'c'], 67 type: 'string', 68 help: 'Set key comment, if output format supports' 69 }, 70 { 71 names: ['help', 'h'], 72 type: 'bool', 73 help: 'Shows this help text' 74 } 75 ]; 76 77 if (require.main === module) { 78 var parser = dashdash.createParser({ 79 options: options 80 }); 81 82 try { 83 var opts = parser.parse(process.argv); 84 } catch (e) { 85 console.error('sshpk-conv: error: %s', e.message); 86 process.exit(1); 87 } 88 89 if (opts.help || opts._args.length > 1) { 90 var help = parser.help({}).trimRight(); 91 console.error('sshpk-conv: converts between SSH key formats\n'); 92 console.error(help); 93 console.error('\navailable key formats:'); 94 console.error(' - pem, pkcs1 eg id_rsa'); 95 console.error(' - ssh eg id_rsa.pub'); 96 console.error(' - pkcs8 format you want for openssl'); 97 console.error(' - openssh like output of ssh-keygen -o'); 98 console.error(' - rfc4253 raw OpenSSH wire format'); 99 console.error(' - dnssec dnssec-keygen format'); 100 console.error(' - putty PuTTY ppk format'); 101 console.error('\navailable fingerprint formats:'); 102 console.error(' - hex colon-separated hex for SSH'); 103 console.error(' straight hex for SPKI'); 104 console.error(' - base64 SHA256:* format from OpenSSH'); 105 process.exit(1); 106 } 107 108 /* 109 * Key derivation can only be done on private keys, so use of the -d 110 * option necessarily implies -p. 111 */ 112 if (opts.derive) 113 opts.private = true; 114 115 var inFile = process.stdin; 116 var inFileName = 'stdin'; 117 118 var inFilePath; 119 if (opts.file) { 120 inFilePath = opts.file; 121 } else if (opts._args.length === 1) { 122 inFilePath = opts._args[0]; 123 } 124 125 if (inFilePath) 126 inFileName = path.basename(inFilePath); 127 128 try { 129 if (inFilePath) { 130 fs.accessSync(inFilePath, fs.R_OK); 131 inFile = fs.createReadStream(inFilePath); 132 } 133 } catch (e) { 134 ifError(e, 'error opening input file'); 135 } 136 137 var outFile = process.stdout; 138 139 try { 140 if (opts.out && !opts.identify) { 141 fs.accessSync(path.dirname(opts.out), fs.W_OK); 142 outFile = fs.createWriteStream(opts.out); 143 } 144 } catch (e) { 145 ifError(e, 'error opening output file'); 146 } 147 148 var bufs = []; 149 inFile.on('readable', function () { 150 var data; 151 while ((data = inFile.read())) 152 bufs.push(data); 153 }); 154 var parseOpts = {}; 155 parseOpts.filename = inFileName; 156 inFile.on('end', function processKey() { 157 var buf = Buffer.concat(bufs); 158 var fmt = 'auto'; 159 if (opts.informat) 160 fmt = opts.informat; 161 var f = sshpk.parseKey; 162 if (opts.private) 163 f = sshpk.parsePrivateKey; 164 try { 165 var key = f(buf, fmt, parseOpts); 166 } catch (e) { 167 if (e.name === 'KeyEncryptedError') { 168 getPassword(function (err, pw) { 169 if (err) 170 ifError(err); 171 parseOpts.passphrase = pw; 172 processKey(); 173 }); 174 return; 175 } 176 ifError(e); 177 } 178 179 if (opts.derive) 180 key = key.derive(opts.derive); 181 182 if (opts.comment) 183 key.comment = opts.comment; 184 185 if (opts.identify) { 186 var kind = 'public'; 187 if (sshpk.PrivateKey.isPrivateKey(key)) 188 kind = 'private'; 189 console.log('%s: a %d bit %s %s key', inFileName, 190 key.size, key.type.toUpperCase(), kind); 191 if (key.type === 'ecdsa') 192 console.log('ECDSA curve: %s', key.curve); 193 if (key.comment) 194 console.log('Comment: %s', key.comment); 195 console.log('SHA256 fingerprint: ' + 196 key.fingerprint('sha256').toString()); 197 console.log('MD5 fingerprint: ' + 198 key.fingerprint('md5').toString()); 199 console.log('SPKI-SHA256 fingerprint: ' + 200 key.fingerprint('sha256', 'spki').toString()); 201 process.exit(0); 202 return; 203 } 204 205 if (opts.fingerprint) { 206 var hash = opts.hash; 207 var type = opts.spki ? 'spki' : 'ssh'; 208 var format = opts.outformat; 209 var fp = key.fingerprint(hash, type).toString(format); 210 outFile.write(fp); 211 outFile.write('\n'); 212 outFile.once('drain', function () { 213 process.exit(0); 214 }); 215 return; 216 } 217 218 fmt = undefined; 219 if (opts.outformat) 220 fmt = opts.outformat; 221 outFile.write(key.toBuffer(fmt)); 222 if (fmt === 'ssh' || 223 (!opts.private && fmt === undefined)) 224 outFile.write('\n'); 225 outFile.once('drain', function () { 226 process.exit(0); 227 }); 228 }); 229 } 230 231 function ifError(e, txt) { 232 if (txt) 233 txt = txt + ': '; 234 else 235 txt = ''; 236 console.error('sshpk-conv: ' + txt + e.name + ': ' + e.message); 237 if (process.env['DEBUG'] || process.env['V']) { 238 console.error(e.stack); 239 if (e.innerErr) 240 console.error(e.innerErr.stack); 241 } 242 process.exit(1); 243 }