@fidm/asn1
Version:
ASN.1/DER, PEM for Node.js
175 lines • 5.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
// **Github:** https://github.com/fidm/asn1
//
// **License:** MIT
const util_1 = require("util");
const pemLineLength = 64;
const pemStart = '-----BEGIN ';
const pemEnd = '-----END ';
const pemEndOfLine = '-----';
const procType = 'Proc-Type';
/**
* Implements the PEM data encoding, which originated in Privacy
* Enhanced Mail. The most common use of PEM encoding today is in TLS keys and
* certificates. See RFC 1421.
*
* A PEM represents a PEM encoded structure.
*
* The encoded form is:
* ```
* -----BEGIN Type-----
* Headers
* base64-encoded Bytes
* -----END Type-----
* ```
*
* Headers like:
* ```
* Proc-Type: 4,ENCRYPTED
* DEK-Info: DES-EDE3-CBC,29DE8F99F382D122
* ```
*/
class PEM {
/**
* Parse PEM formatted buffer, returns one or more PEM object.
* If there is no PEM object, it will throw error.
* @param data buffer to parse.
*/
static parse(data) {
const res = [];
const lines = data.toString('utf8').split('\n')
.map((s) => s.trim())
.filter((s) => s !== '' && !s.startsWith('#'));
while (lines.length > 0) {
res.push(parse(lines));
}
if (res.length === 0) {
throw new Error('PEM: no block');
}
return res;
}
constructor(type, body) {
this.type = type;
this.body = body;
this.headers = Object.create(null);
}
/**
* Return exists Proc-Type header or empty string
*/
get procType() {
return this.getHeader(procType);
}
/**
* Return a header or empty string with given key.
*/
getHeader(key) {
const val = this.headers[key];
return val == null ? '' : val;
}
/**
* Set a header with given key/value.
*/
setHeader(key, val) {
if (key.includes(':')) {
throw new Error('pem: cannot encode a header key that contains a colon');
}
if (key === '' || val === '') {
throw new Error('pem: invalid header key or value');
}
this.headers[key] = val;
}
/**
* Encode to PEM formatted string.
*/
toString() {
let rVal = pemStart + this.type + pemEndOfLine + '\n';
const headers = Object.keys(this.headers);
if (headers.length > 0) {
// The Proc-Type header must be written first. See RFC 1421, section 4.6.1.1
const type = this.procType;
if (type !== '') {
rVal += `${procType}: ${type}\n`;
}
// For consistency of output, write other headers sorted by key.
headers.sort();
for (const key of headers) {
if (key !== procType) {
rVal += `${key}: ${this.headers[key]}\n`;
}
}
rVal += '\n';
}
const body = this.body.toString('base64');
let offset = 0;
while (offset < body.length) {
rVal += body.slice(offset, offset + pemLineLength) + '\n';
offset += pemLineLength;
}
rVal += pemEnd + this.type + pemEndOfLine + '\n';
return rVal;
}
/**
* Encode to PEM formatted buffer.
*/
toBuffer() {
return Buffer.from(this.toString(), 'utf8');
}
/**
* Returns the body.
*/
valueOf() {
return this.body;
}
/**
* Return a friendly JSON object for debuging.
*/
toJSON() {
return {
type: this.type,
body: this.body,
headers: this.headers,
};
}
[util_1.inspect.custom](_depth, options) {
return `<${this.constructor.name} ${util_1.inspect(this.toJSON(), options)}>`;
}
}
exports.PEM = PEM;
function parse(lines) {
let line = lines.shift();
if (line == null || !line.startsWith(pemStart) || !line.endsWith(pemEndOfLine)) {
throw new Error('pem: invalid BEGIN line');
}
const type = line.slice(pemStart.length, line.length - pemEndOfLine.length);
if (type === '') {
throw new Error('pem: invalid type');
}
const headers = [];
line = lines.shift();
while (line != null && line.includes(': ')) {
const header = line.split(': ');
if (header.length !== 2 || header[0] === '' || header[1] === '') {
throw new Error('pem: invalid Header line');
}
headers.push(header);
line = lines.shift();
}
let body = '';
while (line != null && !line.startsWith(pemEnd)) {
body += line;
line = lines.shift();
}
if (line == null || line !== `${pemEnd}${type}${pemEndOfLine}`) {
throw new Error('pem: invalid END line');
}
const pem = new PEM(type, Buffer.from(body, 'base64'));
if (body === '' || pem.body.toString('base64') !== body) {
throw new Error('pem: invalid base64 body');
}
for (const header of headers) {
pem.setHeader(header[0], header[1]);
}
return pem;
}
//# sourceMappingURL=pem.js.map