private-key.js 5.9 KB

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