Compare commits

...

13 Commits
v0.9.0 ... main

23 changed files with 243 additions and 168 deletions

6
.jshintrc Normal file
View File

@ -0,0 +1,6 @@
{
"esversion": 11,
"node": true,
"unused": true,
"curly": true
}

8
.prettierrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"bracketSpacing": true,
"printWidth": 80,
"singleQuote": true,
"tabWidth": 4,
"trailingComma": "none",
"useTabs": true
}

View File

@ -1,4 +1,4 @@
# @root/keypairs # [@root/keypairs](https://git.rootprojects.org/root/keypairs.js)
Lightweight JavaScript RSA and ECDSA utils that work on Windows, Mac, and Linux Lightweight JavaScript RSA and ECDSA utils that work on Windows, Mac, and Linux
using modern node.js APIs (no need for C compiler). using modern node.js APIs (no need for C compiler).
@ -20,6 +20,8 @@ and [Rasha.js (RSA)](https://git.coolaj86.com/coolaj86/rasha.js/).
- [ ] Auth0 - [ ] Auth0
- [ ] CLI - [ ] CLI
- See [keypairs-cli](https://npmjs.com/packages/keypairs-cli/) - See [keypairs-cli](https://npmjs.com/packages/keypairs-cli/)
- [x] Node
- [x] Browsers (Webpack >=5)
<!-- <!--
@ -86,6 +88,72 @@ return Keypairs.signJwt({
By default ECDSA keys will be used since they've had native support in node 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. _much_ longer than RSA has, and they're smaller, and faster to generate.
## Webpack 5+ (for Browsers)
This package includes native browser versions of all special functions.
Since Webpack 5 now fully supports vanilla JavaScript and exclusive browser builds out-of-the-box,
it's pretty easy to create a minimal config to use Keypairs in your browser projects:
`webpack.config.js`:
```js
'use strict';
var path = require('path');
module.exports = {
entry: './main.js',
mode: 'development',
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 3001
},
output: {
publicPath: 'http://localhost:3001/'
},
module: {
rules: [{}]
},
plugins: []
};
```
`main.js`:
```js
'use strict';
var Keypairs = require('./keypairs.js');
Keypairs.generate().then(function (pair) {
console.log(pair.private);
console.log(pair.public);
});
```
`index.html`:
```html
<!DOCTYPE html>
<html>
<body>
<script src="./dist/main.js"></script>
</body>
</html>
```
```bash
npm install --save-dev webpack@5
# Or, if webpack 5 is still in beta: npm install --save-dev webpack@next
npm install --save-dev webpack-cli
npx webpack --mode=production
ls dist/main.js
```
## API Overview ## API Overview
- generate (JWK) - generate (JWK)

View File

@ -107,7 +107,7 @@ Keypairs.generate = function(opts) {
} else if (/^RSA$/i.test(opts.kty)) { } else if (/^RSA$/i.test(opts.kty)) {
p = Rasha.generate(opts); p = Rasha.generate(opts);
} else { } else {
return Promise.Reject( return Promise.reject(
new Error( new Error(
"'" + "'" +
opts.kty + opts.kty +
@ -218,7 +218,7 @@ Keypairs.signJwt = function(opts) {
var claims = JSON.parse(JSON.stringify(opts.claims || {})); var claims = JSON.parse(JSON.stringify(opts.claims || {}));
header.typ = 'JWT'; header.typ = 'JWT';
if (!header.kid && false !== header.kid) { if (!header.kid && !header.jwk && false !== header.kid) {
header.kid = thumb; header.kid = thumb;
} }
if (!header.alg && opts.alg) { if (!header.alg && opts.alg) {
@ -294,19 +294,19 @@ Keypairs.signJws = function(opts) {
if (!protect.alg) { if (!protect.alg) {
protect.alg = alg(); protect.alg = alg();
} }
// There's a particular request where ACME / Let's Encrypt explicitly doesn't use a kid // There's a particular request where ACME / Let's Encrypt explicitly doesn't use a kid
// There should be a kid unless it's `false` or there's a `jwk` (a self-signed JWS)
if (!protect.kid) {
if (false === protect.kid) { if (false === protect.kid) {
protect.kid = undefined; protect.kid = undefined;
} else if (!protect.kid) { } else if (!protect.jwk) {
protect.kid = thumb; protect.kid = thumb;
} }
}
protectedHeader = JSON.stringify(protect); protectedHeader = JSON.stringify(protect);
} }
// Not sure how to handle the empty case since ACME POST-as-GET must be empty
//if (!payload) {
// throw new Error("opts.payload should be JSON, string, or ArrayBuffer (it may be empty, but that must be explicit)");
//}
// Trying to detect if it's a plain object (not Buffer, ArrayBuffer, Array, Uint8Array, etc) // Trying to detect if it's a plain object (not Buffer, ArrayBuffer, Array, Uint8Array, etc)
if ( if (
payload && payload &&

View File

@ -9,5 +9,7 @@ sha2.sum = function(alg, str) {
data = encoder.encode(str); data = encoder.encode(str);
} }
var sha = 'SHA-' + String(alg).replace(/^sha-?/i, ''); var sha = 'SHA-' + String(alg).replace(/^sha-?/i, '');
return window.crypto.subtle.digest(sha, data); return window.crypto.subtle.digest(sha, data).then(function (buf) {
return new Uint8Array(buf);
});
}; };

View File

@ -13,10 +13,7 @@ module.exports = function(bitlen, exp) {
} }
var keypair = ursa.generatePrivateKey(bitlen, exp); var keypair = ursa.generatePrivateKey(bitlen, exp);
var result = { var result = {
privateKeyPem: keypair privateKeyPem: keypair.toPrivatePem().toString('ascii').trim()
.toPrivatePem()
.toString('ascii')
.trim()
}; };
return result; return result;
}; };

View File

@ -10,10 +10,7 @@ Keypairs._sign = function(opts, payload) {
// node specifies RSA-SHAxxx even when it's actually ecdsa (it's all encoded x509 shasums anyway) // node specifies RSA-SHAxxx even when it's actually ecdsa (it's all encoded x509 shasums anyway)
// TODO opts.alg = (protect||header).alg // TODO opts.alg = (protect||header).alg
var nodeAlg = 'SHA' + Keypairs._getBits(opts); var nodeAlg = 'SHA' + Keypairs._getBits(opts);
var binsig = crypto var binsig = crypto.createSign(nodeAlg).update(payload).sign(pem);
.createSign(nodeAlg)
.update(payload)
.sign(pem);
if ('EC' === opts.jwk.kty && !/x509|asn1/i.test(opts.format)) { if ('EC' === opts.jwk.kty && !/x509|asn1/i.test(opts.format)) {
// ECDSA JWT signatures differ from "normal" ECDSA signatures // ECDSA JWT signatures differ from "normal" ECDSA signatures

View File

@ -9,9 +9,6 @@ sha2.sum = function(alg, str) {
var sha = 'sha' + String(alg).replace(/^sha-?/i, ''); var sha = 'sha' + String(alg).replace(/^sha-?/i, '');
// utf8 is the default for strings // utf8 is the default for strings
var buf = Buffer.from(str); var buf = Buffer.from(str);
return crypto return crypto.createHash(sha).update(buf).digest();
.createHash(sha)
.update(buf)
.digest();
}); });
}; };

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "@root/keypairs", "name": "@root/keypairs",
"version": "0.9.0", "version": "0.10.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@root/keypairs", "name": "@root/keypairs",
"version": "0.9.0", "version": "0.10.2",
"description": "Lightweight, Zero-Dependency RSA and EC/ECDSA crypto for Node.js and Browsers", "description": "Lightweight, Zero-Dependency RSA and EC/ECDSA crypto for Node.js and Browsers",
"main": "keypairs.js", "main": "keypairs.js",
"browser": { "browser": {
@ -20,7 +20,7 @@
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.rootprojects.org/root/csr.js.git" "url": "https://github.com/therootcompany/keypairs.js.git"
}, },
"keywords": [ "keywords": [
"ASN.1", "ASN.1",

View File

@ -89,14 +89,14 @@ Keypairs.parseOrGenerate({ key: null })
return true; return true;
} }
), ),
Keypairs.parse({ key: JSON.stringify(pair.private) }).then(function( Keypairs.parse({ key: JSON.stringify(pair.private) }).then(
pair function (pair) {
) {
if (!pair.private || !pair.public) { if (!pair.private || !pair.public) {
throw new Error('missing key pairs (stringified jwt)'); throw new Error('missing key pairs (stringified jwt)');
} }
return true; return true;
}), }
),
Keypairs.parse({ Keypairs.parse({
key: JSON.stringify(pair.private), key: JSON.stringify(pair.private),
public: true public: true