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 getPassword = require('getpass').getPass; 11 12var options = [ 13 { 14 names: ['hash', 'H'], 15 type: 'string', 16 help: 'Hash algorithm (sha1, sha256, sha384, sha512)' 17 }, 18 { 19 names: ['verbose', 'v'], 20 type: 'bool', 21 help: 'Display verbose info about key and hash used' 22 }, 23 { 24 names: ['identity', 'i'], 25 type: 'string', 26 help: 'Path to key to use' 27 }, 28 { 29 names: ['file', 'f'], 30 type: 'string', 31 help: 'Input filename' 32 }, 33 { 34 names: ['out', 'o'], 35 type: 'string', 36 help: 'Output filename' 37 }, 38 { 39 names: ['format', 't'], 40 type: 'string', 41 help: 'Signature format (asn1, ssh, raw)' 42 }, 43 { 44 names: ['binary', 'b'], 45 type: 'bool', 46 help: 'Output raw binary instead of base64' 47 }, 48 { 49 names: ['help', 'h'], 50 type: 'bool', 51 help: 'Shows this help text' 52 } 53]; 54 55var parseOpts = {}; 56 57if (require.main === module) { 58 var parser = dashdash.createParser({ 59 options: options 60 }); 61 62 try { 63 var opts = parser.parse(process.argv); 64 } catch (e) { 65 console.error('sshpk-sign: error: %s', e.message); 66 process.exit(1); 67 } 68 69 if (opts.help || opts._args.length > 1) { 70 var help = parser.help({}).trimRight(); 71 console.error('sshpk-sign: sign data using an SSH key\n'); 72 console.error(help); 73 process.exit(1); 74 } 75 76 if (!opts.identity) { 77 var help = parser.help({}).trimRight(); 78 console.error('sshpk-sign: the -i or --identity option ' + 79 'is required\n'); 80 console.error(help); 81 process.exit(1); 82 } 83 84 var keyData = fs.readFileSync(opts.identity); 85 parseOpts.filename = opts.identity; 86 87 run(); 88} 89 90function run() { 91 var key; 92 try { 93 key = sshpk.parsePrivateKey(keyData, 'auto', parseOpts); 94 } catch (e) { 95 if (e.name === 'KeyEncryptedError') { 96 getPassword(function (err, pw) { 97 parseOpts.passphrase = pw; 98 run(); 99 }); 100 return; 101 } 102 console.error('sshpk-sign: error loading private key "' + 103 opts.identity + '": ' + e.name + ': ' + e.message); 104 process.exit(1); 105 } 106 107 var hash = opts.hash || key.defaultHashAlgorithm(); 108 109 var signer; 110 try { 111 signer = key.createSign(hash); 112 } catch (e) { 113 console.error('sshpk-sign: error creating signer: ' + 114 e.name + ': ' + e.message); 115 process.exit(1); 116 } 117 118 if (opts.verbose) { 119 console.error('sshpk-sign: using %s-%s with a %d bit key', 120 key.type, hash, key.size); 121 } 122 123 var inFile = process.stdin; 124 var inFileName = 'stdin'; 125 126 var inFilePath; 127 if (opts.file) { 128 inFilePath = opts.file; 129 } else if (opts._args.length === 1) { 130 inFilePath = opts._args[0]; 131 } 132 133 if (inFilePath) 134 inFileName = path.basename(inFilePath); 135 136 try { 137 if (inFilePath) { 138 fs.accessSync(inFilePath, fs.R_OK); 139 inFile = fs.createReadStream(inFilePath); 140 } 141 } catch (e) { 142 console.error('sshpk-sign: error opening input file' + 143 ': ' + e.name + ': ' + e.message); 144 process.exit(1); 145 } 146 147 var outFile = process.stdout; 148 149 try { 150 if (opts.out && !opts.identify) { 151 fs.accessSync(path.dirname(opts.out), fs.W_OK); 152 outFile = fs.createWriteStream(opts.out); 153 } 154 } catch (e) { 155 console.error('sshpk-sign: error opening output file' + 156 ': ' + e.name + ': ' + e.message); 157 process.exit(1); 158 } 159 160 inFile.pipe(signer); 161 inFile.on('end', function () { 162 var sig; 163 try { 164 sig = signer.sign(); 165 } catch (e) { 166 console.error('sshpk-sign: error signing data: ' + 167 e.name + ': ' + e.message); 168 process.exit(1); 169 } 170 171 var fmt = opts.format || 'asn1'; 172 var output; 173 try { 174 output = sig.toBuffer(fmt); 175 if (!opts.binary) 176 output = output.toString('base64'); 177 } catch (e) { 178 console.error('sshpk-sign: error converting signature' + 179 ' to ' + fmt + ' format: ' + e.name + ': ' + 180 e.message); 181 process.exit(1); 182 } 183 184 outFile.write(output); 185 if (!opts.binary) 186 outFile.write('\n'); 187 outFile.once('drain', function () { 188 process.exit(0); 189 }); 190 }); 191} 192