x509.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. // Copyright 2016 Joyent, Inc.
  2. module.exports = {
  3. read: read,
  4. verify: verify,
  5. sign: sign,
  6. write: write
  7. };
  8. var assert = require('assert-plus');
  9. var asn1 = require('asn1');
  10. var algs = require('../algs');
  11. var utils = require('../utils');
  12. var Key = require('../key');
  13. var PrivateKey = require('../private-key');
  14. var pem = require('./pem');
  15. var Identity = require('../identity');
  16. var Signature = require('../signature');
  17. var Certificate = require('../certificate');
  18. var pkcs8 = require('./pkcs8');
  19. /*
  20. * This file is based on RFC5280 (X.509).
  21. */
  22. /* Helper to read in a single mpint */
  23. function readMPInt(der, nm) {
  24. assert.strictEqual(der.peek(), asn1.Ber.Integer,
  25. nm + ' is not an Integer');
  26. return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true)));
  27. }
  28. function verify(cert, key) {
  29. var sig = cert.signatures.x509;
  30. assert.object(sig, 'x509 signature');
  31. var algParts = sig.algo.split('-');
  32. if (algParts[0] !== key.type)
  33. return (false);
  34. var blob = sig.cache;
  35. if (blob === undefined) {
  36. var der = new asn1.BerWriter();
  37. writeTBSCert(cert, der);
  38. blob = der.buffer;
  39. }
  40. var verifier = key.createVerify(algParts[1]);
  41. verifier.write(blob);
  42. return (verifier.verify(sig.signature));
  43. }
  44. function Local(i) {
  45. return (asn1.Ber.Context | asn1.Ber.Constructor | i);
  46. }
  47. function Context(i) {
  48. return (asn1.Ber.Context | i);
  49. }
  50. var SIGN_ALGS = {
  51. 'rsa-md5': '1.2.840.113549.1.1.4',
  52. 'rsa-sha1': '1.2.840.113549.1.1.5',
  53. 'rsa-sha256': '1.2.840.113549.1.1.11',
  54. 'rsa-sha384': '1.2.840.113549.1.1.12',
  55. 'rsa-sha512': '1.2.840.113549.1.1.13',
  56. 'dsa-sha1': '1.2.840.10040.4.3',
  57. 'dsa-sha256': '2.16.840.1.101.3.4.3.2',
  58. 'ecdsa-sha1': '1.2.840.10045.4.1',
  59. 'ecdsa-sha256': '1.2.840.10045.4.3.2',
  60. 'ecdsa-sha384': '1.2.840.10045.4.3.3',
  61. 'ecdsa-sha512': '1.2.840.10045.4.3.4'
  62. };
  63. Object.keys(SIGN_ALGS).forEach(function (k) {
  64. SIGN_ALGS[SIGN_ALGS[k]] = k;
  65. });
  66. SIGN_ALGS['1.3.14.3.2.3'] = 'rsa-md5';
  67. SIGN_ALGS['1.3.14.3.2.29'] = 'rsa-sha1';
  68. var EXTS = {
  69. 'issuerKeyId': '2.5.29.35',
  70. 'altName': '2.5.29.17'
  71. };
  72. function read(buf, options) {
  73. if (typeof (buf) === 'string') {
  74. buf = new Buffer(buf, 'binary');
  75. }
  76. assert.buffer(buf, 'buf');
  77. var der = new asn1.BerReader(buf);
  78. der.readSequence();
  79. if (Math.abs(der.length - der.remain) > 1) {
  80. throw (new Error('DER sequence does not contain whole byte ' +
  81. 'stream'));
  82. }
  83. var tbsStart = der.offset;
  84. der.readSequence();
  85. var sigOffset = der.offset + der.length;
  86. var tbsEnd = sigOffset;
  87. if (der.peek() === Local(0)) {
  88. der.readSequence(Local(0));
  89. var version = der.readInt();
  90. assert.ok(version <= 3,
  91. 'only x.509 versions up to v3 supported');
  92. }
  93. var cert = {};
  94. cert.signatures = {};
  95. var sig = (cert.signatures.x509 = {});
  96. sig.extras = {};
  97. cert.serial = readMPInt(der, 'serial');
  98. der.readSequence();
  99. var after = der.offset + der.length;
  100. var certAlgOid = der.readOID();
  101. var certAlg = SIGN_ALGS[certAlgOid];
  102. if (certAlg === undefined)
  103. throw (new Error('unknown signature algorithm ' + certAlgOid));
  104. der._offset = after;
  105. cert.issuer = Identity.parseAsn1(der);
  106. der.readSequence();
  107. cert.validFrom = readDate(der);
  108. cert.validUntil = readDate(der);
  109. cert.subjects = [Identity.parseAsn1(der)];
  110. der.readSequence();
  111. after = der.offset + der.length;
  112. cert.subjectKey = pkcs8.readPkcs8(undefined, 'public', der);
  113. der._offset = after;
  114. /* issuerUniqueID */
  115. if (der.peek() === Local(1)) {
  116. der.readSequence(Local(1));
  117. sig.extras.issuerUniqueID =
  118. buf.slice(der.offset, der.offset + der.length);
  119. der._offset += der.length;
  120. }
  121. /* subjectUniqueID */
  122. if (der.peek() === Local(2)) {
  123. der.readSequence(Local(2));
  124. sig.extras.subjectUniqueID =
  125. buf.slice(der.offset, der.offset + der.length);
  126. der._offset += der.length;
  127. }
  128. /* extensions */
  129. if (der.peek() === Local(3)) {
  130. der.readSequence(Local(3));
  131. var extEnd = der.offset + der.length;
  132. der.readSequence();
  133. while (der.offset < extEnd)
  134. readExtension(cert, buf, der);
  135. assert.strictEqual(der.offset, extEnd);
  136. }
  137. assert.strictEqual(der.offset, sigOffset);
  138. der.readSequence();
  139. after = der.offset + der.length;
  140. var sigAlgOid = der.readOID();
  141. var sigAlg = SIGN_ALGS[sigAlgOid];
  142. if (sigAlg === undefined)
  143. throw (new Error('unknown signature algorithm ' + sigAlgOid));
  144. der._offset = after;
  145. var sigData = der.readString(asn1.Ber.BitString, true);
  146. if (sigData[0] === 0)
  147. sigData = sigData.slice(1);
  148. var algParts = sigAlg.split('-');
  149. sig.signature = Signature.parse(sigData, algParts[0], 'asn1');
  150. sig.signature.hashAlgorithm = algParts[1];
  151. sig.algo = sigAlg;
  152. sig.cache = buf.slice(tbsStart, tbsEnd);
  153. return (new Certificate(cert));
  154. }
  155. function readDate(der) {
  156. if (der.peek() === asn1.Ber.UTCTime) {
  157. return (utcTimeToDate(der.readString(asn1.Ber.UTCTime)));
  158. } else if (der.peek() === asn1.Ber.GeneralizedTime) {
  159. return (gTimeToDate(der.readString(asn1.Ber.GeneralizedTime)));
  160. } else {
  161. throw (new Error('Unsupported date format'));
  162. }
  163. }
  164. /* RFC5280, section 4.2.1.6 (GeneralName type) */
  165. var ALTNAME = {
  166. OtherName: Local(0),
  167. RFC822Name: Context(1),
  168. DNSName: Context(2),
  169. X400Address: Local(3),
  170. DirectoryName: Local(4),
  171. EDIPartyName: Local(5),
  172. URI: Context(6),
  173. IPAddress: Context(7),
  174. OID: Context(8)
  175. };
  176. function readExtension(cert, buf, der) {
  177. der.readSequence();
  178. var after = der.offset + der.length;
  179. var extId = der.readOID();
  180. var id;
  181. var sig = cert.signatures.x509;
  182. sig.extras.exts = [];
  183. var critical;
  184. if (der.peek() === asn1.Ber.Boolean)
  185. critical = der.readBoolean();
  186. switch (extId) {
  187. case (EXTS.altName):
  188. der.readSequence(asn1.Ber.OctetString);
  189. der.readSequence();
  190. var aeEnd = der.offset + der.length;
  191. while (der.offset < aeEnd) {
  192. switch (der.peek()) {
  193. case ALTNAME.OtherName:
  194. case ALTNAME.EDIPartyName:
  195. der.readSequence();
  196. der._offset += der.length;
  197. break;
  198. case ALTNAME.OID:
  199. der.readOID(ALTNAME.OID);
  200. break;
  201. case ALTNAME.RFC822Name:
  202. /* RFC822 specifies email addresses */
  203. var email = der.readString(ALTNAME.RFC822Name);
  204. id = Identity.forEmail(email);
  205. if (!cert.subjects[0].equals(id))
  206. cert.subjects.push(id);
  207. break;
  208. case ALTNAME.DirectoryName:
  209. der.readSequence(ALTNAME.DirectoryName);
  210. id = Identity.parseAsn1(der);
  211. if (!cert.subjects[0].equals(id))
  212. cert.subjects.push(id);
  213. break;
  214. case ALTNAME.DNSName:
  215. var host = der.readString(
  216. ALTNAME.DNSName);
  217. id = Identity.forHost(host);
  218. if (!cert.subjects[0].equals(id))
  219. cert.subjects.push(id);
  220. break;
  221. default:
  222. der.readString(der.peek());
  223. break;
  224. }
  225. }
  226. sig.extras.exts.push({ oid: extId, critical: critical });
  227. break;
  228. default:
  229. sig.extras.exts.push({
  230. oid: extId,
  231. critical: critical,
  232. data: der.readString(asn1.Ber.OctetString, true)
  233. });
  234. break;
  235. }
  236. der._offset = after;
  237. }
  238. var UTCTIME_RE =
  239. /^([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/;
  240. function utcTimeToDate(t) {
  241. var m = t.match(UTCTIME_RE);
  242. assert.ok(m, 'timestamps must be in UTC');
  243. var d = new Date();
  244. var thisYear = d.getUTCFullYear();
  245. var century = Math.floor(thisYear / 100) * 100;
  246. var year = parseInt(m[1], 10);
  247. if (thisYear % 100 < 50 && year >= 60)
  248. year += (century - 1);
  249. else
  250. year += century;
  251. d.setUTCFullYear(year, parseInt(m[2], 10) - 1, parseInt(m[3], 10));
  252. d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10));
  253. if (m[6] && m[6].length > 0)
  254. d.setUTCSeconds(parseInt(m[6], 10));
  255. return (d);
  256. }
  257. var GTIME_RE =
  258. /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/;
  259. function gTimeToDate(t) {
  260. var m = t.match(GTIME_RE);
  261. assert.ok(m);
  262. var d = new Date();
  263. d.setUTCFullYear(parseInt(m[1], 10), parseInt(m[2], 10) - 1,
  264. parseInt(m[3], 10));
  265. d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10));
  266. if (m[6] && m[6].length > 0)
  267. d.setUTCSeconds(parseInt(m[6], 10));
  268. return (d);
  269. }
  270. function zeroPad(n) {
  271. var s = '' + n;
  272. while (s.length < 2)
  273. s = '0' + s;
  274. return (s);
  275. }
  276. function dateToUTCTime(d) {
  277. var s = '';
  278. s += zeroPad(d.getUTCFullYear() % 100);
  279. s += zeroPad(d.getUTCMonth() + 1);
  280. s += zeroPad(d.getUTCDate());
  281. s += zeroPad(d.getUTCHours());
  282. s += zeroPad(d.getUTCMinutes());
  283. s += zeroPad(d.getUTCSeconds());
  284. s += 'Z';
  285. return (s);
  286. }
  287. function sign(cert, key) {
  288. if (cert.signatures.x509 === undefined)
  289. cert.signatures.x509 = {};
  290. var sig = cert.signatures.x509;
  291. sig.algo = key.type + '-' + key.defaultHashAlgorithm();
  292. if (SIGN_ALGS[sig.algo] === undefined)
  293. return (false);
  294. var der = new asn1.BerWriter();
  295. writeTBSCert(cert, der);
  296. var blob = der.buffer;
  297. sig.cache = blob;
  298. var signer = key.createSign();
  299. signer.write(blob);
  300. cert.signatures.x509.signature = signer.sign();
  301. return (true);
  302. }
  303. function write(cert, options) {
  304. var sig = cert.signatures.x509;
  305. assert.object(sig, 'x509 signature');
  306. var der = new asn1.BerWriter();
  307. der.startSequence();
  308. if (sig.cache) {
  309. der._ensure(sig.cache.length);
  310. sig.cache.copy(der._buf, der._offset);
  311. der._offset += sig.cache.length;
  312. } else {
  313. writeTBSCert(cert, der);
  314. }
  315. der.startSequence();
  316. der.writeOID(SIGN_ALGS[sig.algo]);
  317. if (sig.algo.match(/^rsa-/))
  318. der.writeNull();
  319. der.endSequence();
  320. var sigData = sig.signature.toBuffer('asn1');
  321. var data = new Buffer(sigData.length + 1);
  322. data[0] = 0;
  323. sigData.copy(data, 1);
  324. der.writeBuffer(data, asn1.Ber.BitString);
  325. der.endSequence();
  326. return (der.buffer);
  327. }
  328. function writeTBSCert(cert, der) {
  329. var sig = cert.signatures.x509;
  330. assert.object(sig, 'x509 signature');
  331. der.startSequence();
  332. der.startSequence(Local(0));
  333. der.writeInt(2);
  334. der.endSequence();
  335. der.writeBuffer(utils.mpNormalize(cert.serial), asn1.Ber.Integer);
  336. der.startSequence();
  337. der.writeOID(SIGN_ALGS[sig.algo]);
  338. der.endSequence();
  339. cert.issuer.toAsn1(der);
  340. der.startSequence();
  341. der.writeString(dateToUTCTime(cert.validFrom), asn1.Ber.UTCTime);
  342. der.writeString(dateToUTCTime(cert.validUntil), asn1.Ber.UTCTime);
  343. der.endSequence();
  344. var subject = cert.subjects[0];
  345. var altNames = cert.subjects.slice(1);
  346. subject.toAsn1(der);
  347. pkcs8.writePkcs8(der, cert.subjectKey);
  348. if (sig.extras && sig.extras.issuerUniqueID) {
  349. der.writeBuffer(sig.extras.issuerUniqueID, Local(1));
  350. }
  351. if (sig.extras && sig.extras.subjectUniqueID) {
  352. der.writeBuffer(sig.extras.subjectUniqueID, Local(2));
  353. }
  354. if (altNames.length > 0 || subject.type === 'host' ||
  355. (sig.extras && sig.extras.exts)) {
  356. der.startSequence(Local(3));
  357. der.startSequence();
  358. var exts = [
  359. { oid: EXTS.altName }
  360. ];
  361. if (sig.extras && sig.extras.exts)
  362. exts = sig.extras.exts;
  363. for (var i = 0; i < exts.length; ++i) {
  364. der.startSequence();
  365. der.writeOID(exts[i].oid);
  366. if (exts[i].critical !== undefined)
  367. der.writeBoolean(exts[i].critical);
  368. if (exts[i].oid === EXTS.altName) {
  369. der.startSequence(asn1.Ber.OctetString);
  370. der.startSequence();
  371. if (subject.type === 'host') {
  372. der.writeString(subject.hostname,
  373. Context(2));
  374. }
  375. for (var j = 0; j < altNames.length; ++j) {
  376. if (altNames[j].type === 'host') {
  377. der.writeString(
  378. altNames[j].hostname,
  379. ALTNAME.DNSName);
  380. } else if (altNames[j].type ===
  381. 'email') {
  382. der.writeString(
  383. altNames[j].email,
  384. ALTNAME.RFC822Name);
  385. } else {
  386. /*
  387. * Encode anything else as a
  388. * DN style name for now.
  389. */
  390. der.startSequence(
  391. ALTNAME.DirectoryName);
  392. altNames[j].toAsn1(der);
  393. der.endSequence();
  394. }
  395. }
  396. der.endSequence();
  397. der.endSequence();
  398. } else {
  399. der.writeBuffer(exts[i].data,
  400. asn1.Ber.OctetString);
  401. }
  402. der.endSequence();
  403. }
  404. der.endSequence();
  405. der.endSequence();
  406. }
  407. der.endSequence();
  408. }