peilaus alkaen https://github.com/therootcompany/keypairs.js
AJ ONeal
5 vuotta sitten
4 muutettua tiedostoa jossa 326 lisäystä ja 2 poistoa
@ -1,3 +1,261 @@ |
|||
# root-keypairs.js |
|||
# @root/keypairs |
|||
|
|||
Lightweight, Zero-Dependency RSA and EC/ECDSA crypto for Node.js and Browsers |
|||
Lightweight JavaScript RSA and ECDSA utils that work on Windows, Mac, and Linux |
|||
using modern node.js APIs (no need for C compiler). |
|||
|
|||
A thin wrapper around [Eckles.js (ECDSA)](https://git.coolaj86.com/coolaj86/eckles.js/) |
|||
and [Rasha.js (RSA)](https://git.coolaj86.com/coolaj86/rasha.js/). |
|||
|
|||
# Features |
|||
|
|||
- [x] Generate keypairs |
|||
- [x] RSA |
|||
- [x] ECDSA (P-256, P-384) |
|||
- [x] PEM-to-JWK (and SSH-to-JWK) |
|||
- [x] JWK-to-PEM (and JWK-to-SSH) |
|||
- [x] Create JWTs (and sign JWS) |
|||
- [x] SHA256 JWK Thumbprints |
|||
- [ ] JWK fetching. See [Keyfetch.js](https://npmjs.com/packages/keyfetch/) |
|||
- [ ] OIDC |
|||
- [ ] Auth0 |
|||
- [ ] CLI |
|||
- See [keypairs-cli](https://npmjs.com/packages/keypairs-cli/) |
|||
|
|||
<!-- |
|||
|
|||
* [ ] generate CSR (DER as PEM or base64url) |
|||
|
|||
--> |
|||
|
|||
# Progress |
|||
|
|||
This is fully functional, but the re-usable code from ACME.js hasn't been fully teased out for the v2.0 release. |
|||
|
|||
(SSH conversions have not yet made it to 2.0) |
|||
|
|||
# Usage |
|||
|
|||
A brief introduction to the APIs: |
|||
|
|||
```js |
|||
// generate a new keypair as jwk |
|||
// (defaults to EC P-256 when no options are specified) |
|||
Keypairs.generate().then(function(pair) { |
|||
console.log(pair.private); |
|||
console.log(pair.public); |
|||
}); |
|||
``` |
|||
|
|||
```js |
|||
// JWK to PEM |
|||
// (supports various 'format' and 'encoding' options) |
|||
return Keypairs.export({ jwk: pair.private, format: 'pkcs8' }).then(function( |
|||
pem |
|||
) { |
|||
console.log(pem); |
|||
}); |
|||
``` |
|||
|
|||
```js |
|||
// PEM to JWK |
|||
return Keypairs.import({ pem: pem }).then(function(jwk) { |
|||
console.log(jwk); |
|||
}); |
|||
``` |
|||
|
|||
```js |
|||
// Thumbprint a JWK (SHA256) |
|||
return Keypairs.thumbprint({ jwk: jwk }).then(function(thumb) { |
|||
console.log(thumb); |
|||
}); |
|||
``` |
|||
|
|||
```js |
|||
// Sign a JWT (aka compact JWS) |
|||
return Keypairs.signJwt({ |
|||
jwk: pair.private |
|||
, iss: 'https://example.com' |
|||
, exp: '1h' |
|||
// optional claims |
|||
, claims: { |
|||
, sub: 'jon.doe@gmail.com' |
|||
} |
|||
}); |
|||
``` |
|||
|
|||
By default ECDSA keys will be used since they've had native support in node |
|||
_much_ longer than RSA has, and they're smaller, and faster to generate. |
|||
|
|||
## API Overview |
|||
|
|||
- generate (JWK) |
|||
- parse (PEM) |
|||
- parseOrGenerate (PEM to JWK) |
|||
- import (PEM-to-JWK) |
|||
- export (JWK-to-PEM, private or public) |
|||
- publish (Private JWK to Public JWK) |
|||
- thumbprint (JWK SHA256) |
|||
- signJwt |
|||
- signJws |
|||
|
|||
#### Keypairs.generate(options) |
|||
|
|||
Generates a public/private pair of JWKs as `{ private, public }` |
|||
|
|||
Option examples: |
|||
|
|||
- RSA `{ kty: 'RSA', modulusLength: 2048 }` |
|||
- ECDSA `{ kty: 'ECDSA', namedCurve: 'P-256' }` |
|||
|
|||
When no options are supplied EC P-256 (also known as `prime256v1` and `secp256r1`) is used by default. |
|||
|
|||
#### Keypairs.parse(options) |
|||
|
|||
Parses either a JWK (encoded as JSON) or an x509 (encdode as PEM) and gives |
|||
back the JWK representation. |
|||
|
|||
Option Examples: |
|||
|
|||
- JWK { key: '{ "kty":"EC", ... }' } |
|||
- PEM { key: '-----BEGIN PRIVATE KEY-----\n...' } |
|||
- Public Key Only { key: '-----BEGIN PRIVATE KEY-----\n...', public: true } |
|||
- Must Have Private Key { key: '-----BEGIN PUBLIC KEY-----\n...', private: true } |
|||
|
|||
Example: |
|||
|
|||
```js |
|||
Keypairs.parse({ key: '...' }).catch(function(e) { |
|||
// could not be parsed or was a public key |
|||
console.warn(e); |
|||
return Keypairs.generate(); |
|||
}); |
|||
``` |
|||
|
|||
#### Keypairs.parseOrGenerate({ key, throw, [generate opts]... }) |
|||
|
|||
Parses the key. Logs a warning on failure, marches on. |
|||
(a shortcut for the above, with `private: true`) |
|||
|
|||
Option Examples: |
|||
|
|||
- parse key if exist, otherwise generate `{ key: process.env["PRIVATE_KEY"] }` |
|||
- generated key curve `{ key: null, namedCurve: 'P-256' }` |
|||
- generated key modulus `{ key: null, modulusLength: 2048 }` |
|||
|
|||
Example: |
|||
|
|||
```js |
|||
Keypairs.parseOrGenerate({ key: process.env['PRIVATE_KEY'] }).then(function( |
|||
pair |
|||
) { |
|||
console.log(pair.public); |
|||
}); |
|||
``` |
|||
|
|||
Great for when you have a set of shared keys for development and randomly |
|||
generated keys in |
|||
|
|||
#### Keypairs.import({ pem: '...' } |
|||
|
|||
Takes a PEM in pretty much any format (PKCS1, SEC1, PKCS8, SPKI) and returns a JWK. |
|||
|
|||
#### Keypairs.export(options) |
|||
|
|||
Exports a JWK as a PEM. |
|||
|
|||
Exports PEM in PKCS8 (private) or SPKI (public) by default. |
|||
|
|||
Options |
|||
|
|||
```js |
|||
{ jwk: jwk |
|||
, public: true |
|||
, encoding: 'pem' // or 'der' |
|||
, format: 'pkcs8' // or 'ssh', 'pkcs1', 'sec1', 'spki' |
|||
} |
|||
``` |
|||
|
|||
#### Keypairs.publish({ jwk: jwk, exp: '3d', use: 'sig' }) |
|||
|
|||
Promises a public key that adheres to the OIDC and Auth0 spec (plus expiry), suitable to be published to a JWKs URL: |
|||
|
|||
``` |
|||
{ "kty": "EC" |
|||
, "crv": "P-256" |
|||
, "x": "..." |
|||
, "y": "..." |
|||
, "kid": "..." |
|||
, "use": "sig" |
|||
, "exp": 1552074208 |
|||
} |
|||
``` |
|||
|
|||
In particular this adds "use" and "exp". |
|||
|
|||
#### Keypairs.thumbprint({ jwk: jwk }) |
|||
|
|||
Promises a JWK-spec thumbprint: URL Base64-encoded sha256 |
|||
|
|||
#### Keypairs.signJwt({ jwk, header, claims }) |
|||
|
|||
Returns a JWT (otherwise known as a protected JWS in "compressed" format). |
|||
|
|||
```js |
|||
{ jwk: jwk |
|||
// required claims |
|||
, iss: 'https://example.com' |
|||
, exp: '15m' |
|||
// all optional claims |
|||
, claims: { |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Exp may be human readable duration (i.e. 1h, 15m, 30s) or a datetime in seconds. |
|||
|
|||
Header defaults: |
|||
|
|||
```js |
|||
{ kid: thumbprint |
|||
, alg: 'xS256' |
|||
, typ: 'JWT' |
|||
} |
|||
``` |
|||
|
|||
Payload notes: |
|||
|
|||
- `iat: now` is added by default (set `false` to disable) |
|||
- `exp` must be set (set `false` to disable) |
|||
- `iss` should be the base URL for JWK lookup (i.e. via OIDC, Auth0) |
|||
|
|||
Notes: |
|||
|
|||
`header` is actually the JWS `protected` value, as all JWTs use protected headers (yay!) |
|||
and `claims` are really the JWS `payload`. |
|||
|
|||
#### Keypairs.signJws({ jwk, header, protected, payload }) |
|||
|
|||
This is provided for APIs like ACME (Let's Encrypt) that use uncompressed JWS (instead of JWT, which is compressed). |
|||
|
|||
Options: |
|||
|
|||
- `header` not what you think. Leave undefined unless you need this for the spec you're following. |
|||
- `protected` is the typical JWT-style header |
|||
- `kid` and `alg` will be added by default (these are almost always required), set `false` explicitly to disable |
|||
- `payload` can be JSON, a string, or even a buffer (which gets URL Base64 encoded) |
|||
- you must set this to something, even if it's an empty string, object, or Buffer |
|||
|
|||
# Additional Documentation |
|||
|
|||
Keypairs.js provides a 1-to-1 mapping to the Rasha.js and Eckles.js APIs for the following: |
|||
|
|||
- generate(options) |
|||
- import({ pem: '---BEGIN...' }) |
|||
- export({ jwk: { kty: 'EC', ... }) |
|||
- thumbprint({ jwk: jwk }) |
|||
|
|||
If you want to know the algorithm-specific options that are available for those |
|||
you'll want to take a look at the corresponding documentation: |
|||
|
|||
- See ECDSA documentation at [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js/) |
|||
- See RSA documentation at [Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js/) |
|||
|
@ -0,0 +1,3 @@ |
|||
'use strict'; |
|||
|
|||
module.exports = require('@root/acme/keypairs'); |
@ -0,0 +1,30 @@ |
|||
{ |
|||
"name": "@root/keypairs", |
|||
"version": "1.0.0-wip.0", |
|||
"lockfileVersion": 1, |
|||
"requires": true, |
|||
"dependencies": { |
|||
"@root/acme": { |
|||
"version": "3.0.0-wip.0", |
|||
"resolved": "https://registry.npmjs.org/@root/acme/-/acme-3.0.0-wip.0.tgz", |
|||
"integrity": "sha512-IwnG3ZFt1fl81O1M+FFV91b5Kpw7GYAD1jXwvOWnq9KF50AVO6+L7MUQIAFCK1q/u/weC73DCFrw/6kFN+Vi9A==", |
|||
"requires": { |
|||
"@root/csr": "^1.0.0-wip.0", |
|||
"@root/encoding": "^1.0.1" |
|||
} |
|||
}, |
|||
"@root/csr": { |
|||
"version": "1.0.0-wip.0", |
|||
"resolved": "https://registry.npmjs.org/@root/csr/-/csr-1.0.0-wip.0.tgz", |
|||
"integrity": "sha512-ZrZeGgf/hvfIyMDAZXfD45rYriaZF6LJu7+l0ioPPKgLWVEUAUBkV53z7JbzlcPvXXr6/ZjECzWQ7MYQfMBUAg==", |
|||
"requires": { |
|||
"@root/acme": "^3.0.0-wip.0" |
|||
} |
|||
}, |
|||
"@root/encoding": { |
|||
"version": "1.0.1", |
|||
"resolved": "https://registry.npmjs.org/@root/encoding/-/encoding-1.0.1.tgz", |
|||
"integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ==" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
{ |
|||
"name": "@root/keypairs", |
|||
"version": "1.0.0-wip.0", |
|||
"description": "Lightweight, Zero-Dependency RSA and EC/ECDSA crypto for Node.js and Browsers", |
|||
"main": "keypairs.js", |
|||
"scripts": { |
|||
"test": "node tests" |
|||
}, |
|||
"files": [ |
|||
"*.js", |
|||
"lib", |
|||
"dist" |
|||
], |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://git.rootprojects.org/root/csr.js.git" |
|||
}, |
|||
"keywords": [ |
|||
"ASN.1", |
|||
"DER", |
|||
"PEM", |
|||
"x509", |
|||
"RSA", |
|||
"EC", |
|||
"ECDSA", |
|||
"asn1" |
|||
], |
|||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", |
|||
"license": "MPL-2.0", |
|||
"dependencies": { |
|||
"@root/acme": "^3.0.0-wip.0" |
|||
} |
|||
} |
Ladataan…
Reference in new issue