1#!/usr/bin/env node 2// -*- mode: js -*- 3// vim: set filetype=javascript : 4// Copyright 2015 Joyent, Inc. All rights reserved. 5 6var dashdash = require('dashdash'); 7var sshpk = require('../lib/index'); 8var fs = require('fs'); 9var path = require('path'); 10var tty = require('tty'); 11var readline = require('readline'); 12var getPassword = require('getpass').getPass; 13 14var 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: ['comment', 'c'], 52 type: 'string', 53 help: 'Set key comment, if output format supports' 54 }, 55 { 56 names: ['help', 'h'], 57 type: 'bool', 58 help: 'Shows this help text' 59 } 60]; 61 62if (require.main === module) { 63 var parser = dashdash.createParser({ 64 options: options 65 }); 66 67 try { 68 var opts = parser.parse(process.argv); 69 } catch (e) { 70 console.error('sshpk-conv: error: %s', e.message); 71 process.exit(1); 72 } 73 74 if (opts.help || opts._args.length > 1) { 75 var help = parser.help({}).trimRight(); 76 console.error('sshpk-conv: converts between SSH key formats\n'); 77 console.error(help); 78 console.error('\navailable formats:'); 79 console.error(' - pem, pkcs1 eg id_rsa'); 80 console.error(' - ssh eg id_rsa.pub'); 81 console.error(' - pkcs8 format you want for openssl'); 82 console.error(' - openssh like output of ssh-keygen -o'); 83 console.error(' - rfc4253 raw OpenSSH wire format'); 84 console.error(' - dnssec dnssec-keygen format'); 85 process.exit(1); 86 } 87 88 /* 89 * Key derivation can only be done on private keys, so use of the -d 90 * option necessarily implies -p. 91 */ 92 if (opts.derive) 93 opts.private = true; 94 95 var inFile = process.stdin; 96 var inFileName = 'stdin'; 97 98 var inFilePath; 99 if (opts.file) { 100 inFilePath = opts.file; 101 } else if (opts._args.length === 1) { 102 inFilePath = opts._args[0]; 103 } 104 105 if (inFilePath) 106 inFileName = path.basename(inFilePath); 107 108 try { 109 if (inFilePath) { 110 fs.accessSync(inFilePath, fs.R_OK); 111 inFile = fs.createReadStream(inFilePath); 112 } 113 } catch (e) { 114 console.error('sshpk-conv: error opening input file' + 115 ': ' + e.name + ': ' + e.message); 116 process.exit(1); 117 } 118 119 var outFile = process.stdout; 120 121 try { 122 if (opts.out && !opts.identify) { 123 fs.accessSync(path.dirname(opts.out), fs.W_OK); 124 outFile = fs.createWriteStream(opts.out); 125 } 126 } catch (e) { 127 console.error('sshpk-conv: error opening output file' + 128 ': ' + e.name + ': ' + e.message); 129 process.exit(1); 130 } 131 132 var bufs = []; 133 inFile.on('readable', function () { 134 var data; 135 while ((data = inFile.read())) 136 bufs.push(data); 137 }); 138 var parseOpts = {}; 139 parseOpts.filename = inFileName; 140 inFile.on('end', function processKey() { 141 var buf = Buffer.concat(bufs); 142 var fmt = 'auto'; 143 if (opts.informat) 144 fmt = opts.informat; 145 var f = sshpk.parseKey; 146 if (opts.private) 147 f = sshpk.parsePrivateKey; 148 try { 149 var key = f(buf, fmt, parseOpts); 150 } catch (e) { 151 if (e.name === 'KeyEncryptedError') { 152 getPassword(function (err, pw) { 153 if (err) { 154 console.log('sshpk-conv: ' + 155 err.name + ': ' + 156 err.message); 157 process.exit(1); 158 } 159 parseOpts.passphrase = pw; 160 processKey(); 161 }); 162 return; 163 } 164 console.error('sshpk-conv: ' + 165 e.name + ': ' + e.message); 166 process.exit(1); 167 } 168 169 if (opts.derive) 170 key = key.derive(opts.derive); 171 172 if (opts.comment) 173 key.comment = opts.comment; 174 175 if (!opts.identify) { 176 fmt = undefined; 177 if (opts.outformat) 178 fmt = opts.outformat; 179 outFile.write(key.toBuffer(fmt)); 180 if (fmt === 'ssh' || 181 (!opts.private && fmt === undefined)) 182 outFile.write('\n'); 183 outFile.once('drain', function () { 184 process.exit(0); 185 }); 186 } else { 187 var kind = 'public'; 188 if (sshpk.PrivateKey.isPrivateKey(key)) 189 kind = 'private'; 190 console.log('%s: a %d bit %s %s key', inFileName, 191 key.size, key.type.toUpperCase(), kind); 192 if (key.type === 'ecdsa') 193 console.log('ECDSA curve: %s', key.curve); 194 if (key.comment) 195 console.log('Comment: %s', key.comment); 196 console.log('Fingerprint:'); 197 console.log(' ' + key.fingerprint().toString()); 198 console.log(' ' + key.fingerprint('md5').toString()); 199 process.exit(0); 200 } 201 }); 202} 203