Compare commits

...

10 Commits

35 changed files with 107 additions and 1704 deletions

View File

@ -1,4 +1,5 @@
# rsa-compat.js # [rsa-compat.js](https://git.coolaj86.com/coolaj86/rsa-compat.js)
!["Lifetime Downloads"](https://img.shields.io/npm/dt/rsa-compat.svg "Lifetime Download Count can't be shown") !["Lifetime Downloads"](https://img.shields.io/npm/dt/rsa-compat.svg "Lifetime Download Count can't be shown")
!["Monthly Downloads"](https://img.shields.io/npm/dm/rsa-compat.svg "Monthly Download Count can't be shown") !["Monthly Downloads"](https://img.shields.io/npm/dm/rsa-compat.svg "Monthly Download Count can't be shown")
!["Weekly Downloads"](https://img.shields.io/npm/dw/rsa-compat.svg "Weekly Download Count can't be shown") !["Weekly Downloads"](https://img.shields.io/npm/dw/rsa-compat.svg "Weekly Download Count can't be shown")
@ -7,9 +8,6 @@
JavaScript RSA utils that work on Windows, Mac, and Linux with or without C compiler JavaScript RSA utils that work on Windows, Mac, and Linux with or without C compiler
This now uses node-native RSA key generation and lightweight, zero-dependency solutions for key conversion.
However, it also optionally depends on `ursa` and `forge` for backwards compatibility with older node versions.
This was built for the [ACME.js](https://git.coolaj86.com/coolaj86/acme.js) and This was built for the [ACME.js](https://git.coolaj86.com/coolaj86/acme.js) and
[Greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js) **Let's Encrypt** clients [Greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js) **Let's Encrypt** clients
and is particularly suitable for building **certbot**-like clients. and is particularly suitable for building **certbot**-like clients.
@ -24,6 +22,8 @@ node.js
npm install --save rsa-compat npm install --save rsa-compat
``` ```
If you need compatibility with older versions of node, you may need to `npm install --save ursa-optional node-forge`.
### CLI ### CLI
```bash ```bash
@ -80,16 +80,9 @@ Here's what the object might look like:
, n: '/*base64 modulus n = pq*/' , n: '/*base64 modulus n = pq*/'
, e: '/*base64 exponent (usually 65537)*/' , e: '/*base64 exponent (usually 65537)*/'
} }
, _ursa: '/*undefined or intermediate ursa object*/'
, _ursaPublic: '/*undefined or intermediate ursa object*/'
, _forge: '/*undefined or intermediate forge object*/'
, _forgePublic: '/*undefined or intermediate forge object*/'
} }
``` ```
NOTE: this object is JSON safe as _ursa and _forge will be ignored
See http://crypto.stackexchange.com/questions/6593/what-data-is-saved-in-rsa-private-key to learn a little more about the meaning of the specific fields in the JWK. See http://crypto.stackexchange.com/questions/6593/what-data-is-saved-in-rsa-private-key to learn a little more about the meaning of the specific fields in the JWK.
# API Summary # API Summary
@ -106,6 +99,7 @@ See http://crypto.stackexchange.com/questions/6593/what-data-is-saved-in-rsa-pri
* (deprecated `RSA.signJws(keypair, payload, nonce)`) * (deprecated `RSA.signJws(keypair, payload, nonce)`)
* `RSA.generateCsrPem(keypair, names)` * `RSA.generateCsrPem(keypair, names)`
* `RSA.generateCsrDerWeb64(keypair, names)` * `RSA.generateCsrDerWeb64(keypair, names)`
* `RSA.thumbprint(keypair)`
`keypair` can be any object with any of these keys `publicKeyPem, privateKeyPem, publicKeyJwk, privateKeyJwk` `keypair` can be any object with any of these keys `publicKeyPem, privateKeyPem, publicKeyJwk, privateKeyJwk`
@ -195,6 +189,21 @@ The result looks like this:
} }
``` ```
### RSA.thumbprint(keypair)
Generates a JWK thumbprint.
`RSA.thumbprint(keypair)`:
```javascript
var thumb = RSA.thumbprint(keypair);
console.log(thumb);
```
```
// kK4OXp5CT1FEkHi6WkegldmeTJecSTyJN-DxZ91nQ30
```
### RSA.generateCsr*(keypair, names) ### RSA.generateCsr*(keypair, names)
You can generate the CSR in human-readable or binary / base64 formats: You can generate the CSR in human-readable or binary / base64 formats:
@ -257,9 +266,14 @@ but it does matter.
# ChangeLog: # ChangeLog:
* v1.9.0 * v2.0
* remove ursa and node-forge deps
* mark for node v10.11+
* v1.9
* consistently handle key generation across node crypto, ursa, and forge * consistently handle key generation across node crypto, ursa, and forge
* move all other operations to rasha.js and rsa-csr.js * move all other operations to rasha.js and rsa-csr.js
* bugfix non-standard JWKs output (which *mostly* worked)
* move dependencies to optional
* v1.4.0 * v1.4.0
* remove ursa as dependency (just causes confusion), but note in docs * remove ursa as dependency (just causes confusion), but note in docs
* drop node < v6 support * drop node < v6 support

View File

@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'; 'use strict';
var Rasha = require('./rasha'); var Keypairs = require('keypairs');
module.exports = function (bitlen, exp) { module.exports = function (bitlen, exp) {
var k = require('node-forge').pki.rsa var k = require('node-forge').pki.rsa
@ -21,8 +21,8 @@ module.exports = function (bitlen, exp) {
, qi: _toUrlBase64(k.qInv) , qi: _toUrlBase64(k.qInv)
}; };
return { return {
publicKeyPem: Rasha.exportSync({ jwk: jwk, public: true }) publicKeyPem: Keypairs._exportSync({ jwk: jwk, public: true })
, privateKeyPem: Rasha.exportSync({ jwk: jwk }) , privateKeyPem: Keypairs._exportSync({ jwk: jwk })
, privateKeyJwk: jwk , privateKeyJwk: jwk
, publicKeyJwk: { , publicKeyJwk: {
kty: jwk.kty kty: jwk.kty

View File

@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'; 'use strict';
var Rasha = require('./rasha'); var Keypairs = require('keypairs');
module.exports = function (bitlen, exp) { module.exports = function (bitlen, exp) {
var keypair = require('crypto').generateKeyPairSync( var keypair = require('crypto').generateKeyPairSync(
@ -25,8 +25,8 @@ module.exports = function (bitlen, exp) {
publicKeyPem: keypair.publicKey.trim() publicKeyPem: keypair.publicKey.trim()
, privateKeyPem: keypair.privateKey.trim() , privateKeyPem: keypair.privateKey.trim()
}; };
result.publicKeyJwk = Rasha.importSync({ pem: result.publicKeyPem, public: true }); result.publicKeyJwk = Keypairs._importSync({ pem: result.publicKeyPem, public: true });
result.privateKeyJwk = Rasha.importSync({ pem: result.privateKeyPem }); result.privateKeyJwk = Keypairs._importSync({ pem: result.privateKeyPem });
return result; return result;
}; };

View File

@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'; 'use strict';
var Rasha = require('./rasha'); var Keypairs = require('keypairs');
module.exports = function (bitlen, exp) { module.exports = function (bitlen, exp) {
var ursa; var ursa;
@ -18,8 +18,8 @@ module.exports = function (bitlen, exp) {
publicKeyPem: keypair.toPublicPem().toString('ascii').trim() publicKeyPem: keypair.toPublicPem().toString('ascii').trim()
, privateKeyPem: keypair.toPrivatePem().toString('ascii').trim() , privateKeyPem: keypair.toPrivatePem().toString('ascii').trim()
}; };
result.publicKeyJwk = Rasha.importSync({ pem: result.publicKeyPem, public: true }); result.publicKeyJwk = Keypairs._importSync({ pem: result.publicKeyPem, public: true });
result.privateKeyJwk = Rasha.importSync({ pem: result.privateKeyPem }); result.privateKeyJwk = Keypairs._importSync({ pem: result.privateKeyPem });
return result; return result;
}; };

View File

@ -20,7 +20,8 @@ module.exports = function (bitlen, exp) {
return require('./generate-privkey-ursa.js')(bitlen, exp); return require('./generate-privkey-ursa.js')(bitlen, exp);
} catch(e) { } catch(e) {
if (e.code !== 'MODULE_NOT_FOUND') { if (e.code !== 'MODULE_NOT_FOUND') {
throw e; console.error("[rsa-compat] Unexpected error when using 'ursa':");
console.error(e);
} }
if (!oldver) { if (!oldver) {
oldver = true; oldver = true;
@ -47,11 +48,10 @@ module.exports = function (bitlen, exp) {
try { try {
return require('./generate-privkey-forge.js')(bitlen, exp); return require('./generate-privkey-forge.js')(bitlen, exp);
} catch(e) { } catch(e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
console.error("[ERROR] rsa-compat: could not generate a private key."); console.error("[ERROR] rsa-compat: could not generate a private key.");
console.error("None of crypto.generateKeyPair, ursa, nor node-forge are present"); console.error("None of crypto.generateKeyPair, ursa, nor node-forge are present");
console.error("");
throw e;
} }
} }
} }

View File

@ -1,11 +0,0 @@
kind: pipeline
name: default
pipeline:
build:
image: node
environment:
RASHA_TEST_LARGE_KEYS: "true"
commands:
- npm install --ignore-scripts
- npm test

View File

@ -1 +0,0 @@
all.*

View File

@ -1,218 +0,0 @@
[Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js) · [![Build Status](https://strong-emu-11.telebit.io/api/badges/coolaj86/rasha.js/status.svg)](https://strong-emu-11.telebit.io/coolaj86/rasha.js)
=========
Sponsored by [Root](https://therootcompany.com).
Built for [ACME.js](https://git.coolaj86.com/coolaj86/acme.js)
and [Greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js)
| ~550 lines of code | 3kb gzipped | 10kb minified | 18kb with comments |
RSA tools. Lightweight. Zero Dependencies. Universal compatibility.
* [x] Fast and Easy RSA Key Generation
* [x] PEM-to-JWK
* [x] JWK-to-PEM
* [x] SSH "pub" format
* [ ] ECDSA
* **Need EC or ECDSA tools?** Check out [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js)
## Generate RSA Key
Achieves the *fastest possible key generation* using node's native RSA bindings to OpenSSL,
then converts to JWK for ease-of-use.
```
Rasha.generate({ format: 'jwk' }).then(function (keypair) {
console.log(keypair.private);
console.log(keypair.public);
});
```
**options**
* `format` defaults to `'jwk'`
* `'pkcs1'` (traditional)
* `'pkcs8'` <!-- * `'ssh'` -->
* `modulusLength` defaults to 2048 (must not be lower)
* generally you shouldn't pick a larger key size - they're slow
* **2048** is more than sufficient
* 3072 is way, way overkill and takes a few seconds to generate
* 4096 can take about a minute to generate and is just plain wasteful
**advanced options**
These options are provided for debugging and should not be used.
* `publicExponent` defaults to 65537 (`0x10001`)
## PEM-to-JWK
* [x] PKCS#1 (traditional)
* [x] PKCS#8, SPKI/PKIX
* [x] 2048-bit, 3072-bit, 4096-bit (and ostensibily all others)
* [x] SSH (RFC4716), (RFC 4716/SSH2)
```js
var Rasha = require('rasha');
var pem = require('fs')
.readFileSync('./node_modles/rasha/fixtures/privkey-rsa-2048.pkcs1.pem', 'ascii');
Rasha.import({ pem: pem }).then(function (jwk) {
console.log(jwk);
});
```
```js
{
"kty": "RSA",
"n": "m2ttVBxPlWw06ZmGBWVDl...QlEz7UNNj9RGps_50-CNw",
"e": "AQAB",
"d": "Cpfo7Mm9Nu8YMC_xrZ54W...Our1IdDzJ_YfHPt9sHMQQ",
"p": "ynG-t9HwKCN3MWRYFdnFz...E9S4DsGcAarIuOT2TsTCE",
"q": "xIkAjgUzB1zaUzJtW2Zgv...38ahSrBFEVnxjpnPh1Q1c",
"dp": "tzDGjECFOU0ehqtuqhcu...dVGAXJoGOdv5VpaZ7B1QE",
"dq": "kh5dyDk7YCz7sUFbpsmu...aX9PKa12HFlny6K1daL48",
"qi": "AlHWbx1gp6Z9pbw_1hlS...lhmIOgRApS0t9VoXtHhFU"
}
```
## JWK-to-PEM
* [x] PKCS#1 (traditional)
* [x] PKCS#8, SPKI/PKIX
* [x] 2048-bit, 4096-bit (and ostensibily all others)
* [x] SSH (RFC4716), (RFC 4716/SSH2)
```js
var Rasha = require('rasha');
var jwk = require('rasha/fixtures/privkey-rsa-2048.jwk.json');
Rasha.export({ jwk: jwk }).then(function (pem) {
// PEM in PKCS1 (traditional) format
console.log(pem);
});
```
```
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhD
NzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ
38P8LdAIlb0pqDHxEJ9adWomjuFf.....5cCBahfsiNtNR6WV1/iCSuINYs6uPdA
Jlw7hm9m8TAmFWWyfL0s7wiRvAYkQvpxetorTwHJVLabBDJ+WBOAY2enOLHIRQv+
atAvHrLXjkUdzF96o0icyF6n7QzGfUPmeWGYg6BEClLS31Whe0eEVQ==
-----END RSA PRIVATE KEY-----
```
### Advanced Options
`format: 'pkcs8'`:
The default output format `pkcs1` (RSA-specific format) is used for private keys.
Use `format: 'pkcs8'` to output in PKCS#8 format instead.
```js
Rasha.export({ jwk: jwk, format: 'pkcs8' }).then(function (pem) {
// PEM in PKCS#8 format
console.log(pem);
});
```
```
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCba21UHE+VbDTp
mYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo
6HF5JijfWzK7haHFuRMEsgI4VwIY.....LorV1ovjwKBgAJR1m8dYKemfaW8P9YZ
Uux7lwIFqF+yI201HpZXX+IJK4g1izq490AmXDuGb2bxMCYVZbJ8vSzvCJG8BiRC
+nF62itPAclUtpsEMn5YE4BjZ6c4schFC/5q0C8esteORR3MX3qjSJzIXqftDMZ9
Q+Z5YZiDoEQKUtLfVaF7R4RV
-----END PRIVATE KEY-----
```
`format: 'ssh'`:
Although SSH uses PKCS#1 for private keys, it uses ts own special non-ASN1 format
(affectionately known as rfc4716) for public keys. I got curious and then decided
to add this format as well.
To get the same format as you
would get with `ssh-keygen`, pass `ssh` as the format option:
```js
Rasha.export({ jwk: jwk, format: 'ssh' }).then(function (pub) {
// Special SSH2 Public Key format (RFC 4716)
console.log(pub);
});
```
```
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCba21UHE.....Q02P1Eamz/nT4I3 rsa@localhost
```
`public: 'true'`:
If a private key is used as input, a private key will be output.
If you'd like to output a public key instead you can pass `public: true`.
or `format: 'spki'`.
```js
Rasha.export({ jwk: jwk, public: true }).then(function (pem) {
// PEM in SPKI/PKIX format
console.log(pem);
});
```
```
-----BEGIN PUBLIC KEY-----
MIIBCgKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJ
efLukC+xu0LBKylYojT5vTkxaOhx.....TmzCh2ikrwTMja7mUdBJf2bK3By5AB0
Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQAB
-----END PUBLIC KEY-----
```
Testing
-------
All cases are tested in `test.sh`.
You can compare these keys to the ones that you get from OpenSSL, OpenSSH/ssh-keygen, and WebCrypto:
```bash
# Generate 2048-bit RSA Keypair
openssl genrsa -out privkey-rsa-2048.pkcs1.pem 2048
# Convert PKCS1 (traditional) RSA Keypair to PKCS8 format
openssl rsa -in privkey-rsa-2048.pkcs1.pem -pubout -out pub-rsa-2048.spki.pem
# Export Public-only RSA Key in PKCS1 (traditional) format
openssl pkcs8 -topk8 -nocrypt -in privkey-rsa-2048.pkcs1.pem -out privkey-rsa-2048.pkcs8.pem
# Convert PKCS1 (traditional) RSA Public Key to SPKI/PKIX format
openssl rsa -in pub-rsa-2048.spki.pem -pubin -RSAPublicKey_out -out pub-rsa-2048.pkcs1.pem
# Convert RSA public key to SSH format
ssh-keygen -f ./pub-rsa-2048.spki.pem -i -mPKCS8 > ./pub-rsa-2048.ssh.pub
```
Goals of this project
-----
* Focused support for 2048-bit and 4096-bit RSA keypairs (although any size is technically supported)
* Zero Dependencies
* VanillaJS
* Quality Code: Good comments and tests
* Convert both ways: PEM-to-JWK and JWK-to-PEM (also supports SSH pub files)
* Browser support as well (TODO)
* OpenSSL, ssh-keygen, and WebCrypto compatibility
Legal
-----
[Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js) |
MPL-2.0 |
[Terms of Use](https://therootcompany.com/legal/#terms) |
[Privacy Policy](https://therootcompany.com/legal/#privacy)

View File

@ -1,63 +0,0 @@
ACME <- rsa-compat
unsign csr
greenlock-ecdsa
greenlock-ec-pem-to-jwk
greenlock-ec-jwk-to-pem
greenlock-ec-ssh-to-jwk
greenlock-ec-jwk-to-ssh
greenlock-ec-csr
greenlock-rsa
greenlock-rsa-pem-to-jwk
greenlock-rsa-jwk-to-pem
greenlock-rsa-ssh-to-jwk
greenlock-rsa-jwk-to-ssh
greenlock-rsa-csr
greenlock-x509
greenlock-acme
greenlock-asn1
greenlock-encoding
bluecrypt-ecdsa
bluecrypt-ec-pem-to-jwk
bluecrypt-ec-jwk-to-pem
bluecrypt-ec-ssh-to-jwk
bluecrypt-ec-jwk-to-ssh
bluecrypt-ec-csr
bluecrypt-rsa
bluecrypt-rsa-pem-to-jwk
bluecrypt-rsa-jwk-to-pem
bluecrypt-rsa-ssh-to-jwk
bluecrypt-rsa-jwk-to-ssh
bluecrypt-rsa-csr
bluecrypt-ans1
bluecrypt-x509
bluecrypt-acme
bluecrypt-encoding
keypairs
asymmetric
symmetric
groot-crypto
broot-crypto
** unified openssl commands **
https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b
https://connect2id.com/products/nimbus-jose-jwt/examples/jwk-generation
https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}

View File

@ -1,97 +0,0 @@
#!/usr/bin/env node
'use strict';
var fs = require('fs');
var Rasha = require('../index.js');
var PEM = require('../lib/pem.js');
var ASN1 = require('../lib/asn1.js');
var infile = process.argv[2];
var format = process.argv[3];
var msg = process.argv[4];
var sign;
if ('sign' === format) {
sign = true;
format = 'pkcs8';
}
if (!infile) {
infile = 'jwk';
}
if (-1 !== [ 'jwk', 'pem', 'json', 'der', 'pkcs1', 'pkcs8', 'spki' ].indexOf(infile)) {
console.log("Generating new key...");
Rasha.generate({
format: infile
, modulusLength: parseInt(format, 10) || 2048
, encoding: parseInt(format, 10) ? null : format
}).then(function (key) {
if ('der' === infile || 'der' === format) {
key.private = key.private.toString('binary');
key.public = key.public.toString('binary');
}
console.log(key.private);
console.log(key.public);
}).catch(function (err) {
console.error(err);
process.exit(1);
});
return;
}
var key = fs.readFileSync(infile, 'ascii');
try {
key = JSON.parse(key);
} catch(e) {
// ignore
}
if ('string' === typeof key) {
if ('tpl' === format) {
var block = PEM.parseBlock(key);
var asn1 = ASN1.parse(block.der);
ASN1.tpl(asn1);
return;
}
if (sign) { signMessage(key, msg); return; }
var pub = (-1 !== [ 'public', 'spki', 'pkix' ].indexOf(format));
Rasha.import({ pem: key, public: (pub || format) }).then(function (jwk) {
console.info(JSON.stringify(jwk, null, 2));
}).catch(function (err) {
console.error(err);
process.exit(1);
});
} else {
Rasha.export({ jwk: key, format: format }).then(function (pem) {
if (sign) { signMessage(pem, msg); return; }
console.info(pem);
}).catch(function (err) {
console.error(err);
process.exit(2);
});
}
function signMessage(pem, name) {
var msg;
try {
msg = fs.readFileSync(name);
} catch(e) {
console.warn("[info] input string did not exist as a file, signing the string itself");
msg = Buffer.from(name, 'binary');
}
var crypto = require('crypto');
var sign = crypto.createSign('SHA256');
sign.write(msg)
sign.end()
var buf = sign.sign(pem, 'hex');
console.log(buf);
//console.log(buf.toString('base64'));
/*
Rasha.sign({ pem: pem, message: msg, alg: 'SHA256' }).then(function (sig) {
}).catch(function () {
console.error(err);
process.exit(3);
});
*/
}

View File

@ -1,11 +0,0 @@
{
"kty": "RSA",
"n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw",
"e": "AQAB",
"d": "Cpfo7Mm9Nu8YMC_xrZ54W9mKHPkCG9rZ93Ds9PNp-RXUgb-ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSBsJyNv2LJZon-e85X74nv53UlIkmo9SYxdLz2JaJ-iIWEe8Qh-7llLktrTJV_xr98_tbhgSppz_IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF_WtX67XJ0C6-LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV-NbthEn5rmRtVnjRZ3yaxQ0ud8vC-NONn7yvGUlOur1IdDzJ_YfHPt9sHMQQ",
"p": "ynG-t9HwKCN3MWRYFdnFzi9-02Qcy3p8B5pu3ary2E70hYn2pHlUG2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU_UYEbAtPv_PxSmzpQp9n9XnYvBLBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCE",
"q": "xIkAjgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c_8397_DZDDo46fnFYjs6uPa03HpmKUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnFD2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1c",
"dp": "tzDGjECFOU0ehqtuqhcuT63a7h8hj19-7MJqoFwY9HQ-ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyhc_nAVfYPEC_2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k-q2tp3ieBYdVGAXJoGOdv5VpaZ7B1QE",
"dq": "kh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3xN7iUVmAg-ToKjwbVnG5-7eXiC779rQVwnrD_0yh1AFJ8wjRPqDIR7ObXGHikIxT1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7-NxLKWzqao_u4lhnDQaX9PKa12HFlny6K1daL48",
"qi": "AlHWbx1gp6Z9pbw_1hlS7HuXAgWoX7IjbTUelldf4gkriDWLOrj3QCZcO4ZvZvEwJhVlsny9LO8IkbwGJEL6cXraK08ByVS2mwQyflgTgGNnpzixyEUL_mrQLx6y145FHcxfeqNInMhep-0Mxn1D5nlhmIOgRApS0t9VoXtHhFU"
}

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhD
NzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ
38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57
qAZM6+I70on0/iDZm7+jcqOPgADAmbWHhy67BXkk4yy/YzD4yOGZFXZcNp915/TW
5bRd//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By
5AB0Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQABAoIBAAqX6OzJvTbvGDAv
8a2eeFvZihz5Ahva2fdw7PTzafkV1IG/pY02xT2VrGMRjSi5ZRc/CzaLK9aU9mQz
K8DZNAH9Yljbw+oJMl2okHQsogz0gbCcjb9iyWaJ/nvOV++J7+d1JSJJqPUmMXS8
9iWifoiFhHvEIfu5ZS5La0yVf8a/fP7W4YEqac/yHjspqt0hGWkBzPKU1O8O17wo
9qW/a/HzdDNF3IFmSGn9yxhH5IRf1rV+u1ydAuvi25E+dY7ZS0TRl+oWhnbHF0tD
lloyjicyhrlfjW7YRJ+a5kbVZ40Wd8msUNLnfLwvjTjZ+8rxlJTrq9SHQ8yf2Hxz
7fbBzEECgYEAynG+t9HwKCN3MWRYFdnFzi9+02Qcy3p8B5pu3ary2E70hYn2pHlU
G2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU/UYEbAtPv/PxSmzpQp9n9XnYvB
LBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCECgYEAxIkA
jgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c/8397/DZDDo46fnFYjs6uPa03Hpm
KUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnF
D2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1cCgYEAtzDGjECFOU0ehqtuqhcu
T63a7h8hj19+7MJqoFwY9HQ+ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyh
c/nAVfYPEC/2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k+q2tp3ie
BYdVGAXJoGOdv5VpaZ7B1QECgYEAkh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3
xN7iUVmAg+ToKjwbVnG5+7eXiC779rQVwnrD/0yh1AFJ8wjRPqDIR7ObXGHikIxT
1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7+NxLKWzqao/u4lhnDQaX9PKa12HFlny6
K1daL48CgYACUdZvHWCnpn2lvD/WGVLse5cCBahfsiNtNR6WV1/iCSuINYs6uPdA
Jlw7hm9m8TAmFWWyfL0s7wiRvAYkQvpxetorTwHJVLabBDJ+WBOAY2enOLHIRQv+
atAvHrLXjkUdzF96o0icyF6n7QzGfUPmeWGYg6BEClLS31Whe0eEVQ==
-----END RSA PRIVATE KEY-----

View File

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCba21UHE+VbDTp
mYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo
6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/R
JSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZ
tYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYN
IP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Ea
mz/nT4I3AgMBAAECggEACpfo7Mm9Nu8YMC/xrZ54W9mKHPkCG9rZ93Ds9PNp+RXU
gb+ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSB
sJyNv2LJZon+e85X74nv53UlIkmo9SYxdLz2JaJ+iIWEe8Qh+7llLktrTJV/xr98
/tbhgSppz/IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF/W
tX67XJ0C6+LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV+NbthEn5rmRtVnjRZ3
yaxQ0ud8vC+NONn7yvGUlOur1IdDzJ/YfHPt9sHMQQKBgQDKcb630fAoI3cxZFgV
2cXOL37TZBzLenwHmm7dqvLYTvSFifakeVQbZr0E0TxznEdDcfHjdahZ/qzTM66c
9XUbbwuRT9RgRsC0+/8/FKbOlCn2f1edi8EsEXxjfPuDGAufW9UU0BGtB1G2Pzup
jdrDsT1LgOwZwBqsi45PZOxMIQKBgQDEiQCOBTMHXNpTMm1bZmC+n1xhivUOakff
R49mXdz/zf3v8NkMOjjp+cViOzq49rTcemYpSduPCvXhAeV9eWe0kES5fFyouOR0
p0nihvvG54tMriy6j1XwtKuQsKFXGVlDCcUPYTmWXSxV54N4wkdDDfxqFKsEURWf
GOmc+HVDVwKBgQC3MMaMQIU5TR6Gq26qFy5PrdruHyGPX37swmqgXBj0dD4AuR9f
Jgtd4FIbEdvKIhi6h1mDoux8w2BQnevcTKFz+cBV9g8QL/Yh0AbhZiaruJdgX0tB
XT2oSwpsXKSbtB6QTeE7vCzCjmT6ra2neJ4Fh1UYBcmgY52/lWlpnsHVAQKBgQCS
Hl3IOTtgLPuxQVumya4B64+OgfaCGiiHa7fE3uJRWYCD5OgqPBtWcbn7t5eILvv2
tBXCesP/TKHUAUnzCNE+oMhHs5tcYeKQjFPVVJBaqImboB9nMOwvQtQPhhLeI92G
hvv43EspbOpqj+7iWGcNBpf08prXYcWWfLorV1ovjwKBgAJR1m8dYKemfaW8P9YZ
Uux7lwIFqF+yI201HpZXX+IJK4g1izq490AmXDuGb2bxMCYVZbJ8vSzvCJG8BiRC
+nF62itPAclUtpsEMn5YE4BjZ6c4schFC/5q0C8esteORR3MX3qjSJzIXqftDMZ9
Q+Z5YZiDoEQKUtLfVaF7R4RV
-----END PRIVATE KEY-----

View File

@ -1,5 +0,0 @@
{
"kty": "RSA",
"n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw",
"e": "AQAB"
}

View File

@ -1,8 +0,0 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJ
efLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8
LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM
6+I70on0/iDZm7+jcqOPgADAmbWHhy67BXkk4yy/YzD4yOGZFXZcNp915/TW5bRd
//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0
Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQAB
-----END RSA PUBLIC KEY-----

View File

@ -1,9 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2ttVBxPlWw06ZmGBWVD
lfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo
31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP
7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6+I70on0/iDZm7+jcqOPgADAmbWHhy67
BXkk4yy/YzD4yOGZFXZcNp915/TW5bRd//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5
eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps/50+C
NwIDAQAB
-----END PUBLIC KEY-----

View File

@ -1 +0,0 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCba21UHE+VbDTpmYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/RJSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZtYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYNIP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Eamz/nT4I3 rsa@localhost

View File

@ -1,2 +0,0 @@
'use strict';
module.exports = require('./lib/rasha.js');

View File

@ -1,235 +0,0 @@
'use strict';
//
// A dumbed-down, minimal ASN.1 parser / packer combo
//
// Note: generally I like to write congruent code
// (i.e. output can be used as input and vice-versa)
// However, this seemed to be more readable and easier
// to use written as-is, asymmetrically.
// (I also generally prefer to export objects rather
// functions but, yet again, asthetics one in this case)
var Enc = require('./encoding.js');
//
// Packer
//
// Almost every ASN.1 type that's important for CSR
// can be represented generically with only a few rules.
var ASN1 = module.exports = function ASN1(/*type, hexstrings...*/) {
var args = Array.prototype.slice.call(arguments);
var typ = args.shift();
var str = args.join('').replace(/\s+/g, '').toLowerCase();
var len = (str.length/2);
var lenlen = 0;
var hex = typ;
// We can't have an odd number of hex chars
if (len !== Math.round(len)) {
throw new Error("invalid hex");
}
// The first byte of any ASN.1 sequence is the type (Sequence, Integer, etc)
// The second byte is either the size of the value, or the size of its size
// 1. If the second byte is < 0x80 (128) it is considered the size
// 2. If it is > 0x80 then it describes the number of bytes of the size
// ex: 0x82 means the next 2 bytes describe the size of the value
// 3. The special case of exactly 0x80 is "indefinite" length (to end-of-file)
if (len > 127) {
lenlen += 1;
while (len > 255) {
lenlen += 1;
len = len >> 8;
}
}
if (lenlen) { hex += Enc.numToHex(0x80 + lenlen); }
return hex + Enc.numToHex(str.length/2) + str;
};
// The Integer type has some special rules
ASN1.UInt = function UINT() {
var str = Array.prototype.slice.call(arguments).join('');
var first = parseInt(str.slice(0, 2), 16);
// If the first byte is 0x80 or greater, the number is considered negative
// Therefore we add a '00' prefix if the 0x80 bit is set
if (0x80 & first) { str = '00' + str; }
return ASN1('02', str);
};
// The Bit String type also has a special rule
ASN1.BitStr = function BITSTR() {
var str = Array.prototype.slice.call(arguments).join('');
// '00' is a mask of how many bits of the next byte to ignore
return ASN1('03', '00' + str);
};
//
// Parser
//
ASN1.ELOOP = "uASN1.js Error: iterated over 15+ elements (probably a malformed file)";
ASN1.EDEEP = "uASN1.js Error: element nested 20+ layers deep (probably a malformed file)";
// Container Types are Sequence 0x30, Octect String 0x04, Array? (0xA0, 0xA1)
// Value Types are Integer 0x02, Bit String 0x03, Null 0x05, Object ID 0x06,
// Sometimes Bit String is used as a container (RSA Pub Spki)
ASN1.VTYPES = [ 0x02, 0x03, 0x05, 0x06, 0x0c, 0x82 ];
ASN1.parse = function parseAsn1(buf, depth, ws) {
if (!ws) { ws = ''; }
if (depth >= 20) { throw new Error(ASN1.EDEEP); }
var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1)
var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] };
var child;
var iters = 0;
var adjust = 0;
var adjustedLen;
// Determine how many bytes the length uses, and what it is
if (0x80 & asn1.length) {
asn1.lengthSize = 0x7f & asn1.length;
// I think that buf->hex->int solves the problem of Endianness... not sure
asn1.length = parseInt(Enc.bufToHex(buf.slice(index, index + asn1.lengthSize)), 16);
index += asn1.lengthSize;
}
// High-order bit Integers have a leading 0x00 to signify that they are positive.
// Bit Streams use the first byte to signify padding, which x.509 doesn't use.
if (0x00 === buf[index] && (0x02 === asn1.type || 0x03 === asn1.type)) {
// However, 0x00 on its own is a valid number
if (asn1.length > 1) {
index += 1;
adjust = -1;
}
}
adjustedLen = asn1.length + adjust;
//console.warn(ws + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
// this is a primitive value type
if (-1 !== ASN1.VTYPES.indexOf(asn1.type)) {
asn1.value = buf.slice(index, index + adjustedLen);
return asn1;
}
asn1.children = [];
//console.warn('1 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', 0);
while (iters < 15 && index < (2 + asn1.length + asn1.lengthSize)) {
iters += 1;
child = ASN1.parse(buf.slice(index, index + adjustedLen), (depth || 0) + 1, ws + ' ');
// The numbers don't match up exactly and I don't remember why...
// probably something with adjustedLen or some such, but the tests pass
index += (2 + child.lengthSize + child.length);
//console.warn('2 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', (2 + child.lengthSize + child.length));
if (index > (2 + asn1.lengthSize + asn1.length)) {
console.error(JSON.stringify(asn1, function (k, v) {
if ('value' === k) { return '0x' + Enc.bufToHex(v.data); } return v;
}, 2));
throw new Error("Parse error: child value length (" + child.length
+ ") is greater than remaining parent length (" + (asn1.length - index)
+ " = " + asn1.length + " - " + index + ")");
}
asn1.children.push(child);
//console.warn(ws + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
}
if (index !== (2 + asn1.lengthSize + asn1.length)) {
console.warn('index:', index, 'length:', (2 + asn1.lengthSize + asn1.length))
throw new Error("premature end-of-file");
}
if (iters >= 15) { throw new Error(ASN1.ELOOP); }
return asn1;
};
/*
ASN1._stringify = function(asn1) {
//console.log(JSON.stringify(asn1, null, 2));
//console.log(asn1);
var ws = '';
function write(asn1) {
console.log(ws, 'ch', Enc.numToHex(asn1.type), asn1.length);
if (!asn1.children) {
return;
}
asn1.children.forEach(function (a) {
ws += '\t';
write(a);
ws = ws.slice(1);
});
}
write(asn1);
};
*/
ASN1.tpl = function (asn1) {
//console.log(JSON.stringify(asn1, null, 2));
//console.log(asn1);
var sp = ' ';
var ws = sp;
var i = 0;
var vars = [];
var str = ws;
function write(asn1, k) {
str += "\n" + ws;
var val;
if ('number' !== typeof k) {
// ignore
} else {
str += ', ';
}
if (0x02 === asn1.type) {
str += "ASN1.UInt(";
} else if (0x03 === asn1.type) {
str += "ASN1.BitStr(";
} else {
str += "ASN1('" + Enc.numToHex(asn1.type) + "'";
}
if (!asn1.children) {
if (0x05 !== asn1.type) {
if (0x06 !== asn1.type) {
val = asn1.value || new Uint8Array(0);
vars.push("\n// 0x" + Enc.numToHex(val.byteLength) + " (" + val.byteLength + " bytes)\nopts.tpl" + i + " = '"
+ Enc.bufToHex(val) + "';");
if (0x02 !== asn1.type && 0x03 !== asn1.type) {
str += ", ";
}
str += "Enc.bufToHex(opts.tpl" + i + ")";
} else {
str += ", '" + Enc.bufToHex(asn1.value) + "'";
}
} else {
console.warn("XXXXXXXXXXXXXXXXXXXXX");
}
str += ")";
return ;
}
asn1.children.forEach(function (a, j) {
i += 1;
ws += sp;
write(a, j);
ws = ws.slice(sp.length);
});
str += "\n" + ws + ")";
}
write(asn1);
console.log('var opts = {};');
console.log(vars.join('\n') + '\n');
console.log();
console.log('function buildSchema(opts) {');
console.log(sp + 'return Enc.hexToBuf(' + str.slice(3) + ');');
console.log('}');
console.log();
console.log('buildSchema(opts);');
};
module.exports = ASN1;

View File

@ -1,104 +0,0 @@
'use strict';
var Enc = module.exports;
Enc.base64ToBuf = function (str) {
// always convert from urlsafe base64, just in case
//return Buffer.from(Enc.urlBase64ToBase64(str)).toString('base64');
// node handles urlBase64 automatically
return Buffer.from(str, 'base64');
};
Enc.base64ToHex = function (b64) {
return Enc.bufToHex(Enc.base64ToBuf(b64));
};
Enc.bufToBase64 = function (u8) {
// we want to maintain api compatability with browser APIs,
// so we assume that this could be a Uint8Array
return Buffer.from(u8).toString('base64');
};
/*
Enc.bufToUint8 = function bufToUint8(buf) {
return new Uint8Array(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength));
};
*/
Enc.bufToUrlBase64 = function (u8) {
return Enc.bufToBase64(u8)
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
};
Enc.bufToHex = function (u8) {
var hex = [];
var i, h;
var len = (u8.byteLength || u8.length);
for (i = 0; i < len; i += 1) {
h = u8[i].toString(16);
if (2 !== h.length) { h = '0' + h; }
hex.push(h);
}
return hex.join('').toLowerCase();
};
Enc.hexToBase64 = function (hex) {
return Buffer.from(hex, 'hex').toString('base64');
};
Enc.hexToBuf = function (hex) {
return Buffer.from(hex, 'hex');
};
Enc.numToHex = function (d) {
d = d.toString(16);
if (d.length % 2) {
return '0' + d;
}
return d;
};
/*
Enc.strToBase64 = function (str) {
// node automatically can tell the difference
// between uc2 (utf-8) strings and binary strings
// so we don't have to re-encode the strings
return Buffer.from(str).toString('base64');
};
*/
/*
Enc.strToBin = function (str) {
var escstr = encodeURIComponent(str);
// replaces any uri escape sequence, such as %0A,
// with binary escape, such as 0x0A
var binstr = escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
return String.fromCharCode(parseInt(p1, 16));
});
return binstr;
};
*/
Enc.strToBuf = function (str) {
return Buffer.from(str);
};
Enc.strToHex = function (str) {
return Buffer.from(str).toString('hex');
};
/*
Enc.urlBase64ToBase64 = function (str) {
var r = str % 4;
if (2 === r) {
str += '==';
} else if (3 === r) {
str += '=';
}
return str.replace(/-/g, '+').replace(/_/g, '/');
};
*/

View File

@ -1,28 +0,0 @@
'use strict';
var PEM = module.exports;
var Enc = require('./encoding.js');
PEM.parseBlock = function pemToDer(pem) {
var lines = pem.trim().split(/\n/);
var end = lines.length - 1;
var head = lines[0].match(/-----BEGIN (.*)-----/);
var foot = lines[end].match(/-----END (.*)-----/);
if (head) {
lines = lines.slice(1, end);
head = head[1];
if (head !== foot[1]) {
throw new Error("headers and footers do not match");
}
}
return { type: head, bytes: Enc.base64ToBuf(lines.join('')) };
};
PEM.packBlock = function (opts) {
return '-----BEGIN ' + opts.type + '-----\n'
+ Enc.bufToBase64(opts.bytes).match(/.{1,64}/g).join('\n') + '\n'
+ '-----END ' + opts.type + '-----'
;
};

View File

@ -1,204 +0,0 @@
'use strict';
var RSA = module.exports;
var SSH = require('./ssh.js');
var PEM = require('./pem.js');
var x509 = require('./x509.js');
var ASN1 = require('./asn1.js');
/*global Promise*/
RSA.generate = function (opts) {
return Promise.resolve().then(function () {
var typ = 'rsa';
var format = opts.format;
var encoding = opts.encoding;
var priv;
var pub;
if (!format) {
format = 'jwk';
}
if ('spki' === format || 'pkcs8' === format) {
format = 'pkcs8';
pub = 'spki';
}
if ('pem' === format) {
format = 'pkcs1';
encoding = 'pem';
} else if ('der' === format) {
format = 'pkcs1';
encoding = 'der';
}
if ('jwk' === format || 'json' === format) {
format = 'jwk';
encoding = 'json';
} else {
priv = format;
pub = pub || format;
}
if (!encoding) {
encoding = 'pem';
}
if (priv) {
priv = { type: priv, format: encoding };
pub = { type: pub, format: encoding };
} else {
// jwk
priv = { type: 'pkcs1', format: 'pem' };
pub = { type: 'pkcs1', format: 'pem' };
}
return new Promise(function (resolve, reject) {
return require('crypto').generateKeyPair(typ, {
modulusLength: opts.modulusLength || 2048
, publicExponent: opts.publicExponent || 0x10001
, privateKeyEncoding: priv
, publicKeyEncoding: pub
}, function (err, pubkey, privkey) {
if (err) { reject(err); }
resolve({
private: privkey
, public: pubkey
});
});
}).then(function (keypair) {
if ('jwk' !== format) {
return keypair;
}
return {
private: RSA.importSync({ pem: keypair.private, format: priv.type })
, public: RSA.importSync({ pem: keypair.public, format: pub.type, public: true })
};
});
});
};
RSA.importSync = function (opts) {
if (!opts || !opts.pem || 'string' !== typeof opts.pem) {
throw new Error("must pass { pem: pem } as a string");
}
var jwk = { kty: 'RSA', n: null, e: null };
if (0 === opts.pem.indexOf('ssh-rsa ')) {
return SSH.parse(opts.pem, jwk);
}
var pem = opts.pem;
var block = PEM.parseBlock(pem);
//var hex = toHex(u8);
var asn1 = ASN1.parse(block.bytes);
var meta = x509.guess(block.bytes, asn1);
if ('pkcs1' === meta.format) {
jwk = x509.parsePkcs1(block.bytes, asn1, jwk);
} else {
jwk = x509.parsePkcs8(block.bytes, asn1, jwk);
}
if (opts.public) {
jwk = RSA.nueter(jwk);
}
return jwk;
};
RSA.parse = function parseRsa(opts) {
// wrapped in a promise for API compatibility
// with the forthcoming browser version
// (and potential future native node capability)
return Promise.resolve().then(function () {
return RSA.importSync(opts);
});
};
RSA.toJwk = RSA.import = RSA.parse;
/*
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
*/
RSA.exportSync = function (opts) {
if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
throw new Error("must pass { jwk: jwk }");
}
var jwk = JSON.parse(JSON.stringify(opts.jwk));
var format = opts.format;
var pub = opts.public;
if (pub || -1 !== [ 'spki', 'pkix', 'ssh', 'rfc4716' ].indexOf(format)) {
jwk = RSA.nueter(jwk);
}
if ('RSA' !== jwk.kty) {
throw new Error("options.jwk.kty must be 'RSA' for RSA keys");
}
if (!jwk.p) {
// TODO test for n and e
pub = true;
if (!format || 'pkcs1' === format) {
format = 'pkcs1';
} else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
format = 'spki';
} else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
format = 'ssh';
} else {
throw new Error("options.format must be 'spki', 'pkcs1', or 'ssh' for public RSA keys, not ("
+ typeof format + ") " + format);
}
} else {
// TODO test for all necessary keys (d, p, q ...)
if (!format || 'pkcs1' === format) {
format = 'pkcs1';
} else if ('pkcs8' !== format) {
throw new Error("options.format must be 'pkcs1' or 'pkcs8' for private RSA keys");
}
}
if ('pkcs1' === format) {
if (jwk.d) {
return PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: x509.packPkcs1(jwk) });
} else {
return PEM.packBlock({ type: "RSA PUBLIC KEY", bytes: x509.packPkcs1(jwk) });
}
} else if ('pkcs8' === format) {
return PEM.packBlock({ type: "PRIVATE KEY", bytes: x509.packPkcs8(jwk) });
} else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
return PEM.packBlock({ type: "PUBLIC KEY", bytes: x509.packSpki(jwk) });
} else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
return SSH.pack({ jwk: jwk, comment: opts.comment });
} else {
throw new Error("Sanity Error: reached unreachable code block with format: " + format);
}
};
RSA.pack = function (opts) {
// wrapped in a promise for API compatibility
// with the forthcoming browser version
// (and potential future native node capability)
return Promise.resolve().then(function () {
return RSA.exportSync(opts);
});
};
RSA.toPem = RSA.export = RSA.pack;
// snip the _private_ parts... hAHAHAHA!
RSA.nueter = function (jwk) {
// (snip rather than new object to keep potential extra data)
// otherwise we could just do this:
// return { kty: jwk.kty, n: jwk.n, e: jwk.e };
[ 'p', 'q', 'd', 'dp', 'dq', 'qi' ].forEach(function (key) {
if (key in jwk) { jwk[key] = undefined; }
return jwk;
});
return jwk;
};

View File

@ -1,80 +0,0 @@
'use strict';
var SSH = module.exports;
var Enc = require('./encoding.js');
// 7 s s h - r s a
SSH.RSA = '00000007 73 73 68 2d 72 73 61'.replace(/\s+/g, '').toLowerCase();
SSH.parse = function (pem, jwk) {
var parts = pem.split(/\s+/);
var buf = Enc.base64ToBuf(parts[1]);
var els = [];
var index = 0;
var len;
var i = 0;
var offset = (buf.byteOffset || 0);
// using dataview to be browser-compatible (I do want _some_ code reuse)
var dv = new DataView(buf.buffer.slice(offset, offset + buf.byteLength));
var el;
if (SSH.RSA !== Enc.bufToHex(buf.slice(0, SSH.RSA.length/2))) {
throw new Error("does not lead with ssh header");
}
while (index < buf.byteLength) {
i += 1;
if (i > 3) { throw new Error("15+ elements, probably not a public ssh key"); }
len = dv.getUint32(index, false);
index += 4;
el = buf.slice(index, index + len);
// remove BigUInt '00' prefix
if (0x00 === el[0]) {
el = el.slice(1);
}
els.push(el);
index += len;
}
jwk.n = Enc.bufToUrlBase64(els[2]);
jwk.e = Enc.bufToUrlBase64(els[1]);
return jwk;
};
SSH.pack = function (opts) {
var jwk = opts.jwk;
var header = 'ssh-rsa';
var comment = opts.comment || 'rsa@localhost';
var e = SSH._padHexInt(Enc.base64ToHex(jwk.e));
var n = SSH._padHexInt(Enc.base64ToHex(jwk.n));
var hex = [
SSH._numToUint32Hex(header.length)
, Enc.strToHex(header)
, SSH._numToUint32Hex(e.length/2)
, e
, SSH._numToUint32Hex(n.length/2)
, n
].join('');
return [ header, Enc.hexToBase64(hex), comment ].join(' ');
};
SSH._numToUint32Hex = function (num) {
var hex = num.toString(16);
while (hex.length < 8) {
hex = '0' + hex;
}
return hex;
};
SSH._padHexInt = function (hex) {
// BigInt is negative if the high order bit 0x80 is set,
// so ASN1, SSH, and many other formats pad with '0x00'
// to signifiy a positive number.
var i = parseInt(hex.slice(0, 2), 16);
if (0x80 & i) {
return '00' + hex;
}
return hex;
};

View File

@ -1,111 +0,0 @@
'use strict';
// We believe in a proactive approach to sustainable open source.
// As part of that we make it easy for you to opt-in to following our progress
// and we also stay up-to-date on telemetry such as operating system and node
// version so that we can focus our efforts where they'll have the greatest impact.
//
// Want to learn more about our Terms, Privacy Policy, and Mission?
// Check out https://therootcompany.com/legal/
var os = require('os');
var crypto = require('crypto');
var https = require('https');
var pkg = require('../package.json');
// to help focus our efforts in the right places
var data = {
package: pkg.name
, version: pkg.version
, node: process.version
, arch: process.arch || os.arch()
, platform: process.platform || os.platform()
, release: os.release()
};
function addCommunityMember(opts) {
setTimeout(function () {
var req = https.request({
hostname: 'api.therootcompany.com'
, port: 443
, path: '/api/therootcompany.com/public/community'
, method: 'POST'
, headers: { 'Content-Type': 'application/json' }
}, function (resp) {
// let the data flow, so we can ignore it
resp.on('data', function () {});
//resp.on('data', function (chunk) { console.log(chunk.toString()); });
resp.on('error', function () { /*ignore*/ });
//resp.on('error', function (err) { console.error(err); });
});
var obj = JSON.parse(JSON.stringify(data));
obj.action = 'updates';
try {
obj.ppid = ppid(obj.action);
} catch(e) {
// ignore
//console.error(e);
}
obj.name = opts.name || undefined;
obj.address = opts.email;
obj.community = 'node.js@therootcompany.com';
req.write(JSON.stringify(obj, 2, null));
req.end();
req.on('error', function () { /*ignore*/ });
//req.on('error', function (err) { console.error(err); });
}, 50);
}
function ping(action) {
setTimeout(function () {
var req = https.request({
hostname: 'api.therootcompany.com'
, port: 443
, path: '/api/therootcompany.com/public/ping'
, method: 'POST'
, headers: { 'Content-Type': 'application/json' }
}, function (resp) {
// let the data flow, so we can ignore it
resp.on('data', function () { });
//resp.on('data', function (chunk) { console.log(chunk.toString()); });
resp.on('error', function () { /*ignore*/ });
//resp.on('error', function (err) { console.error(err); });
});
var obj = JSON.parse(JSON.stringify(data));
obj.action = action;
try {
obj.ppid = ppid(obj.action);
} catch(e) {
// ignore
//console.error(e);
}
req.write(JSON.stringify(obj, 2, null));
req.end();
req.on('error', function (/*e*/) { /*console.error('req.error', e);*/ });
}, 50);
}
// to help identify unique installs without getting
// the personally identifiable info that we don't want
function ppid(action) {
var parts = [ action, data.package, data.version, data.node, data.arch, data.platform, data.release ];
var ifaces = os.networkInterfaces();
Object.keys(ifaces).forEach(function (ifname) {
if (/^en/.test(ifname) || /^eth/.test(ifname) || /^wl/.test(ifname)) {
if (ifaces[ifname] && ifaces[ifname].length) {
parts.push(ifaces[ifname][0].mac);
}
}
});
return crypto.createHash('sha1').update(parts.join(',')).digest('base64');
}
module.exports.ping = ping;
module.exports.joinCommunity = addCommunityMember;
if (require.main === module) {
ping('install');
//addCommunityMember({ name: "AJ ONeal", email: 'coolaj86@gmail.com' });
}

View File

@ -1,153 +0,0 @@
'use strict';
var x509 = module.exports;
var ASN1 = require('./asn1.js');
var Enc = require('./encoding.js');
x509.guess = function (der, asn1) {
// accepting der for compatability with other usages
var meta = { kty: 'RSA', format: 'pkcs1', public: true };
//meta.asn1 = ASN1.parse(u8);
if (asn1.children.every(function(el) {
return 0x02 === el.type;
})) {
if (2 === asn1.children.length) {
// rsa pkcs1 public
return meta;
} else if (asn1.children.length >= 9) {
// the standard allows for "otherPrimeInfos", hence at least 9
meta.public = false;
// rsa pkcs1 private
return meta;
} else {
throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)");
}
} else {
meta.format = 'pkcs8';
}
return meta;
};
x509.parsePkcs1 = function parseRsaPkcs1(buf, asn1, jwk) {
if (!asn1.children.every(function(el) {
return 0x02 === el.type;
})) {
throw new Error("not an RSA PKCS#1 public or private key (not all ints)");
}
if (2 === asn1.children.length) {
jwk.n = Enc.bufToUrlBase64(asn1.children[0].value);
jwk.e = Enc.bufToUrlBase64(asn1.children[1].value);
return jwk;
} else if (asn1.children.length >= 9) {
// the standard allows for "otherPrimeInfos", hence at least 9
jwk.n = Enc.bufToUrlBase64(asn1.children[1].value);
jwk.e = Enc.bufToUrlBase64(asn1.children[2].value);
jwk.d = Enc.bufToUrlBase64(asn1.children[3].value);
jwk.p = Enc.bufToUrlBase64(asn1.children[4].value);
jwk.q = Enc.bufToUrlBase64(asn1.children[5].value);
jwk.dp = Enc.bufToUrlBase64(asn1.children[6].value);
jwk.dq = Enc.bufToUrlBase64(asn1.children[7].value);
jwk.qi = Enc.bufToUrlBase64(asn1.children[8].value);
return jwk;
} else {
throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)");
}
};
x509.parsePkcs8 = function parseRsaPkcs8(buf, asn1, jwk) {
if (2 === asn1.children.length
&& 0x03 === asn1.children[1].type
&& 0x30 === asn1.children[1].value[0]) {
asn1 = ASN1.parse(asn1.children[1].value);
jwk.n = Enc.bufToUrlBase64(asn1.children[0].value);
jwk.e = Enc.bufToUrlBase64(asn1.children[1].value);
} else if (3 === asn1.children.length
&& 0x04 === asn1.children[2].type
&& 0x30 === asn1.children[2].children[0].type
&& 0x02 === asn1.children[2].children[0].children[0].type) {
asn1 = asn1.children[2].children[0];
jwk.n = Enc.bufToUrlBase64(asn1.children[1].value);
jwk.e = Enc.bufToUrlBase64(asn1.children[2].value);
jwk.d = Enc.bufToUrlBase64(asn1.children[3].value);
jwk.p = Enc.bufToUrlBase64(asn1.children[4].value);
jwk.q = Enc.bufToUrlBase64(asn1.children[5].value);
jwk.dp = Enc.bufToUrlBase64(asn1.children[6].value);
jwk.dq = Enc.bufToUrlBase64(asn1.children[7].value);
jwk.qi = Enc.bufToUrlBase64(asn1.children[8].value);
} else {
throw new Error("not an RSA PKCS#8 public or private key (wrong format)");
}
return jwk;
};
x509.packPkcs1 = function (jwk) {
var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
if (!jwk.d) {
return Enc.hexToBuf(ASN1('30', n, e));
}
return Enc.hexToBuf(ASN1('30'
, ASN1.UInt('00')
, n
, e
, ASN1.UInt(Enc.base64ToHex(jwk.d))
, ASN1.UInt(Enc.base64ToHex(jwk.p))
, ASN1.UInt(Enc.base64ToHex(jwk.q))
, ASN1.UInt(Enc.base64ToHex(jwk.dp))
, ASN1.UInt(Enc.base64ToHex(jwk.dq))
, ASN1.UInt(Enc.base64ToHex(jwk.qi))
));
};
x509.packPkcs8 = function (jwk) {
if (!jwk.d) {
// Public RSA
return Enc.hexToBuf(ASN1('30'
, ASN1('30'
, ASN1('06', '2a864886f70d010101')
, ASN1('05')
)
, ASN1.BitStr(ASN1('30'
, ASN1.UInt(Enc.base64ToHex(jwk.n))
, ASN1.UInt(Enc.base64ToHex(jwk.e))
))
));
}
// Private RSA
return Enc.hexToBuf(ASN1('30'
, ASN1.UInt('00')
, ASN1('30'
, ASN1('06', '2a864886f70d010101')
, ASN1('05')
)
, ASN1('04'
, ASN1('30'
, ASN1.UInt('00')
, ASN1.UInt(Enc.base64ToHex(jwk.n))
, ASN1.UInt(Enc.base64ToHex(jwk.e))
, ASN1.UInt(Enc.base64ToHex(jwk.d))
, ASN1.UInt(Enc.base64ToHex(jwk.p))
, ASN1.UInt(Enc.base64ToHex(jwk.q))
, ASN1.UInt(Enc.base64ToHex(jwk.dp))
, ASN1.UInt(Enc.base64ToHex(jwk.dq))
, ASN1.UInt(Enc.base64ToHex(jwk.qi))
)
)
));
};
x509.packSpki = x509.packPkcs8;

View File

@ -1,40 +0,0 @@
{
"name": "rasha",
"version": "1.1.0",
"description": "💯 PEM-to-JWK and JWK-to-PEM for RSA keys in a lightweight, zero-dependency library focused on perfect universal compatibility.",
"homepage": "https://git.coolaj86.com/coolaj86/rasha.js",
"main": "index.js",
"bin": {
"rasha": "bin/rasha.js"
},
"files": [
"bin",
"fixtures",
"lib"
],
"directories": {
"lib": "lib"
},
"scripts": {
"postinstall": "node lib/telemetry.js event:install",
"test": "bash test.sh"
},
"repository": {
"type": "git",
"url": "https://git.coolaj86.com/coolaj86/rasha.js"
},
"keywords": [
"zero-dependency",
"PEM-to-JWK",
"JWK-to-PEM",
"RSA",
"2048",
"4096",
"asn1",
"x509",
"JWK-to-SSH",
"PEM-to-SSH"
],
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "MPL-2.0"
}

View File

@ -1,6 +0,0 @@
var Rasha = require('./');
var pem = require('fs').readFileSync(process.argv[2], 'ascii');
var jwk = Rasha.importSync({ pem: pem });
console.log(Buffer.from(jwk.n, 'base64').byteLength);

View File

@ -1,172 +0,0 @@
#!/bin/bash
# cause errors to hard-fail
# (and diff non-0 exit status will cause failure)
set -e
pemtojwk() {
keyid=$1
if [ -z "$keyid" ]; then
echo ""
echo "Testing PEM-to-JWK PKCS#1"
fi
#
node bin/rasha.js ./fixtures/privkey-rsa-2048.pkcs1.${keyid}pem \
> ./fixtures/privkey-rsa-2048.jwk.1.json
diff ./fixtures/privkey-rsa-2048.jwk.${keyid}json ./fixtures/privkey-rsa-2048.jwk.1.json
#
node bin/rasha.js ./fixtures/pub-rsa-2048.pkcs1.${keyid}pem \
> ./fixtures/pub-rsa-2048.jwk.1.json
diff ./fixtures/pub-rsa-2048.jwk.${keyid}json ./fixtures/pub-rsa-2048.jwk.1.json
if [ -z "$keyid" ]; then
echo "Pass"
fi
if [ -z "$keyid" ]; then
echo ""
echo "Testing PEM-to-JWK PKCS#8"
fi
#
node bin/rasha.js ./fixtures/privkey-rsa-2048.pkcs8.${keyid}pem \
> ./fixtures/privkey-rsa-2048.jwk.1.json
diff ./fixtures/privkey-rsa-2048.jwk.${keyid}json ./fixtures/privkey-rsa-2048.jwk.1.json
#
node bin/rasha.js ./fixtures/pub-rsa-2048.spki.${keyid}pem \
> ./fixtures/pub-rsa-2048.jwk.1.json
diff ./fixtures/pub-rsa-2048.jwk.${keyid}json ./fixtures/pub-rsa-2048.jwk.1.json
if [ -z "$keyid" ]; then
echo "Pass"
fi
}
jwktopem() {
keyid=$1
if [ -z "$keyid" ]; then
echo ""
echo "Testing JWK-to-PEM PKCS#1"
fi
#
node bin/rasha.js ./fixtures/privkey-rsa-2048.jwk.${keyid}json pkcs1 \
> ./fixtures/privkey-rsa-2048.pkcs1.1.pem
diff ./fixtures/privkey-rsa-2048.pkcs1.${keyid}pem ./fixtures/privkey-rsa-2048.pkcs1.1.pem
#
node bin/rasha.js ./fixtures/pub-rsa-2048.jwk.${keyid}json pkcs1 \
> ./fixtures/pub-rsa-2048.pkcs1.1.pem
diff ./fixtures/pub-rsa-2048.pkcs1.${keyid}pem ./fixtures/pub-rsa-2048.pkcs1.1.pem
if [ -z "$keyid" ]; then
echo "Pass"
fi
if [ -z "$keyid" ]; then
echo ""
echo "Testing JWK-to-PEM PKCS#8"
fi
#
node bin/rasha.js ./fixtures/privkey-rsa-2048.jwk.${keyid}json pkcs8 \
> ./fixtures/privkey-rsa-2048.pkcs8.1.pem
diff ./fixtures/privkey-rsa-2048.pkcs8.${keyid}pem ./fixtures/privkey-rsa-2048.pkcs8.1.pem
#
node bin/rasha.js ./fixtures/pub-rsa-2048.jwk.${keyid}json spki \
> ./fixtures/pub-rsa-2048.spki.1.pem
diff ./fixtures/pub-rsa-2048.spki.${keyid}pem ./fixtures/pub-rsa-2048.spki.1.pem
if [ -z "$keyid" ]; then
echo "Pass"
fi
if [ -z "$keyid" ]; then
echo ""
echo "Testing JWK-to-SSH"
fi
#
node bin/rasha.js ./fixtures/privkey-rsa-2048.jwk.${keyid}json ssh > ./fixtures/pub-rsa-2048.ssh.1.pub
diff ./fixtures/pub-rsa-2048.ssh.${keyid}pub ./fixtures/pub-rsa-2048.ssh.1.pub
#
node bin/rasha.js ./fixtures/pub-rsa-2048.jwk.${keyid}json ssh > ./fixtures/pub-rsa-2048.ssh.1.pub
diff ./fixtures/pub-rsa-2048.ssh.${keyid}pub ./fixtures/pub-rsa-2048.ssh.1.pub
if [ -z "$keyid" ]; then
echo "Pass"
fi
}
rndkey() {
keyid="rnd.1."
keysize=$1
# Generate 2048-bit RSA Keypair
openssl genrsa -out fixtures/privkey-rsa-2048.pkcs1.${keyid}pem $keysize
# Convert PKCS1 (traditional) RSA Keypair to PKCS8 format
openssl rsa -in fixtures/privkey-rsa-2048.pkcs1.${keyid}pem -pubout \
-out fixtures/pub-rsa-2048.spki.${keyid}pem
# Export Public-only RSA Key in PKCS1 (traditional) format
openssl pkcs8 -topk8 -nocrypt -in fixtures/privkey-rsa-2048.pkcs1.${keyid}pem \
-out fixtures/privkey-rsa-2048.pkcs8.${keyid}pem
# Convert PKCS1 (traditional) RSA Public Key to SPKI/PKIX format
openssl rsa -in fixtures/pub-rsa-2048.spki.${keyid}pem -pubin -RSAPublicKey_out \
-out fixtures/pub-rsa-2048.pkcs1.${keyid}pem
# Convert RSA public key to SSH format
sshpub=$(ssh-keygen -f fixtures/pub-rsa-2048.spki.${keyid}pem -i -mPKCS8)
echo "$sshpub rsa@localhost" > fixtures/pub-rsa-2048.ssh.${keyid}pub
# to JWK
node bin/rasha.js ./fixtures/privkey-rsa-2048.pkcs1.${keyid}pem \
> ./fixtures/privkey-rsa-2048.jwk.${keyid}json
node bin/rasha.js ./fixtures/pub-rsa-2048.pkcs1.${keyid}pem \
> ./fixtures/pub-rsa-2048.jwk.${keyid}json
pemtojwk "$keyid"
jwktopem "$keyid"
}
pemtojwk ""
jwktopem ""
echo ""
echo "testing node key generation"
node bin/rasha.js > /dev/null
node bin/rasha.js jwk > /dev/null
node bin/rasha.js json 2048 > /dev/null
node bin/rasha.js der > /dev/null
node bin/rasha.js pkcs8 der > /dev/null
node bin/rasha.js pem > /dev/null
node bin/rasha.js pkcs1 pem > /dev/null
node bin/rasha.js spki > /dev/null
echo "PASS"
echo ""
echo ""
echo "Re-running tests with random keys of varying sizes"
echo ""
# commented out sizes below 512, since they are below minimum size on some systems.
# rndkey 32 # minimum key size
# rndkey 64
# rndkey 128
# rndkey 256
rndkey 512
rndkey 768
rndkey 1024
rndkey 2048 # first secure key size
if [ "${RASHA_TEST_LARGE_KEYS}" == "true" ]; then
rndkey 3072
rndkey 4096 # largest reasonable key size
else
echo ""
echo "Note:"
echo "Keys larger than 2048 have been tested and work, but are omitted from automated tests to save time."
echo "Set RASHA_TEST_LARGE_KEYS=true to enable testing of keys up to 4096."
fi
echo ""
echo "Pass"
rm fixtures/*.1.*
echo ""
echo ""
echo "PASSED:"
echo "• All inputs produced valid outputs"
echo "• All outputs matched known-good values"
echo "• All random tests passed reciprosity"

View File

@ -171,7 +171,7 @@ Rather than trying to make a generic implementation that works with everything u
this library is intentionally focused on around the use case of generating certificates for this library is intentionally focused on around the use case of generating certificates for
ACME services (such as Let's Encrypt). ACME services (such as Let's Encrypt).
That said, [please tell me](https://git.coolaj86.com/coolaj86/rsa-csr.js/issues) if it doesn't That said, [please tell me](https://git.coolaj86.com/coolaj86/rsa-csr.js/issues/new) if it doesn't
do what you need, it may make sense to add it (or otherwise, perhaps to help you create a fork). do what you need, it may make sense to add it (or otherwise, perhaps to help you create a fork).
The primary goal of this project is for this code to do exactly (and all of) The primary goal of this project is for this code to do exactly (and all of)

View File

@ -15,9 +15,13 @@ try {
// ignore // ignore
} }
rsacsr({ key: key, domains: domains }).then(function (csr) { var csr = rsacsr.sync({ key: key, domains: domains });
console.log(csr);
/*
.then(function (csr) {
// Using error so that we can redirect stdout to file // Using error so that we can redirect stdout to file
//console.error("CN=" + domains[0]); //console.error("CN=" + domains[0]);
//console.error("subjectAltName=" + domains.join(',')); //console.error("subjectAltName=" + domains.join(','));
console.log(csr); console.log(csr);
}); });
*/

View File

@ -136,14 +136,14 @@ CSR.toDer = function encode(opts) {
RSA.signSync = function signRsaSync(keypem, ab) { RSA.signSync = function signRsaSync(keypem, ab) {
// Signer is a stream // Signer is a stream
var sign = crypto.createSign('SHA256'); var sign = crypto.createSign('SHA256');
sign.write(new Uint8Array(ab)); sign.write(ab);
sign.end(); sign.end();
// The signature is ASN1 encoded, as it turns out // The signature is ASN1 encoded, as it turns out
var sig = sign.sign(keypem); var sig = sign.sign(keypem);
// Convert to a JavaScript ArrayBuffer just because // Convert to a JavaScript ArrayBuffer just because
return new Uint8Array(sig.buffer.slice(sig.byteOffset, sig.byteOffset + sig.byteLength)); return sig.buffer.slice(sig.byteOffset, sig.byteOffset + sig.byteLength);
}; };
RSA.sign = function signRsa(keypem, ab) { RSA.sign = function signRsa(keypem, ab) {
return Promise.resolve().then(function () { return Promise.resolve().then(function () {

View File

@ -1,64 +1,34 @@
{ {
"_from": "rsa-csr", "name": "rsa-csr",
"_id": "rsa-csr@1.0.5", "version": "1.0.7",
"_inBundle": false, "description": "💯 A focused, zero-dependency library to generate a Certificate Signing Request (CSR) and sign it!",
"_integrity": "sha512-rmQY0RmcpLdsXEJgE1S2xBam09YVggDIqBGCJNFkhD6ONkmpSGjZ+28J6gWy+ygKHHgC7Z+OpzDLVQYowOte3A==", "homepage": "https://git.coolaj86.com/coolaj86/rsa-csr.js",
"_location": "/rsa-csr", "main": "index.js",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "rsa-csr",
"name": "rsa-csr",
"escapedName": "rsa-csr",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/rsa-csr/-/rsa-csr-1.0.5.tgz",
"_shasum": "ac427ae3aa16089f5f26fc93047a7d2d844b0bf4",
"_spec": "rsa-csr",
"_where": "/Volumes/Data/git.coolaj86.com/coolaj86/rsa-compat.js",
"author": {
"name": "AJ ONeal",
"email": "coolaj86@gmail.com",
"url": "https://coolaj86.com/"
},
"bin": { "bin": {
"rsa-csr": "bin/rsa-csr.js" "rsa-csr": "bin/rsa-csr.js"
}, },
"bundleDependencies": false,
"deprecated": false,
"description": "💯 A focused, zero-dependency library to generate a Certificate Signing Request (CSR) and sign it!",
"directories": {
"lib": "lib"
},
"files": [ "files": [
"bin", "bin",
"fixtures", "fixtures",
"lib" "lib"
], ],
"homepage": "https://git.coolaj86.com/coolaj86/rsa-csr.js", "directories": {
"lib": "lib"
},
"scripts": {
"postinstall": "node lib/telemetry.js event:install",
"test": "bash test.sh"
},
"repository": {
"type": "git",
"url": "https://git.coolaj86.com/coolaj86/rsa-csr.js"
},
"keywords": [ "keywords": [
"zero-dependency", "zero-dependency",
"CSR", "CSR",
"RSA", "RSA",
"x509" "x509"
], ],
"license": "MPL-2.0", "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"main": "index.js", "license": "MPL-2.0"
"name": "rsa-csr",
"repository": {
"type": "git",
"url": "https://git.coolaj86.com/coolaj86/rsa-csr.js"
},
"scripts": {
"postinstall": "node lib/telemetry.js event:install",
"test": "bash test.sh"
},
"version": "1.0.5"
} }

View File

@ -13,7 +13,7 @@ try {
/* ignore */ /* ignore */
} }
var Rasha = require('./rasha'); var Keypairs = require('keypairs');
var RSACSR = require('./rsa-csr'); var RSACSR = require('./rsa-csr');
var NOBJ = {}; var NOBJ = {};
var DEFAULT_BITLEN = 2048; var DEFAULT_BITLEN = 2048;
@ -48,12 +48,12 @@ RSA.import = function (options) {
// Private Keys // Private Keys
if (options.privateKeyPem) { if (options.privateKeyPem) {
if (!options.privateKeyJwk) { if (!options.privateKeyJwk) {
options.privateKeyJwk = Rasha.importSync({ pem: options.privateKeyPem }); options.privateKeyJwk = Keypairs._importSync({ pem: options.privateKeyPem });
} }
} }
if (options.privateKeyJwk) { if (options.privateKeyJwk) {
if (!options.privateKeyPem) { if (!options.privateKeyPem) {
options.privateKeyPem = Rasha.exportSync({ options.privateKeyPem = Keypairs._exportSync({
jwk: options.privateKeyJwk jwk: options.privateKeyJwk
, format: options.format || 'pkcs1' , format: options.format || 'pkcs1'
, encoding: options.encoding || 'pem' , encoding: options.encoding || 'pem'
@ -64,7 +64,7 @@ RSA.import = function (options) {
// Public Keys // Public Keys
if (options.publicKeyPem || options.privateKeyPem) { if (options.publicKeyPem || options.privateKeyPem) {
if (!options.publicKeyJwk) { if (!options.publicKeyJwk) {
options.publicKeyJwk = Rasha.importSync({ options.publicKeyJwk = Keypairs._importSync({
pem: options.publicKeyPem || options.privateKeyPem pem: options.publicKeyPem || options.privateKeyPem
, public: true , public: true
}); });
@ -72,7 +72,7 @@ RSA.import = function (options) {
} }
if (options.publicKeyJwk || options.privateKeyJwk) { if (options.publicKeyJwk || options.privateKeyJwk) {
if (!options.publicKeyPem) { if (!options.publicKeyPem) {
options.publicKeyPem = Rasha.exportSync({ options.publicKeyPem = Keypairs._exportSync({
jwk: options.publicKeyJwk || options.privateKeyJwk jwk: options.publicKeyJwk || options.privateKeyJwk
, format: options.format || 'pkcs1' , format: options.format || 'pkcs1'
, encoding: options.encoding || 'pem' , encoding: options.encoding || 'pem'

27
package-lock.json generated Normal file
View File

@ -0,0 +1,27 @@
{
"name": "rsa-compat",
"version": "2.0.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"eckles": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/eckles/-/eckles-1.4.1.tgz",
"integrity": "sha512-auWyk/k8oSkVHaD4RxkPadKsLUcIwKgr/h8F7UZEueFDBO7BsE4y+H6IMUDbfqKIFPg/9MxV6KcBdJCmVVcxSA=="
},
"keypairs": {
"version": "1.2.14",
"resolved": "https://registry.npmjs.org/keypairs/-/keypairs-1.2.14.tgz",
"integrity": "sha512-ZoZfZMygyB0QcjSlz7Rh6wT2CJasYEHBPETtmHZEfxuJd7bnsOG5AdtPZqHZBT+hoHvuWCp/4y8VmvTvH0Y9uA==",
"requires": {
"eckles": "^1.4.1",
"rasha": "^1.2.4"
}
},
"rasha": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/rasha/-/rasha-1.2.4.tgz",
"integrity": "sha512-GsIwKv+hYSumJyK9wkTDaERLwvWaGYh1WuI7JMTBISfYt13TkKFU/HFzlY4n72p8VfXZRUYm0AqaYhkZVxOC3Q=="
}
}
}

View File

@ -1,13 +1,15 @@
{ {
"name": "rsa-compat", "name": "rsa-compat",
"version": "1.9.0", "version": "2.0.8",
"engines": {
"node": ">=10.12"
},
"description": "RSA utils that work on Windows, Mac, and Linux with or without C compiler", "description": "RSA utils that work on Windows, Mac, and Linux with or without C compiler",
"main": "index.js", "main": "index.js",
"bin": { "bin": {
"rsa-keygen-js": "bin/rsa-keygen.js" "rsa-keygen-js": "bin/rsa-keygen.js"
}, },
"scripts": { "scripts": {
"postinstall": "node lib/telemetry.js event:install",
"test": "bash test.sh" "test": "bash test.sh"
}, },
"repository": { "repository": {
@ -19,6 +21,7 @@
"ursa", "ursa",
"forge", "forge",
"certificate", "certificate",
"csr",
"tls", "tls",
"ssl", "ssl",
"windows", "windows",
@ -33,13 +36,12 @@
"url": "https://git.coolaj86.com/coolaj86/rsa-compat.js/issues" "url": "https://git.coolaj86.com/coolaj86/rsa-compat.js/issues"
}, },
"homepage": "https://git.coolaj86.com/coolaj86/rsa-compat.js#readme", "homepage": "https://git.coolaj86.com/coolaj86/rsa-compat.js#readme",
"dependencies": { "trulyOptionalDependencies": {
"node-forge": "^0.7.6" "buffer-v6-polyfill": "^1.0.3",
}, "node-forge": "^0.7.6",
"optionalDependencies": {
"ursa-optional": "^0.9.10" "ursa-optional": "^0.9.10"
}, },
"trulyOptionalDependencies": { "dependencies": {
"buffer-v6-polyfill": "^1.0.3" "keypairs": "^1.2.14"
} }
} }