private-key.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // Copyright 2017 Joyent, Inc.
  2. module.exports = PrivateKey;
  3. var assert = require('assert-plus');
  4. var algs = require('./algs');
  5. var crypto = require('crypto');
  6. var Fingerprint = require('./fingerprint');
  7. var Signature = require('./signature');
  8. var errs = require('./errors');
  9. var util = require('util');
  10. var utils = require('./utils');
  11. var dhe = require('./dhe');
  12. var generateECDSA = dhe.generateECDSA;
  13. var generateED25519 = dhe.generateED25519;
  14. var edCompat;
  15. var nacl;
  16. try {
  17. edCompat = require('./ed-compat');
  18. } catch (e) {
  19. /* Just continue through, and bail out if we try to use it. */
  20. }
  21. var Key = require('./key');
  22. var InvalidAlgorithmError = errs.InvalidAlgorithmError;
  23. var KeyParseError = errs.KeyParseError;
  24. var KeyEncryptedError = errs.KeyEncryptedError;
  25. var formats = {};
  26. formats['auto'] = require('./formats/auto');
  27. formats['pem'] = require('./formats/pem');
  28. formats['pkcs1'] = require('./formats/pkcs1');
  29. formats['pkcs8'] = require('./formats/pkcs8');
  30. formats['rfc4253'] = require('./formats/rfc4253');
  31. formats['ssh-private'] = require('./formats/ssh-private');
  32. formats['openssh'] = formats['ssh-private'];
  33. formats['ssh'] = formats['ssh-private'];
  34. function PrivateKey(opts) {
  35. assert.object(opts, 'options');
  36. Key.call(this, opts);
  37. this._pubCache = undefined;
  38. }
  39. util.inherits(PrivateKey, Key);
  40. PrivateKey.formats = formats;
  41. PrivateKey.prototype.toBuffer = function (format, options) {
  42. if (format === undefined)
  43. format = 'pkcs1';
  44. assert.string(format, 'format');
  45. assert.object(formats[format], 'formats[format]');
  46. assert.optionalObject(options, 'options');
  47. return (formats[format].write(this, options));
  48. };
  49. PrivateKey.prototype.hash = function (algo) {
  50. return (this.toPublic().hash(algo));
  51. };
  52. PrivateKey.prototype.toPublic = function () {
  53. if (this._pubCache)
  54. return (this._pubCache);
  55. var algInfo = algs.info[this.type];
  56. var pubParts = [];
  57. for (var i = 0; i < algInfo.parts.length; ++i) {
  58. var p = algInfo.parts[i];
  59. pubParts.push(this.part[p]);
  60. }
  61. this._pubCache = new Key({
  62. type: this.type,
  63. source: this,
  64. parts: pubParts
  65. });
  66. if (this.comment)
  67. this._pubCache.comment = this.comment;
  68. return (this._pubCache);
  69. };
  70. PrivateKey.prototype.derive = function (newType) {
  71. assert.string(newType, 'type');
  72. var priv, pub, pair;
  73. if (this.type === 'ed25519' && newType === 'curve25519') {
  74. if (nacl === undefined)
  75. nacl = require('tweetnacl');
  76. priv = this.part.r.data;
  77. if (priv[0] === 0x00)
  78. priv = priv.slice(1);
  79. priv = priv.slice(0, 32);
  80. pair = nacl.box.keyPair.fromSecretKey(new Uint8Array(priv));
  81. pub = new Buffer(pair.publicKey);
  82. priv = Buffer.concat([priv, pub]);
  83. return (new PrivateKey({
  84. type: 'curve25519',
  85. parts: [
  86. { name: 'R', data: utils.mpNormalize(pub) },
  87. { name: 'r', data: priv }
  88. ]
  89. }));
  90. } else if (this.type === 'curve25519' && newType === 'ed25519') {
  91. if (nacl === undefined)
  92. nacl = require('tweetnacl');
  93. priv = this.part.r.data;
  94. if (priv[0] === 0x00)
  95. priv = priv.slice(1);
  96. priv = priv.slice(0, 32);
  97. pair = nacl.sign.keyPair.fromSeed(new Uint8Array(priv));
  98. pub = new Buffer(pair.publicKey);
  99. priv = Buffer.concat([priv, pub]);
  100. return (new PrivateKey({
  101. type: 'ed25519',
  102. parts: [
  103. { name: 'R', data: utils.mpNormalize(pub) },
  104. { name: 'r', data: priv }
  105. ]
  106. }));
  107. }
  108. throw (new Error('Key derivation not supported from ' + this.type +
  109. ' to ' + newType));
  110. };
  111. PrivateKey.prototype.createVerify = function (hashAlgo) {
  112. return (this.toPublic().createVerify(hashAlgo));
  113. };
  114. PrivateKey.prototype.createSign = function (hashAlgo) {
  115. if (hashAlgo === undefined)
  116. hashAlgo = this.defaultHashAlgorithm();
  117. assert.string(hashAlgo, 'hash algorithm');
  118. /* ED25519 is not supported by OpenSSL, use a javascript impl. */
  119. if (this.type === 'ed25519' && edCompat !== undefined)
  120. return (new edCompat.Signer(this, hashAlgo));
  121. if (this.type === 'curve25519')
  122. throw (new Error('Curve25519 keys are not suitable for ' +
  123. 'signing or verification'));
  124. var v, nm, err;
  125. try {
  126. nm = hashAlgo.toUpperCase();
  127. v = crypto.createSign(nm);
  128. } catch (e) {
  129. err = e;
  130. }
  131. if (v === undefined || (err instanceof Error &&
  132. err.message.match(/Unknown message digest/))) {
  133. nm = 'RSA-';
  134. nm += hashAlgo.toUpperCase();
  135. v = crypto.createSign(nm);
  136. }
  137. assert.ok(v, 'failed to create verifier');
  138. var oldSign = v.sign.bind(v);
  139. var key = this.toBuffer('pkcs1');
  140. var type = this.type;
  141. var curve = this.curve;
  142. v.sign = function () {
  143. var sig = oldSign(key);
  144. if (typeof (sig) === 'string')
  145. sig = new Buffer(sig, 'binary');
  146. sig = Signature.parse(sig, type, 'asn1');
  147. sig.hashAlgorithm = hashAlgo;
  148. sig.curve = curve;
  149. return (sig);
  150. };
  151. return (v);
  152. };
  153. PrivateKey.parse = function (data, format, options) {
  154. if (typeof (data) !== 'string')
  155. assert.buffer(data, 'data');
  156. if (format === undefined)
  157. format = 'auto';
  158. assert.string(format, 'format');
  159. if (typeof (options) === 'string')
  160. options = { filename: options };
  161. assert.optionalObject(options, 'options');
  162. if (options === undefined)
  163. options = {};
  164. assert.optionalString(options.filename, 'options.filename');
  165. if (options.filename === undefined)
  166. options.filename = '(unnamed)';
  167. assert.object(formats[format], 'formats[format]');
  168. try {
  169. var k = formats[format].read(data, options);
  170. assert.ok(k instanceof PrivateKey, 'key is not a private key');
  171. if (!k.comment)
  172. k.comment = options.filename;
  173. return (k);
  174. } catch (e) {
  175. if (e.name === 'KeyEncryptedError')
  176. throw (e);
  177. throw (new KeyParseError(options.filename, format, e));
  178. }
  179. };
  180. PrivateKey.isPrivateKey = function (obj, ver) {
  181. return (utils.isCompatible(obj, PrivateKey, ver));
  182. };
  183. PrivateKey.generate = function (type, options) {
  184. if (options === undefined)
  185. options = {};
  186. assert.object(options, 'options');
  187. switch (type) {
  188. case 'ecdsa':
  189. if (options.curve === undefined)
  190. options.curve = 'nistp256';
  191. assert.string(options.curve, 'options.curve');
  192. return (generateECDSA(options.curve));
  193. case 'ed25519':
  194. return (generateED25519());
  195. default:
  196. throw (new Error('Key generation not supported with key ' +
  197. 'type "' + type + '"'));
  198. }
  199. };
  200. /*
  201. * API versions for PrivateKey:
  202. * [1,0] -- initial ver
  203. * [1,1] -- added auto, pkcs[18], openssh/ssh-private formats
  204. * [1,2] -- added defaultHashAlgorithm
  205. * [1,3] -- added derive, ed, createDH
  206. * [1,4] -- first tagged version
  207. */
  208. PrivateKey.prototype._sshpkApiVersion = [1, 4];
  209. PrivateKey._oldVersionDetect = function (obj) {
  210. assert.func(obj.toPublic);
  211. assert.func(obj.createSign);
  212. if (obj.derive)
  213. return ([1, 3]);
  214. if (obj.defaultHashAlgorithm)
  215. return ([1, 2]);
  216. if (obj.formats['auto'])
  217. return ([1, 1]);
  218. return ([1, 0]);
  219. };