pem.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. // **Github:** https://github.com/fidm/asn1
  4. //
  5. // **License:** MIT
  6. const util_1 = require("util");
  7. const pemLineLength = 64;
  8. const pemStart = '-----BEGIN ';
  9. const pemEnd = '-----END ';
  10. const pemEndOfLine = '-----';
  11. const procType = 'Proc-Type';
  12. /**
  13. * Implements the PEM data encoding, which originated in Privacy
  14. * Enhanced Mail. The most common use of PEM encoding today is in TLS keys and
  15. * certificates. See RFC 1421.
  16. *
  17. * A PEM represents a PEM encoded structure.
  18. *
  19. * The encoded form is:
  20. * ```
  21. * -----BEGIN Type-----
  22. * Headers
  23. * base64-encoded Bytes
  24. * -----END Type-----
  25. * ```
  26. *
  27. * Headers like:
  28. * ```
  29. * Proc-Type: 4,ENCRYPTED
  30. * DEK-Info: DES-EDE3-CBC,29DE8F99F382D122
  31. * ```
  32. */
  33. class PEM {
  34. /**
  35. * Parse PEM formatted buffer, returns one or more PEM object.
  36. * If there is no PEM object, it will throw error.
  37. * @param data buffer to parse.
  38. */
  39. static parse(data) {
  40. const res = [];
  41. const lines = data.toString('utf8').split('\n')
  42. .map((s) => s.trim())
  43. .filter((s) => s !== '' && !s.startsWith('#'));
  44. while (lines.length > 0) {
  45. res.push(parse(lines));
  46. }
  47. if (res.length === 0) {
  48. throw new Error('PEM: no block');
  49. }
  50. return res;
  51. }
  52. constructor(type, body) {
  53. this.type = type;
  54. this.body = body;
  55. this.headers = Object.create(null);
  56. }
  57. /**
  58. * Return exists Proc-Type header or empty string
  59. */
  60. get procType() {
  61. return this.getHeader(procType);
  62. }
  63. /**
  64. * Return a header or empty string with given key.
  65. */
  66. getHeader(key) {
  67. const val = this.headers[key];
  68. return val == null ? '' : val;
  69. }
  70. /**
  71. * Set a header with given key/value.
  72. */
  73. setHeader(key, val) {
  74. if (key.includes(':')) {
  75. throw new Error('pem: cannot encode a header key that contains a colon');
  76. }
  77. if (key === '' || val === '') {
  78. throw new Error('pem: invalid header key or value');
  79. }
  80. this.headers[key] = val;
  81. }
  82. /**
  83. * Encode to PEM formatted string.
  84. */
  85. toString() {
  86. let rVal = pemStart + this.type + pemEndOfLine + '\n';
  87. const headers = Object.keys(this.headers);
  88. if (headers.length > 0) {
  89. // The Proc-Type header must be written first. See RFC 1421, section 4.6.1.1
  90. const type = this.procType;
  91. if (type !== '') {
  92. rVal += `${procType}: ${type}\n`;
  93. }
  94. // For consistency of output, write other headers sorted by key.
  95. headers.sort();
  96. for (const key of headers) {
  97. if (key !== procType) {
  98. rVal += `${key}: ${this.headers[key]}\n`;
  99. }
  100. }
  101. rVal += '\n';
  102. }
  103. const body = this.body.toString('base64');
  104. let offset = 0;
  105. while (offset < body.length) {
  106. rVal += body.slice(offset, offset + pemLineLength) + '\n';
  107. offset += pemLineLength;
  108. }
  109. rVal += pemEnd + this.type + pemEndOfLine + '\n';
  110. return rVal;
  111. }
  112. /**
  113. * Encode to PEM formatted buffer.
  114. */
  115. toBuffer() {
  116. return Buffer.from(this.toString(), 'utf8');
  117. }
  118. /**
  119. * Returns the body.
  120. */
  121. valueOf() {
  122. return this.body;
  123. }
  124. /**
  125. * Return a friendly JSON object for debuging.
  126. */
  127. toJSON() {
  128. return {
  129. type: this.type,
  130. body: this.body,
  131. headers: this.headers,
  132. };
  133. }
  134. [util_1.inspect.custom](_depth, options) {
  135. return `<${this.constructor.name} ${util_1.inspect(this.toJSON(), options)}>`;
  136. }
  137. }
  138. exports.PEM = PEM;
  139. function parse(lines) {
  140. let line = lines.shift();
  141. if (line == null || !line.startsWith(pemStart) || !line.endsWith(pemEndOfLine)) {
  142. throw new Error('pem: invalid BEGIN line');
  143. }
  144. const type = line.slice(pemStart.length, line.length - pemEndOfLine.length);
  145. if (type === '') {
  146. throw new Error('pem: invalid type');
  147. }
  148. const headers = [];
  149. line = lines.shift();
  150. while (line != null && line.includes(': ')) {
  151. const header = line.split(': ');
  152. if (header.length !== 2 || header[0] === '' || header[1] === '') {
  153. throw new Error('pem: invalid Header line');
  154. }
  155. headers.push(header);
  156. line = lines.shift();
  157. }
  158. let body = '';
  159. while (line != null && !line.startsWith(pemEnd)) {
  160. body += line;
  161. line = lines.shift();
  162. }
  163. if (line == null || line !== `${pemEnd}${type}${pemEndOfLine}`) {
  164. throw new Error('pem: invalid END line');
  165. }
  166. const pem = new PEM(type, Buffer.from(body, 'base64'));
  167. if (body === '' || pem.body.toString('base64') !== body) {
  168. throw new Error('pem: invalid base64 body');
  169. }
  170. for (const header of headers) {
  171. pem.setHeader(header[0], header[1]);
  172. }
  173. return pem;
  174. }
  175. //# sourceMappingURL=pem.js.map