2018-11-22 11:05:00 +00:00
|
|
|
Rasha.js
|
|
|
|
=========
|
2018-11-20 08:02:36 +00:00
|
|
|
|
2018-11-22 11:05:00 +00:00
|
|
|
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)
|
2018-11-20 08:02:36 +00:00
|
|
|
|
2018-11-22 11:05:00 +00:00
|
|
|
RSA tools. Lightweight. Zero Dependencies. Universal compatibility.
|
2018-11-20 08:02:36 +00:00
|
|
|
|
2018-11-23 18:35:31 +00:00
|
|
|
| ~550 lines of code | 3kb gzipped | 10kb minified | 18kb with comments |
|
|
|
|
|
2018-11-22 11:05:00 +00:00
|
|
|
* [x] PEM-to-JWK
|
2018-11-23 06:41:39 +00:00
|
|
|
* [x] JWK-to-PEM
|
2018-11-22 11:05:00 +00:00
|
|
|
* [x] SSH "pub" format
|
|
|
|
|
2018-11-23 06:45:53 +00:00
|
|
|
This project is fully functional and tested (and the code is pretty clean).
|
2018-11-22 11:05:00 +00:00
|
|
|
|
2018-11-23 06:45:53 +00:00
|
|
|
It is considered to be complete, but if you find a bug please open an issue.
|
2018-11-22 11:05:00 +00:00
|
|
|
|
2018-11-23 19:44:06 +00:00
|
|
|
## Looking for ECDSA as well?
|
|
|
|
|
|
|
|
Check out [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js)
|
|
|
|
|
2018-11-22 11:05:00 +00:00
|
|
|
## PEM-to-JWK
|
|
|
|
|
2018-11-23 00:21:54 +00:00
|
|
|
* [x] PKCS#1 (traditional)
|
|
|
|
* [x] PKCS#8, SPKI/PKIX
|
2018-11-22 11:05:00 +00:00
|
|
|
* [x] 2048-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
|
|
|
|
|
2018-11-23 00:21:54 +00:00
|
|
|
* [x] PKCS#1 (traditional)
|
2018-11-23 06:41:39 +00:00
|
|
|
* [x] PKCS#8, SPKI/PKIX
|
2018-11-22 11:05:00 +00:00
|
|
|
* [x] 2048-bit, 4096-bit (and ostensibily all others)
|
|
|
|
* [x] SSH (RFC4716), (RFC 4716/SSH2)
|
|
|
|
|
|
|
|
```js
|
|
|
|
var Rasha = require('rasha');
|
2018-11-23 00:21:54 +00:00
|
|
|
var jwk = require('rasha/fixtures/privkey-rsa-2048.jwk.json');
|
2018-11-22 11:05:00 +00:00
|
|
|
|
|
|
|
Rasha.export({ jwk: jwk }).then(function (pem) {
|
|
|
|
// PEM in PKCS1 (traditional) format
|
|
|
|
console.log(pem);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
```
|
|
|
|
-----BEGIN RSA PRIVATE KEY-----
|
|
|
|
MIIEpAIBAAKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhD
|
|
|
|
NzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ
|
2018-11-23 06:41:39 +00:00
|
|
|
38P8LdAIlb0pqDHxEJ9adWomjuFf.....5cCBahfsiNtNR6WV1/iCSuINYs6uPdA
|
2018-11-22 11:05:00 +00:00
|
|
|
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
|
2018-11-23 06:41:39 +00:00
|
|
|
6HF5JijfWzK7haHFuRMEsgI4VwIY.....LorV1ovjwKBgAJR1m8dYKemfaW8P9YZ
|
2018-11-22 11:05:00 +00:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
```
|
2018-11-23 06:41:39 +00:00
|
|
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCba21UHE.....Q02P1Eamz/nT4I3 rsa@localhost
|
2018-11-22 11:05:00 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
`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);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
```
|
2018-11-23 06:41:39 +00:00
|
|
|
-----BEGIN PUBLIC KEY-----
|
2018-11-22 11:05:00 +00:00
|
|
|
MIIBCgKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJ
|
2018-11-23 06:41:39 +00:00
|
|
|
efLukC+xu0LBKylYojT5vTkxaOhx.....TmzCh2ikrwTMja7mUdBJf2bK3By5AB0
|
2018-11-22 11:05:00 +00:00
|
|
|
Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQAB
|
2018-11-23 06:41:39 +00:00
|
|
|
-----END PUBLIC KEY-----
|
2018-11-22 11:05:00 +00:00
|
|
|
```
|
|
|
|
|
2018-11-21 08:19:57 +00:00
|
|
|
Testing
|
|
|
|
-------
|
|
|
|
|
2018-11-23 06:45:53 +00:00
|
|
|
All cases are tested in `test.sh`.
|
2018-11-22 11:05:00 +00:00
|
|
|
|
|
|
|
You can compare these keys to the ones that you get from OpenSSL, ssh-keygen, and WebCrypto:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
# Generate 2048-bit RSA Keypair
|
2018-11-21 08:19:57 +00:00
|
|
|
openssl genrsa -out privkey-rsa-2048.pkcs1.pem 2048
|
2018-11-22 11:05:00 +00:00
|
|
|
|
|
|
|
# Convert PKCS1 (traditional) RSA Keypair to PKCS8 format
|
2018-11-21 08:19:57 +00:00
|
|
|
openssl rsa -in privkey-rsa-2048.pkcs1.pem -pubout -out pub-rsa-2048.spki.pem
|
2018-11-22 11:05:00 +00:00
|
|
|
|
|
|
|
# Export Public-only RSA Key in PKCS1 (traditional) format
|
2018-11-21 08:19:57 +00:00
|
|
|
openssl pkcs8 -topk8 -nocrypt -in privkey-rsa-2048.pkcs1.pem -out privkey-rsa-2048.pkcs8.pem
|
2018-11-22 11:05:00 +00:00
|
|
|
|
|
|
|
# Convert PKCS1 (traditional) RSA Public Key to SPKI/PKIX format
|
2018-11-21 08:19:57 +00:00
|
|
|
openssl rsa -in pub-rsa-2048.spki.pem -pubin -RSAPublicKey_out -out pub-rsa-2048.pkcs1.pem
|
2018-11-22 11:05:00 +00:00
|
|
|
|
|
|
|
# Convert RSA public key to SSH format
|
2018-11-21 08:19:57 +00:00
|
|
|
ssh-keygen -f ./pub-rsa-2048.spki.pem -i -mPKCS8 > ./pub-rsa-2048.ssh.pub
|
|
|
|
```
|
|
|
|
|
2018-11-22 11:05:00 +00:00
|
|
|
Goals of this project
|
|
|
|
-----
|
|
|
|
|
|
|
|
* Focused support for 2048-bit and 4096-bit RSA keypairs (although any size is technically supported)
|
2018-11-23 18:35:31 +00:00
|
|
|
* Zero Dependencies
|
|
|
|
* VanillaJS
|
|
|
|
* Quality Code: Good comments and tests
|
|
|
|
* Convert both ways: PEM-to-JWK and JWK-to-PEM (also supports SSH pub files)
|
2018-11-22 11:05:00 +00:00
|
|
|
* Browser support as well (TODO)
|
|
|
|
* OpenSSL, ssh-keygen, and WebCrypto compatibility
|
|
|
|
|
|
|
|
Legal
|
|
|
|
-----
|
|
|
|
|
|
|
|
Licensed MPL-2.0
|
2018-11-21 08:19:57 +00:00
|
|
|
|
2018-11-22 11:05:00 +00:00
|
|
|
[Terms of Use](https://therootcompany.com/legal/#terms) |
|
|
|
|
[Privacy Policy](https://therootcompany.com/legal/#privacy)
|