AJ ONeal
5 years ago
14 changed files with 382 additions and 2 deletions
@ -0,0 +1,8 @@ |
|||
{ |
|||
"bracketSpacing": true, |
|||
"printWidth": 80, |
|||
"singleQuote": true, |
|||
"tabWidth": 4, |
|||
"trailingComma": "none", |
|||
"useTabs": true |
|||
} |
@ -1,3 +1,122 @@ |
|||
# pem.js |
|||
# @root/pem |
|||
|
|||
Lightweight, Zero-Dependency, PEM (RFC 7468) encoder and decoder. |
|||
Lightweight, Zero-Dependency PEM encoder and decoder. |
|||
|
|||
| ~300b gzipped |
|||
| ~650b minified |
|||
| ~1k full |
|||
| |
|||
|
|||
- [x] VanillaJS |
|||
- [x] Zero-Dependency |
|||
- [x] Universal Support |
|||
- [x] Node.js |
|||
- [x] Browsers |
|||
|
|||
# Support |
|||
|
|||
This library supports PEM, which is pretty boring on its own. |
|||
|
|||
Most likely you are also interested in some of the following: |
|||
|
|||
- [keypairs.js](https://git.rootprojects.org/root/keypairs.js) |
|||
- RSA |
|||
- EC / ECDSA |
|||
- [x509.js](https://git.rootprojects.org/root/x509.js) |
|||
- [asn1.js](https://git.rootprojects.org/root/asn1.js) |
|||
|
|||
# Usage |
|||
|
|||
- PEM.parseBlock(str) |
|||
- PEM.packBlock(options) |
|||
|
|||
Parsing |
|||
|
|||
```js |
|||
var PEM = require('@root/pem/parser'); |
|||
|
|||
var block = PEM.parseBlock( |
|||
'-----BEGIN Type-----\nSGVsbG8sIOS4lueVjCE=\n-----END Type-----\n' |
|||
); |
|||
``` |
|||
|
|||
```js |
|||
{ |
|||
bytes: `<48 65 6c 6c 6f 2c 20 e4 b8 96 e7 95 8c 21>`; |
|||
} |
|||
``` |
|||
|
|||
Packing |
|||
|
|||
```js |
|||
var PEM = require('@root/pem/packer'); |
|||
|
|||
var block = PEM.packBlock({ |
|||
type: 'Type', |
|||
// Buffer or Uint8Array or Array |
|||
bytes: [0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c, 0x21] |
|||
); |
|||
``` |
|||
|
|||
```txt |
|||
-----BEGIN Type----- |
|||
SGVsbG8sIOS4lueVjCE= |
|||
-----END Type----- |
|||
``` |
|||
|
|||
# Install |
|||
|
|||
## Node / Webpack |
|||
|
|||
```js |
|||
npm install -g @root/pem |
|||
``` |
|||
|
|||
## Browsers |
|||
|
|||
```html |
|||
<script src="https://unpkg.com/@root/pem/dist/pem.all.js"></script> |
|||
``` |
|||
|
|||
```html |
|||
<script src="https://unpkg.com/@root/pem/dist/pem.all.min.js"></script> |
|||
``` |
|||
|
|||
# A PEM Block |
|||
|
|||
A Block represents a PEM encoded structure. |
|||
|
|||
The encoded form is: |
|||
|
|||
```txt |
|||
-----BEGIN Type----- |
|||
Headers |
|||
base64-encoded Bytes |
|||
-----END Type----- |
|||
``` |
|||
|
|||
where Headers is a possibly empty sequence of Key: Value lines. |
|||
|
|||
(credit: https://golang.org/pkg/encoding/pem/) |
|||
|
|||
# PEM History |
|||
|
|||
PEM was introduced in 1993 via RFC 1421, but not formally |
|||
standardized until RFC 7468 in April of 2015. |
|||
|
|||
It has served as the _de facto_ standard for a variety of |
|||
DER-encoded X509 schemas of ASN.1 data for cryptographic |
|||
keys and certificates such as: |
|||
|
|||
- [x] PKCS#10 (Certificate Signing Request [CSR]) |
|||
- [x] X509 Certificate (fullchain.pem, site.crt) |
|||
- [x] PKIX (cert.pem, privkey.pem, priv.key) |
|||
- [x] PKCS#1 (RSA Public and Private Keys) |
|||
- [x] PKCS#8 (RSA and ECDSA Keypairs) |
|||
- [x] SEC#1 (ECDSARSA Public and Private Keys) |
|||
|
|||
# Legal |
|||
|
|||
MPL-2.0 | |
|||
[Terms of Use](https://therootcompany.com/legal/#terms) | |
|||
[Privacy Policy](https://therootcompany.com/legal/#privacy) |
|||
|
@ -0,0 +1,23 @@ |
|||
'use strict'; |
|||
|
|||
// "A little copying is better than a little dependency" - Rob Pike
|
|||
var Enc = module.exports; |
|||
|
|||
Enc.bufToBase64 = function(u8) { |
|||
var bin = ''; |
|||
// map is not part of u8
|
|||
u8.forEach(function(i) { |
|||
bin += String.fromCharCode(i); |
|||
}); |
|||
return btoa(bin); |
|||
}; |
|||
|
|||
Enc.base64ToBuf = function(b64) { |
|||
return Uint8Array.from( |
|||
atob(b64) |
|||
.split('') |
|||
.map(function(ch) { |
|||
return ch.charCodeAt(0); |
|||
}) |
|||
); |
|||
}; |
@ -0,0 +1,33 @@ |
|||
'use strict'; |
|||
|
|||
var Enc = require('@root/encoding/base64'); |
|||
var PEM = module.exports; |
|||
|
|||
PEM.packBlock = function(opts) { |
|||
// TODO allow for headers?
|
|||
return ( |
|||
'-----BEGIN ' + |
|||
opts.type + |
|||
'-----\n' + |
|||
Enc.bufToBase64(opts.bytes) |
|||
.match(/.{1,64}/g) |
|||
.join('\n') + |
|||
'\n' + |
|||
'-----END ' + |
|||
opts.type + |
|||
'-----' |
|||
); |
|||
}; |
|||
|
|||
// don't replace the full parseBlock, if it exists
|
|||
PEM.parseBlock = |
|||
PEM.parseBlock || |
|||
function(str) { |
|||
var der = str |
|||
.split(/\n/) |
|||
.filter(function(line) { |
|||
return !/-----/.test(line); |
|||
}) |
|||
.join(''); |
|||
return { bytes: Enc.base64ToBuf(der) }; |
|||
}; |
@ -0,0 +1,17 @@ |
|||
#!/bin/bash |
|||
|
|||
# TODO convert to JS |
|||
cat browser/native.js parser.js packer.js > all.tmp.js |
|||
sed -i '' '/use strict/d' all.tmp.js |
|||
sed -i '' '/require/d' all.tmp.js |
|||
sed -i '' '/exports/d' all.tmp.js |
|||
echo ';(function () {' > all.js |
|||
echo "'use strict';" >> all.js |
|||
echo "var PEM = window.PEM = {};" >> all.js |
|||
echo "var Enc = {};" >> all.js |
|||
cat all.tmp.js >> all.js |
|||
rm all.tmp.js |
|||
echo '}());' >> all.js |
|||
|
|||
mv all.js dist/pem.all.js |
|||
uglifyjs dist/pem.all.js > dist/pem.all.min.js |
@ -0,0 +1,56 @@ |
|||
;(function () { |
|||
'use strict'; |
|||
var PEM = window.PEM = {}; |
|||
var Enc = {}; |
|||
|
|||
// "A little copying is better than a little dependency" - Rob Pike
|
|||
|
|||
Enc.bufToBase64 = function(u8) { |
|||
var bin = ''; |
|||
// map is not part of u8
|
|||
u8.forEach(function(i) { |
|||
bin += String.fromCharCode(i); |
|||
}); |
|||
return btoa(bin); |
|||
}; |
|||
|
|||
Enc.base64ToBuf = function(b64) { |
|||
return Uint8Array.from( |
|||
atob(b64) |
|||
.split('') |
|||
.map(function(ch) { |
|||
return ch.charCodeAt(0); |
|||
}) |
|||
); |
|||
}; |
|||
|
|||
|
|||
|
|||
PEM.parseBlock = function(str) { |
|||
var der = str |
|||
.split(/\n/) |
|||
.filter(function(line) { |
|||
return !/-----/.test(line); |
|||
}) |
|||
.join(''); |
|||
return { bytes: Enc.base64ToBuf(der) }; |
|||
}; |
|||
|
|||
|
|||
|
|||
PEM.packBlock = function(opts) { |
|||
// TODO allow for headers?
|
|||
return ( |
|||
'-----BEGIN ' + |
|||
opts.type + |
|||
'-----\n' + |
|||
Enc.bufToBase64(opts.bytes) |
|||
.match(/.{1,64}/g) |
|||
.join('\n') + |
|||
'\n' + |
|||
'-----END ' + |
|||
opts.type + |
|||
'-----' |
|||
); |
|||
}; |
|||
}()); |
@ -0,0 +1 @@ |
|||
(function(){"use strict";var PEM=window.PEM={};var Enc={};Enc.bufToBase64=function(u8){var bin="";u8.forEach(function(i){bin+=String.fromCharCode(i)});return btoa(bin)};Enc.base64ToBuf=function(b64){return Uint8Array.from(atob(b64).split("").map(function(ch){return ch.charCodeAt(0)}))};PEM.parseBlock=function(str){var der=str.split(/\n/).filter(function(line){return!/-----/.test(line)}).join("");return{bytes:Enc.base64ToBuf(der)}};PEM.packBlock=function(opts){return"-----BEGIN "+opts.type+"-----\n"+Enc.bufToBase64(opts.bytes).match(/.{1,64}/g).join("\n")+"\n"+"-----END "+opts.type+"-----"}})(); |
@ -0,0 +1,7 @@ |
|||
'use strict'; |
|||
|
|||
var PEM = require('./parser.js'); |
|||
var PEMPacker = require('./packer.js'); |
|||
PEM.packBlock = PEMPacker.packBlock; |
|||
|
|||
module.exports = PEM; |
@ -0,0 +1,12 @@ |
|||
'use strict'; |
|||
|
|||
// "A little copying is better than a little dependency" - Rob Pike
|
|||
var Enc = module.exports; |
|||
|
|||
Enc.bufToBase64 = function(buf) { |
|||
return Buffer.from(buf).toString('base64'); |
|||
}; |
|||
|
|||
Enc.base64ToBuf = function(b64) { |
|||
return Buffer.from(b64, 'base64'); |
|||
}; |
@ -0,0 +1,29 @@ |
|||
{ |
|||
"name": "@root/pem", |
|||
"version": "1.0.0", |
|||
"description": "VanillaJS, Lightweight, Zero-Dependency, PEM encoder and decoder.", |
|||
"main": "index.js", |
|||
"browser": { |
|||
"./node/native.js": "./browser/native.js" |
|||
}, |
|||
"scripts": { |
|||
"test": "node tests" |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://git.rootprojects.org/root/pem.js.git" |
|||
}, |
|||
"keywords": [ |
|||
"PEM", |
|||
"x509", |
|||
"ASN.1", |
|||
"asn1", |
|||
"RFC7468", |
|||
"RFC1421", |
|||
"RFC", |
|||
"7468", |
|||
"1421" |
|||
], |
|||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", |
|||
"license": "MPL-2.0" |
|||
} |
@ -0,0 +1,22 @@ |
|||
'use strict'; |
|||
|
|||
var PEM = module.exports; |
|||
|
|||
//var Enc = require('@root/encoding/base64');
|
|||
var Enc = require('./node/native.js'); |
|||
|
|||
PEM.packBlock = function(opts) { |
|||
// TODO allow for headers?
|
|||
return ( |
|||
'-----BEGIN ' + |
|||
opts.type + |
|||
'-----\n' + |
|||
Enc.bufToBase64(opts.bytes) |
|||
.match(/.{1,64}/g) |
|||
.join('\n') + |
|||
'\n' + |
|||
'-----END ' + |
|||
opts.type + |
|||
'-----' |
|||
); |
|||
}; |
@ -0,0 +1,15 @@ |
|||
'use strict'; |
|||
|
|||
var PEM = module.exports; |
|||
|
|||
var Enc = require('./node/native.js'); |
|||
|
|||
PEM.parseBlock = function(str) { |
|||
var der = str |
|||
.split(/\n/) |
|||
.filter(function(line) { |
|||
return !/-----/.test(line); |
|||
}) |
|||
.join(''); |
|||
return { bytes: Enc.base64ToBuf(der) }; |
|||
}; |
@ -0,0 +1,36 @@ |
|||
'use strict'; |
|||
|
|||
var PEM = require('../'); |
|||
//console.log(PEM);
|
|||
|
|||
// "Hello, 世界!";
|
|||
var contents = 'SGVs\nbG8sIOS4\nlueVjCE='; |
|||
var pem = '-----BEGIN Type-----\n' + contents + '\n-----END Type-----\n'; |
|||
var block = PEM.parseBlock(pem); |
|||
|
|||
if (14 !== block.bytes.byteLength) { |
|||
throw new Error('should be 14 bytes'); |
|||
} |
|||
|
|||
if (0x48 !== block.bytes[0]) { |
|||
throw new Error('first byte should be 0x48 ("H")'); |
|||
} |
|||
|
|||
if (0x8c !== block.bytes[12]) { |
|||
throw new Error('13th byte should be 0x8c (3rd byte of "界")'); |
|||
} |
|||
|
|||
console.info("PASS: decodes 'bytes' field correctly"); |
|||
|
|||
var pem2 = |
|||
'-----BEGIN Type-----\n' + |
|||
contents.replace(/\n/g, '') + |
|||
'\n-----END Type-----'; |
|||
|
|||
block.type = 'Type'; |
|||
if (pem2 !== PEM.packBlock(block)) { |
|||
console.debug(PEM.packBlock(block)); |
|||
throw new Error('should pack PEM correctly'); |
|||
} |
|||
|
|||
console.info("PASS: encodes 'bytes' and 'type' fields correctly"); |
Loading…
Reference in new issue