express server läuft jetzt mit https
This commit is contained in:
Lukas Nowy
2018-12-16 19:08:08 +01:00
parent 5589b0df3f
commit fd947bd852
475 changed files with 91128 additions and 0 deletions

375
express-server/node_modules/rsa-compat/LICENSE generated vendored Normal file
View File

@ -0,0 +1,375 @@
Copyright 2016-2018 AJ ONeal
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

279
express-server/node_modules/rsa-compat/README.md generated vendored Normal file
View File

@ -0,0 +1,279 @@
# rsa-compat.js
!["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")
!["Weekly Downloads"](https://img.shields.io/npm/dw/rsa-compat.svg "Weekly Download Count can't be shown")
| A [Root](https://therootcompany.com) Project.
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
[Greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js) **Let's Encrypt** clients
and is particularly suitable for building **certbot**-like clients.
(if you're looking for similar tools in the browser, consider [Bluecrypt](https://www.npmjs.com/search?q=bluecrypt))
# Install
node.js
```bash
npm install --save rsa-compat
```
### CLI
```bash
npm install --global rsa-compat
```
# Usage
CLI
---
You can generate keypairs on Windows, Mac, and Linux using rsa-keygen-js:
```bash
# generates a new keypair in the current directory
rsa-keypiar-js
```
Examples
--------
Generate an RSA Keypair:
```javascript
var RSA = require('rsa-compat').RSA;
var options = { bitlen: 2048, exp: 65537, public: true, pem: true, internal: true };
RSA.generateKeypair(options, function (err, keypair) {
console.log(keypair);
});
```
Here's what the object might look like:
`console.log(keypair)`:
```javascript
{ publicKeyPem: '-----BEGIN RSA PUBLIC KEY-----\n/*base64 pem-encoded string*/'
, privateKeyPem: '-----BEGIN RSA PRIVATE KEY-----\n/*base64 pem-encoded string*/'
, privateKeyJwk: {
kty: "RSA"
, n: '/*base64 modulus n = pq*/'
, e: '/*base64 exponent (usually 65537)*/'
, d: '/*base64 private exponent (d = e^1 (mod ϕ(n))/'
, p: '/*base64 first prime*/'
, q: '/*base64 second prime*/'
, dp: '/*base64 first exponent for Chinese remainder theorem (dP = d (mod p1))*/'
, dq: '/*base64 Second exponent, used for CRT (dQ = d (mod q1))/'
, qi: '/*base64 Coefficient, used for CRT (qinv = q^1 (mod p))*/'
}
, publicKeyJwk: {
kty: "RSA"
, n: '/*base64 modulus n = pq*/'
, 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.
# API Summary
* `RSA.generateKeypair(options, cb)`
* (deprecated `RSA.generateKeypair(bitlen, exp, options, cb)`)
* `RSA.import(options)`
* (deprecated `RSA.import(keypair, options)`)
* `RSA.exportPrivatePem(keypair)`
* `RSA.exportPublicPem(keypair)`
* `RSA.exportPrivateJwk(keypair)`
* `RSA.exportPublicJwk(keypair)`
* `RSA.signJws(keypair, header, protect, payload)`
* (deprecated `RSA.signJws(keypair, payload, nonce)`)
* `RSA.generateCsrPem(keypair, names)`
* `RSA.generateCsrDerWeb64(keypair, names)`
`keypair` can be any object with any of these keys `publicKeyPem, privateKeyPem, publicKeyJwk, privateKeyJwk`
### RSA.generateKeypair(options, cb)
Create a private keypair and export it as PEM, JWK, and/or internal formats
```javascript
RSA.generateKeypair(null, function (keypair) { /*...*/ });
RSA.generateKeypair({
bitlen: 2048, exp: 65537, pem: false, public: false, internal: false
}, function (keypair) { /*...*/ });
```
`options`:
```javascript
{ public: false // export public keys
, pem: false // export pems
, jwk: true // export jwks
, internal: false // preserve internal intermediate formats (_ursa, _forge)
, thumbprint: false // JWK sha256 thumbprint
, fingerprint: false // NOT IMPLEMENTED (RSA key fingerprint)
}
```
### RSA.import(options)
Imports keypair as JWKs and internal values `_ursa` and `_forge`.
```javascript
var keypair = RSA.import({ type: 'RSA', privateKeyPem: '...' });
console.log(keypair);
```
```javascript
{ privateKeyPem: ..., privateKeyJwk: ..., _ursa: ..., _forge: ... }
```
### RSA.export*(keypair)
You put in an object like `{ privateKeyPem: '...' }` or `{ publicKeyJwk: {} }`
and you get back the keys in the format you requested.
Note:
* Private keys **can** be used to export both private and public keys
* Public keys can **NOT** be used to generate private keys
Example:
```javascript
var keypair = { privateKeyPem: '...' };
keypair.publicKeyJwk = RSA.exportPublicJwk(keypair);
console.log(keypair);
```
### RSA.signJws(keypair, payload, nonce)
Generates a signature in JWS format (necessary for **certbot**/**letsencrypt**).
```javascript
var message = "Hello, World!"
var nonce = crypto.randomBytes(16).toString('hex');
var jws = RSA.signJws(keypair, message, nonce);
console.log(jws);
```
The result looks like this:
```javascript
{ "header": {
"alg": "RS256",
"jwk": {
"kty": "RSA",
"n": "AMJubTfOtAarnJytLE8fhNsEI8wnpjRvBXGK/Kp0675J10ORzxyMLqzIZF3tcrUkKBrtdc79u4X0GocDUgukpfkY+2UPUS/GxehUYbYrJYWOLkoJWzxn7wfoo9X1JgvBMY6wHQnTKvnzZdkom2FMhGxkLaEUGDSfsNznTTZNBBg9",
"e": "AQAB"
}
},
"protected": "eyJub25jZSI6IjhlZjU2MjRmNWVjOWQzZWYifQ",
"payload": "JLzF1NBNCV3kfbJ5sFaFyX94fJuL2H-IzaoBN-ciiHk",
"signature": "Wb2al5SDyh5gjmkV79MK9m3sfNBBPjntSKor-34BBoGwr6n8qEnBmqB1Y4zbo-5rmvsoPmJsnRlP_hRiUY86zSAQyfbisTGrGBl0IQ7ditpkfYVm0rBWJ8WnYNqYNp8K3qcD7NW72tsy-XoWEjNlz4lWJeRdEG2Nt4CJgnREH4Y"
}
```
### RSA.generateCsr*(keypair, names)
You can generate the CSR in human-readable or binary / base64 formats:
`RSA.generateCsrPem(keypair, names)`:
```javascript
var pem = RSA.generateCsrPem(keypair, [ 'example.com', 'www.example.com' ]);
console.log(pem);
```
web-safe base64 for **certbot**/**letsencrypt**:
`RSA.generateCsrDerWeb64(keypair, names)`:
```javascript
var web64 = RSA.generateCsrDerWeb64(keypair, [ 'example.com', 'www.example.com' ]);
console.log(web64);
```
# Old Node Versions
In recent versions of node >= v10.12 native RSA key generation is fairly quick for 2048-bit keys
(though it may still be too slow for some applications with 4096-bit keys).
In old versions, however, and especially on ARM and/or MIPS procesors, RSA key generation can be
very, very slow.
In old node versions `ursa` can provide faster key generation, but it must be compiled.
`ursa` will not compile for new node versions, but they already include the same openssl bindings anyawy.
```bash
npm install --save ursa
```
Also, if you need **Node < v6** support:
```bash
npm install --save buffer-v6-polyfill
```
## Security and Compatibility
**TL;DR**: Use the default values 2048 and 65537 unless you have a really, really good reason to do otherwise.
Various platforms *require* these values.
Most security experts agree that 4096-bit is no more "secure" than 2048-bit -
a fundamental vulnerability in the RSA algorithm which causes 2048 to be broken
will most likely also cause 4096 to be broken
(i.e. if someone can prove mathematically prove P=NP or a way to predict prime numbers).
Also, many platforms
only support 2048 bit keys due to the insecurity of 1024-bit keys (which are not 1/2 secure
but rather 1/(2^1028) less secure) and the excess computational
cost of 4096-bit keys (it's not a 2x increase, it's more like a 2^2048 increase).
As to why 65537 is even optional as a prime exponent or why it matters... no idea,
but it does matter.
# ChangeLog:
* v1.9.0
* consistently handle key generation across node crypto, ursa, and forge
* move all other operations to rasha.js and rsa-csr.js
* v1.4.0
* remove ursa as dependency (just causes confusion), but note in docs
* drop node < v6 support
# Legal
rsa-compat.js directly includes code from
[Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js)
and
[RSA-CSR.js](https://git.coolaj86.com/coolaj86/rsa-csr.js)
(also [Root](https://therootcompany.com) projects),
retrofitted for rsa-compat.
[rsa-compat.js](https://git.coolaj86.com/coolaj86/rsa-compat.js) |
MPL-2.0 |
[Terms of Use](https://therootcompany.com/legal/#terms) |
[Privacy Policy](https://therootcompany.com/legal/#privacy)

View File

@ -0,0 +1,35 @@
#!/usr/bin/env node
'use strict';
var RSA = require('../').RSA;
var path = require('path');
var fs = require('fs');
var bitlen = 2048;
var exp = 65537;
var opts = { public: true, pem: true };
var cwd = process.cwd();
var privkeyPath = path.join(cwd, 'privkey.pem');
var pubkeyPath = path.join(cwd, 'pubkey.pem');
if (fs.existsSync(privkeyPath)) {
console.error(privkeyPath, "already exists");
process.exit(1);
}
RSA.generateKeypair(bitlen, exp, opts, function (err, keypair) {
console.info('');
console.info('');
fs.writeFileSync(privkeyPath, keypair.privateKeyPem, 'ascii');
console.info(privkeyPath + ':');
console.info('');
console.info(keypair.privateKeyPem);
console.info('');
fs.writeFileSync(pubkeyPath, keypair.publicKeyPem, 'ascii');
console.info(pubkeyPath + ':');
console.info('');
console.info(keypair.publicKeyPem);
});

View File

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBjzCB+QIBADAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAwm5tN860BqucnK0sTx+E2wQjzCemNG8FcYr8qnTrvknX
Q5HPHIwurMhkXe1ytSQoGu11zv27hfQahwNSC6Sl+Rj7ZQ9RL8bF6FRhtislhY4u
SglbPGfvB+ij1fUmC8ExjrAdCdMq+fNl2SibYUyEbGQtoRQYNJ+w3OdNNk0EGD0C
AwEAAaA6MDgGCSqGSIb3DQEJDjErMCkwJwYDVR0RBCAwHoILZXhhbXBsZS5jb22C
D3d3dy5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOBgQCv1nj7zL0vnP11GEDb
TWlXPyT2XaFH9lYOm8By3BKrtk6rhGojlLppmNGaCEBqztebCY2T5LT6mi5lz0xc
YHJ9YC4w1yKz94//gmEMYJ+7fuILexnaeZudp9GjR3OeRWIWr/+3H0U3XnZpazGP
/qK4BCIl9HOOJbF5tyTd/CeeBg==
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1 @@
MIIBjzCB-QIBADAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwm5tN860BqucnK0sTx-E2wQjzCemNG8FcYr8qnTrvknXQ5HPHIwurMhkXe1ytSQoGu11zv27hfQahwNSC6Sl-Rj7ZQ9RL8bF6FRhtislhY4uSglbPGfvB-ij1fUmC8ExjrAdCdMq-fNl2SibYUyEbGQtoRQYNJ-w3OdNNk0EGD0CAwEAAaA6MDgGCSqGSIb3DQEJDjErMCkwJwYDVR0RBCAwHoILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOBgQCv1nj7zL0vnP11GEDbTWlXPyT2XaFH9lYOm8By3BKrtk6rhGojlLppmNGaCEBqztebCY2T5LT6mi5lz0xcYHJ9YC4w1yKz94__gmEMYJ-7fuILexnaeZudp9GjR3OeRWIWr_-3H0U3XnZpazGP_qK4BCIl9HOOJbF5tyTd_CeeBg

View File

@ -0,0 +1,11 @@
{
"kty": "RSA",
"n": "wm5tN860BqucnK0sTx-E2wQjzCemNG8FcYr8qnTrvknXQ5HPHIwurMhkXe1ytSQoGu11zv27hfQahwNSC6Sl-Rj7ZQ9RL8bF6FRhtislhY4uSglbPGfvB-ij1fUmC8ExjrAdCdMq-fNl2SibYUyEbGQtoRQYNJ-w3OdNNk0EGD0",
"e": "AQAB",
"d": "HT8DCrv69G3n9uFNovE4yMEMqW7lX0m75eJkMze3Jj5xNOa_4qlrc-4IuuA2uuyfY72IVQRxqqqXOuvS8ZForZZk-kWSd6z45hrpbNAAHH2Rf7XwnwHY8VJrOQF3UtbktTWqHX36ITZb9Hmf18hWsIeEp8Ng7Ru9h7hNuVxKMjk",
"p": "42M69lUC-EIzYmcsZYkbf7kGhqvcwHk_h7N8TEOa7IYRP_DQL49LrSNuMHxOK9CxJ0RwajsY5o6WYBfoRC0B5w",
"q": "2uWWAmzLitMx9reZDnSQwhw1qGIQ55quEBwmBBQIe46O4SeFT0V8TED-bkVeOYQVCFHCS6GTRBoipMZtTPEYOw",
"dp": "u7Ec6lghq2p5n7AqJWWXHUZM7LzP6tAqcIjnAMyNBM9lTbIpJhjSDohAXCU_IUuR7ye-4vEFDMqFtawGPMAp4Q",
"dq": "XLhDAmPzE6rBzy-VtXnKl247jEd9wZzTfh9uOuwBa9TG0Lhcz2cvb11YaH0ZnGNGRW_cTQzzxDUN1531TlIRYQ",
"qi": "jZqnPoQJ8bCGy8hxTf7IW37cIDvwJRWxfg1S6XmbcKWzab7kxsZAbkrSEanGMMLc6ZdOrVjmCd6nnJRm9U9kjg"
}

View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDCbm03zrQGq5ycrSxPH4TbBCPMJ6Y0bwVxivyqdOu+SddDkc8c
jC6syGRd7XK1JCga7XXO/buF9BqHA1ILpKX5GPtlD1EvxsXoVGG2KyWFji5KCVs8
Z+8H6KPV9SYLwTGOsB0J0yr582XZKJthTIRsZC2hFBg0n7Dc5002TQQYPQIDAQAB
AoGAHT8DCrv69G3n9uFNovE4yMEMqW7lX0m75eJkMze3Jj5xNOa/4qlrc+4IuuA2
uuyfY72IVQRxqqqXOuvS8ZForZZk+kWSd6z45hrpbNAAHH2Rf7XwnwHY8VJrOQF3
UtbktTWqHX36ITZb9Hmf18hWsIeEp8Ng7Ru9h7hNuVxKMjkCQQDjYzr2VQL4QjNi
ZyxliRt/uQaGq9zAeT+Hs3xMQ5rshhE/8NAvj0utI24wfE4r0LEnRHBqOxjmjpZg
F+hELQHnAkEA2uWWAmzLitMx9reZDnSQwhw1qGIQ55quEBwmBBQIe46O4SeFT0V8
TED+bkVeOYQVCFHCS6GTRBoipMZtTPEYOwJBALuxHOpYIatqeZ+wKiVllx1GTOy8
z+rQKnCI5wDMjQTPZU2yKSYY0g6IQFwlPyFLke8nvuLxBQzKhbWsBjzAKeECQFy4
QwJj8xOqwc8vlbV5ypduO4xHfcGc034fbjrsAWvUxtC4XM9nL29dWGh9GZxjRkVv
3E0M88Q1Dded9U5SEWECQQCNmqc+hAnxsIbLyHFN/shbftwgO/AlFbF+DVLpeZtw
pbNpvuTGxkBuStIRqcYwwtzpl06tWOYJ3qeclGb1T2SO
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,16 @@
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMJubTfOtAarnJyt
LE8fhNsEI8wnpjRvBXGK/Kp0675J10ORzxyMLqzIZF3tcrUkKBrtdc79u4X0GocD
UgukpfkY+2UPUS/GxehUYbYrJYWOLkoJWzxn7wfoo9X1JgvBMY6wHQnTKvnzZdko
m2FMhGxkLaEUGDSfsNznTTZNBBg9AgMBAAECgYAdPwMKu/r0bef24U2i8TjIwQyp
buVfSbvl4mQzN7cmPnE05r/iqWtz7gi64Da67J9jvYhVBHGqqpc669LxkWitlmT6
RZJ3rPjmGuls0AAcfZF/tfCfAdjxUms5AXdS1uS1NaodffohNlv0eZ/XyFawh4Sn
w2DtG72HuE25XEoyOQJBAONjOvZVAvhCM2JnLGWJG3+5Boar3MB5P4ezfExDmuyG
ET/w0C+PS60jbjB8TivQsSdEcGo7GOaOlmAX6EQtAecCQQDa5ZYCbMuK0zH2t5kO
dJDCHDWoYhDnmq4QHCYEFAh7jo7hJ4VPRXxMQP5uRV45hBUIUcJLoZNEGiKkxm1M
8Rg7AkEAu7Ec6lghq2p5n7AqJWWXHUZM7LzP6tAqcIjnAMyNBM9lTbIpJhjSDohA
XCU/IUuR7ye+4vEFDMqFtawGPMAp4QJAXLhDAmPzE6rBzy+VtXnKl247jEd9wZzT
fh9uOuwBa9TG0Lhcz2cvb11YaH0ZnGNGRW/cTQzzxDUN1531TlIRYQJBAI2apz6E
CfGwhsvIcU3+yFt+3CA78CUVsX4NUul5m3Cls2m+5MbGQG5K0hGpxjDC3OmXTq1Y
5gnep5yUZvVPZI4=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,13 @@
{
"header": {
"alg": "RS256",
"jwk": {
"kty": "RSA",
"n": "AMJubTfOtAarnJytLE8fhNsEI8wnpjRvBXGK_Kp0675J10ORzxyMLqzIZF3tcrUkKBrtdc79u4X0GocDUgukpfkY-2UPUS_GxehUYbYrJYWOLkoJWzxn7wfoo9X1JgvBMY6wHQnTKvnzZdkom2FMhGxkLaEUGDSfsNznTTZNBBg9",
"e": "AQAB"
}
},
"protected": "eyJub25jZSI6IjhlZjU2MjRmNWVjOWQzZWYifQ",
"payload": "JLzF1NBNCV3kfbJ5sFaFyX94fJuL2H-IzaoBN-ciiHk",
"signature": "Wb2al5SDyh5gjmkV79MK9m3sfNBBPjntSKor-34BBoGwr6n8qEnBmqB1Y4zbo-5rmvsoPmJsnRlP_hRiUY86zSAQyfbisTGrGBl0IQ7ditpkfYVm0rBWJ8WnYNqYNp8K3qcD7NW72tsy-XoWEjNlz4lWJeRdEG2Nt4CJgnREH4Y"
}

7
express-server/node_modules/rsa-compat/index.js generated vendored Normal file
View File

@ -0,0 +1,7 @@
// Copyright 2016-2018 AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
module.exports.RSA = require('./lib/rsa.js');

View File

@ -0,0 +1,57 @@
// Copyright 2016-2018 AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
var Rasha = require('./rasha');
module.exports = function (bitlen, exp) {
var k = require('node-forge').pki.rsa
.generateKeyPair({ bits: bitlen || 2048, e: exp || 0x10001 }).privateKey;
var jwk = {
kty: "RSA"
, n: _toUrlBase64(k.n)
, e: _toUrlBase64(k.e)
, d: _toUrlBase64(k.d)
, p: _toUrlBase64(k.p)
, q: _toUrlBase64(k.q)
, dp: _toUrlBase64(k.dP)
, dq: _toUrlBase64(k.dQ)
, qi: _toUrlBase64(k.qInv)
};
return {
publicKeyPem: Rasha.exportSync({ jwk: jwk, public: true })
, privateKeyPem: Rasha.exportSync({ jwk: jwk })
, privateKeyJwk: jwk
, publicKeyJwk: {
kty: jwk.kty
, n: jwk.n
, e: jwk.e
}
};
};
function _toUrlBase64(fbn) {
var hex = fbn.toRadix(16);
if (hex.length % 2) {
// Invalid hex string
hex = '0' + hex;
}
while ('00' === hex.slice(0, 2)) {
hex = hex.slice(2);
}
return Buffer.from(hex, 'hex').toString('base64')
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g,"")
;
}
if (require.main === module) {
var keypair = module.exports(2048, 0x10001);
console.info(keypair.privateKeyPem);
console.warn(keypair.publicKeyPem);
//console.info(keypair.privateKeyJwk);
//console.warn(keypair.publicKeyJwk);
}

View File

@ -0,0 +1,39 @@
// Copyright 2016-2018 AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
var Rasha = require('./rasha');
module.exports = function (bitlen, exp) {
var keypair = require('crypto').generateKeyPairSync(
'rsa'
, { modulusLength: bitlen
, publicExponent: exp
, privateKeyEncoding: {
type: 'pkcs1'
, format: 'pem'
}
, publicKeyEncoding: {
type: 'pkcs1'
, format: 'pem'
}
}
);
var result = {
publicKeyPem: keypair.publicKey.trim()
, privateKeyPem: keypair.privateKey.trim()
};
result.publicKeyJwk = Rasha.importSync({ pem: result.publicKeyPem, public: true });
result.privateKeyJwk = Rasha.importSync({ pem: result.privateKeyPem });
return result;
};
if (require.main === module) {
var keypair = module.exports(2048, 0x10001);
console.info(keypair.privateKeyPem);
console.warn(keypair.publicKeyPem);
//console.info(keypair.privateKeyJwk);
//console.warn(keypair.publicKeyJwk);
}

View File

@ -0,0 +1,32 @@
// Copyright 2016-2018 AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
var Rasha = require('./rasha');
module.exports = function (bitlen, exp) {
var ursa;
try {
ursa = require('ursa');
} catch(e) {
ursa = require('ursa-optional');
}
var keypair = ursa.generatePrivateKey(bitlen, exp);
var result = {
publicKeyPem: keypair.toPublicPem().toString('ascii').trim()
, privateKeyPem: keypair.toPrivatePem().toString('ascii').trim()
};
result.publicKeyJwk = Rasha.importSync({ pem: result.publicKeyPem, public: true });
result.privateKeyJwk = Rasha.importSync({ pem: result.privateKeyPem });
return result;
};
if (require.main === module) {
var keypair = module.exports(2048, 0x10001);
console.info(keypair.privateKeyPem);
console.warn(keypair.publicKeyPem);
//console.info(keypair.privateKeyJwk);
//console.warn(keypair.publicKeyJwk);
}

View File

@ -0,0 +1,66 @@
// Copyright 2016-2018 AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
var oldver = false;
module.exports = function (bitlen, exp) {
bitlen = parseInt(bitlen, 10) || 2048;
exp = parseInt(exp, 10) || 65537;
try {
return require('./generate-privkey-node.js')(bitlen, exp);
} catch(e) {
if (!/generateKeyPairSync is not a function/.test(e.message)) {
throw e;
}
try {
return require('./generate-privkey-ursa.js')(bitlen, exp);
} catch(e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
if (!oldver) {
oldver = true;
console.warn("[WARN] rsa-compat: Your version of node does not have crypto.generateKeyPair()");
console.warn("[WARN] rsa-compat: Please update to node >= v10.12 or 'npm install ursa'");
console.warn("[WARN] rsa-compat: Using node-forge as a fallback, but it may be unacceptably slow.");
if (/arm|mips/i.test(require('os').arch)) {
console.warn("================================================================");
console.warn(" WARNING");
console.warn("================================================================");
console.warn("");
console.warn("WARNING: You are generating an RSA key using pure JavaScript on");
console.warn(" a VERY SLOW cpu. This could take DOZENS of minutes!");
console.warn("");
console.warn(" We recommend installing node >= v10.12, or 'gcc' and 'ursa'");
console.warn("");
console.warn("EXAMPLE:");
console.warn("");
console.warn(" sudo apt-get install build-essential && npm install ursa");
console.warn("");
console.warn("================================================================");
}
}
try {
return require('./generate-privkey-forge.js')(bitlen, exp);
} catch(e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
console.error("[ERROR] rsa-compat: could not generate a private key.");
console.error("None of crypto.generateKeyPair, ursa, nor node-forge are present");
}
}
}
};
if (require.main === module) {
var keypair = module.exports(2048, 0x10001);
console.info(keypair.privateKeyPem);
console.warn(keypair.publicKeyPem);
//console.info(keypair.privateKeyJwk);
//console.warn(keypair.publicKeyJwk);
}

View File

@ -0,0 +1,218 @@
[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

@ -0,0 +1,97 @@
#!/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

@ -0,0 +1,11 @@
{
"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

@ -0,0 +1,27 @@
-----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

@ -0,0 +1,28 @@
-----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

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

View File

@ -0,0 +1,8 @@
-----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

@ -0,0 +1,9 @@
-----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

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

View File

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

View File

@ -0,0 +1,235 @@
'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

@ -0,0 +1,104 @@
'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

@ -0,0 +1,28 @@
'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

@ -0,0 +1,204 @@
'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

@ -0,0 +1,80 @@
'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

@ -0,0 +1,111 @@
'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

@ -0,0 +1,153 @@
'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

@ -0,0 +1,214 @@
[RSA-CSR.js](https://git.coolaj86.com/coolaj86/rsa-csr.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-express.js)
A focused, **zero-dependency** library that can do exactly one thing really, really well:
* Generate a Certificate Signing Requests (CSR), and sign it!
| < 300 lines of code | 1.7k gzipped | 4.7k minified | 8.5k with comments |
Need JWK-to-PEM? Try [Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js)
Need to generate an EC CSR? Try [ECSDA-CSR.js](https://git.coolaj86.com/coolaj86/ecdsa-csr.js)
Features
========
* [x] Universal CSR support (RSA signing) that Just Works&trade;
* Common Name (CN) Subject
* Subject Alternative Names (SANs / altnames)
* 2048, 3072, and 4096 bit JWK RSA
* RSASSA PKCS1 v1.5
* [x] Zero Dependencies
* (no ASN1.js, PKI.js, forge, jrsasign - not even elliptic.js!)
* [x] Quality
* Focused
* Lightweight
* Well-Commented, Well-Documented
* Secure
* [x] Vanilla Node.js
* no school like the old school
* easy to read and understand
Usage
-----
Given an array of domains it uses the first for the Common Name (CN),
also known as Subject, and all of them as the Subject Alternative Names (SANs or altnames).
```js
'use strict';
var rsacsr = require('rsa-csr');
var key = {
"kty": "RSA",
"n": "m2tt...-CNw",
"e": "AQAB",
"d": "Cpfo...HMQQ",
"p": "ynG-...sTCE",
"q": "xIkA...1Q1c",
"dp": "tzDG...B1QE",
"dq": "kh5d...aL48",
"qi": "AlHW...HhFU"
};
var domains = [ 'example.com', 'www.example.com' ];
return rsacsr({ key: key, domains: domains }).then(function (csr) {
console.log('CSR PEM:');
console.log(csr);
});
```
The output will look something like this (but much longer):
```
-----BEGIN CERTIFICATE REQUEST-----
MIIClTCCAX0CAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCba21UHE+VbDTpmYYFZUOV+OQ8AngOCdjROsPC
0KiEfMvEaEM3NQl58u6QL7G7QsEr.....3pIpUUkx5WbwJY6xDrCyFKG8ktpnee6
WjpTOBnpgHUI1/5ydnf0v29L9N+ALIJGKQxhub3iqB6EhCl93iiQtf4e7M/lzX7l
c1xqsSwVZ3RQVY9bRP9NdGuW4hVvscy5ypqRtXPXQpxMnYwfi9qW5Uo=
-----END CERTIFICATE REQUEST-----
```
#### PEM-to-JWK
If you need to convert a PEM to JWK first, do so:
```js
var Rasha = require('rasha');
Rasha.import({ pem: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAI..." }).then(function (jwk) {
console.log(jwk);
})
```
#### CLI
You're probably better off using OpenSSL for most commandline tasks,
but the `rsa-csr` and `rasha` CLIs are useful for testing and debugging.
```bash
npm install -g rsa-csr
npm install -g rasha
rasha ./privkey.pem > ./privkey.jwk.json
rsa-csr ./privkey.jwk.json example.com,www.example.com > csr.pem
```
### Options
* `key` should be a JWK
* Need PEM support? Use [Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js).
* (supports PEM, DER, PKCS#1 and PKCS#8)
* `domains` must be a list of strings representing domain names
* correctly handles utf-8
* you may also use punycoded, if needed
* `subject` will be `domains[0]` by default
* you shouldn't use this unless you need to
* you may need to if you need utf-8 for domains, but punycode for the subject
### Testing
You can double check that the CSR you get out is actually valid:
```bash
# Generate a key, if needed
openssl genrsa -out ./privkey-rsa.pkcs1.pem $keysize
# Convert to JWK
rasha ./privkey-rsa.pkcs1.pem > ./privkey-rsa.jwk.json
# Create a CSR with your domains
npx rsa-csr ./privkey-rsa.jwk.json example.com,www.example.com > csr.pem
# Verify
openssl req -text -noout -verify -in csr.pem
```
New to Crypto?
--------------
Just a heads up in case you have no idea what you're doing:
First of all, [don't panic](https://coolaj86.com/articles/dont-panic.html).
Next:
* RSA stands for... well, that doesn't matter, actually.
* DSA stands for _Digital Signing Algorithm_.
* RSA a separate standard from EC/ECDSA, but both are *asymmetric*
* Private keys are actually keypairs (they contain the public key)
In many cases the terms get used (and misused) interchangably,
which can be confusing. You'll survive, I promise.
* PEM is just a Base64-encoded DER (think JSON as hex or base64)
* DER is an binary _object notation_ for ASN.1 (think actual stringified JSON or XML)
* ASN.1 is _object notation_ standard (think JSON, the standard)
* X.509 is a suite of schemas (think XLST or json-schema.org)
* PKCS#8, PKIK, SPKI are all X.509 schemas (think defining `firstName` vs `first_name` vs `firstname`)
Now forget about all that and just know this:
**This library solves your problem if** you need RSA _something-or-other_ and CSR _something-or-other_
in order to deal with SSL certificates in an internal organization.
If that's not what you're doing, you may want HTTPS and SSL through
[Greenlock.js](https://git.coolaj86.com/coolaj86/greenlock-express.js),
or you may be looking for something else entirely.
Goals vs Non-Goals
-----
This was built for use by [ACME.js](https://git.coolaj86.com/coolaj86/acme.js)
and [Greenlock.js](https://git.coolaj86.com/coolaj86/greenlock-express.js).
Rather than trying to make a generic implementation that works with everything under the sun,
this library is intentionally focused on around the use case of generating certificates for
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
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)
what it needs to do - No more, no less.
* Support RSA JWKs
* 2048-bit
* 3072-bit
* 4096-bit
* Support PEM and DER via Rasha.js
* PKCS#1 (traditional)
* PKCS#8
* RSASSA-PKCS1-v1_5
* Vanilla node.js (ECMAScript 5.1)
* No babel
* No dependencies
However, there are a few areas where I'd be willing to stretch:
* Type definition files for altscript languages
It is not a goal of this project to support any RSA profiles
except those that are universally supported by browsers and
are sufficiently secure (overkill is overkill).
> A little copying is better than a little dependency. - [Go Proverbs](https://go-proverbs.github.io) by Rob Pike
This code is considered small and focused enough that,
rather than making it a dependency in other small projects,
I personally just copy over the code.
Hence, all of these projects are MPL-2.0 licensed.
Legal
-----
[RSA-CSR.js](https://git.coolaj86.com/coolaj86/rsa-csr.js) |
MPL-2.0 |
[Terms of Use](https://therootcompany.com/legal/#terms) |
[Privacy Policy](https://therootcompany.com/legal/#privacy)

View File

@ -0,0 +1,23 @@
#!/usr/bin/env node
'use strict';
var fs = require('fs');
var rsacsr = require('../index.js');
var keyname = process.argv[2];
var domains = process.argv[3].split(/,/);
var key = fs.readFileSync(keyname, 'ascii');
try {
key = JSON.parse(key);
} catch(e) {
// ignore
}
rsacsr({ key: key, domains: domains }).then(function (csr) {
// Using error so that we can redirect stdout to file
//console.error("CN=" + domains[0]);
//console.error("subjectAltName=" + domains.join(','));
console.log(csr);
});

View File

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIClTCCAX0CAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCba21UHE+VbDTpmYYFZUOV+OQ8AngOCdjROsPC
0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo6HF5JijfWzK7haHFuRMEsgI4
VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/RJSE3Vw/uz2bxiT22uSkSqOyS
hyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZtYeHLrsFeSTjLL9jMPjI4ZkV
dlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYNIP4ttfl5ObMKHaKSvBMyNruZ
R0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Eamz/nT4I3AgMBAAGgOjA4Bgkq
hkiG9w0BCQ4xKzApMCcGA1UdEQQgMB6CC2V4YW1wbGUuY29tgg93d3cuZXhhbXBs
ZS5jb20wDQYJKoZIhvcNAQELBQADggEBAGOFydCZPtRqnEidrB3vkpPp1GmQVqrl
XhqVM3X7UppsD3QDtJfgoSuBuVy3X/mPvy/Ly8WqEwJ7Ur+h76e7rgeFLvN5fQIr
frlpfCXrKaxxl6vxqKJ8s3Mn2LT8VmSVNig34NCtn99/SHNAlr3aU9L3b1+R25VI
FwFbOz/i92gOchT7Xat3fOiQ02k9GrHT33pIpUUkx5WbwJY6xDrCyFKG8ktpnee6
WjpTOBnpgHUI1/5ydnf0v29L9N+ALIJGKQxhub3iqB6EhCl93iiQtf4e7M/lzX7l
c1xqsSwVZ3RQVY9bRP9NdGuW4hVvscy5ypqRtXPXQpxMnYwfi9qW5Uo=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,11 @@
{
"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

@ -0,0 +1,27 @@
-----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

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICqjCCAZICAQAwFzEVMBMGA1UEAwwMd2hhdGV2ZXIubmV0MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrD
wtCohHzLxGhDNzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLIC
OFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjs
kocn2BOnTB57qAZM6+I70on0/iDZm7+jcqOPgADAmbWHhy67BXkk4yy/YzD4yOGZ
FXZcNp915/TW5bRd//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5eTmzCh2ikrwTMja7
mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQABoE4wTAYJ
KoZIhvcNAQkOMT8wPTA7BgNVHREENDAyggx3aGF0ZXZlci5uZXSCEHd3dy53aGF0
ZXZlci5uZXSCEGFwaS53aGF0ZXZlci5uZXQwDQYJKoZIhvcNAQELBQADggEBAB21
KZYjarfd8nUAbwhH8dWZOo4rFcdYFo3xcXPQ11b1Wa79dtG67cgD/dplKFis5qD3
6h4m818w9ESBA3Q1ZUy6HgDPMhCjg2fmCnSsZ5epo47wzvelYonfOX5DAwxgfYsa
335olrXJ0qsTiNmaS7RxDT53vfMOp41NyEAkFmpIAkaHgW/+xFPUSCBXIUWbaCG+
pK3FVNmK3VCVCAP6UvVKYQUWSC6FRG/Q8MHoecdo+bbMlr2s2GPxq9TKInwe8JqT
E9pD7QMsN7uWpMaXNKCje4+Q88Br4URNcGAiYoy4/6hcF2Ki1saTYVIk/DG1P4hX
G5f0ezDLtsC22xe6jHI=
-----END CERTIFICATE REQUEST-----

View File

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

View File

@ -0,0 +1,72 @@
'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)) {
console.error(arguments);
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);
};

View File

@ -0,0 +1,152 @@
'use strict';
var crypto = require('crypto');
var ASN1 = require('./asn1.js');
var Enc = require('./encoding.js');
var PEM = require('./pem.js');
var X509 = require('./x509.js');
var RSA = {};
/*global Promise*/
var CSR = module.exports = function rsacsr(opts) {
// We're using a Promise here to be compatible with the browser version
// which will probably use the webcrypto API for some of the conversions
opts = CSR._prepare(opts);
return CSR.create(opts).then(function (bytes) {
return CSR._encode(opts, bytes);
});
};
CSR._prepare = function (opts) {
var Rasha;
opts = JSON.parse(JSON.stringify(opts));
var pem, jwk;
// We do a bit of extra error checking for user convenience
if (!opts) { throw new Error("You must pass options with key and domains to rsacsr"); }
if (!Array.isArray(opts.domains) || 0 === opts.domains.length) {
new Error("You must pass options.domains as a non-empty array");
}
// I need to check that 例.中国 is a valid domain name
if (!opts.domains.every(function (d) {
// allow punycode? xn--
if ('string' === typeof d /*&& /\./.test(d) && !/--/.test(d)*/) {
return true;
}
})) {
throw new Error("You must pass options.domains as strings");
}
if (opts.pem) {
pem = opts.pem;
} else if (opts.jwk) {
jwk = opts.jwk;
} else {
if (!opts.key) {
throw new Error("You must pass options.key as a JSON web key");
} else if (opts.key.kty) {
jwk = opts.key;
} else {
pem = opts.key;
}
}
if (pem) {
try {
Rasha = require('rasha');
} catch(e) {
throw new Error("Rasha.js is an optional dependency for PEM-to-JWK.\n"
+ "Install it if you'd like to use it:\n"
+ "\tnpm install --save rasha\n"
+ "Otherwise supply a jwk as the private key."
);
}
jwk = Rasha.importSync({ pem: pem });
}
opts.jwk = jwk;
return opts;
};
CSR.sync = function (opts) {
opts = CSR._prepare(opts);
var bytes = CSR.createSync(opts);
return CSR._encode(opts, bytes);
};
CSR._encode = function (opts, bytes) {
if ('der' === (opts.encoding||'').toLowerCase()) {
return bytes;
}
return PEM.packBlock({
type: "CERTIFICATE REQUEST"
, bytes: bytes /* { jwk: jwk, domains: opts.domains } */
});
};
CSR.createSync = function createCsr(opts) {
var hex = CSR.request(opts.jwk, opts.domains);
var csr = CSR.signSync(opts.jwk, hex);
return Enc.hexToBuf(csr);
};
CSR.create = function createCsr(opts) {
var hex = CSR.request(opts.jwk, opts.domains);
return CSR.sign(opts.jwk, hex).then(function (csr) {
return Enc.hexToBuf(csr);
});
};
CSR.request = function createCsrBodyEc(jwk, domains) {
var asn1pub = X509.packCsrPublicKey(jwk);
return X509.packCsr(asn1pub, domains);
};
CSR.signSync = function csrEcSig(jwk, request) {
var keypem = PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: X509.packPkcs1(jwk) });
var sig = RSA.signSync(keypem, Enc.hexToBuf(request));
return CSR.toDer({ request: request, signature: sig });
};
CSR.sign = function csrEcSig(jwk, request) {
var keypem = PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: X509.packPkcs1(jwk) });
return RSA.sign(keypem, Enc.hexToBuf(request)).then(function (sig) {
return CSR.toDer({ request: request, signature: sig });
});
};
CSR.toDer = function encode(opts) {
var sty = ASN1('30'
// 1.2.840.113549.1.1.11 sha256WithRSAEncryption (PKCS #1)
, ASN1('06', '2a864886f70d01010b')
, ASN1('05')
);
return ASN1('30'
// The Full CSR Request Body
, opts.request
// The Signature Type
, sty
// The Signature
, ASN1.BitStr(Enc.bufToHex(opts.signature))
);
};
//
// RSA
//
// Took some tips from https://gist.github.com/codermapuche/da4f96cdb6d5ff53b7ebc156ec46a10a
RSA.signSync = function signRsaSync(keypem, ab) {
// Signer is a stream
var sign = crypto.createSign('SHA256');
sign.write(new Uint8Array(ab));
sign.end();
// The signature is ASN1 encoded, as it turns out
var sig = sign.sign(keypem);
// Convert to a JavaScript ArrayBuffer just because
return new Uint8Array(sig.buffer.slice(sig.byteOffset, sig.byteOffset + sig.byteLength));
};
RSA.sign = function signRsa(keypem, ab) {
return Promise.resolve().then(function () {
return RSA.signSync(keypem, ab);
});
};

View File

@ -0,0 +1,34 @@
'use strict';
var Enc = module.exports;
Enc.base64ToHex = function base64ToHex(b64) {
return Buffer.from(b64, 'base64').toString('hex').toLowerCase();
};
Enc.bufToBase64 = function bufToBase64(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.bufToHex = function toHex(u8) {
return Buffer.from(u8).toString('hex').toLowerCase();
};
Enc.hexToBuf = function (hex) {
return Buffer.from(hex, 'hex');
};
Enc.numToHex = function numToHex(d) {
d = d.toString(16);
if (d.length % 2) {
return '0' + d;
}
return d;
};
Enc.utf8ToHex = function utf8ToHex(str) {
// node can properly handle utf-8 strings
return Buffer.from(str).toString('hex').toLowerCase();
};

View File

@ -0,0 +1,12 @@
'use strict';
var Enc = require('./encoding.js');
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 + '-----'
;
};

View File

@ -0,0 +1,111 @@
'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

@ -0,0 +1,66 @@
'use strict';
var ASN1 = require('./asn1.js');
var Enc = require('./encoding.js');
var X509 = module.exports;
X509.packCsr = function (asn1pubkey, domains) {
return ASN1('30'
// Version (0)
, ASN1.UInt('00')
// 2.5.4.3 commonName (X.520 DN component)
, ASN1('30', ASN1('31', ASN1('30', ASN1('06', '550403'), ASN1('0c', Enc.utf8ToHex(domains[0])))))
// Public Key (RSA or EC)
, asn1pubkey
// Request Body
, ASN1('a0'
, ASN1('30'
// 1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)
, ASN1('06', '2a864886f70d01090e')
, ASN1('31'
, ASN1('30'
, ASN1('30'
// 2.5.29.17 subjectAltName (X.509 extension)
, ASN1('06', '551d11')
, ASN1('04'
, ASN1('30', domains.map(function (d) {
return ASN1('82', Enc.utf8ToHex(d));
}).join(''))))))))
);
};
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.packCsrPublicKey = function (jwk) {
// Sequence the key
var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
var asn1pub = ASN1('30', n, e);
//var asn1pub = X509.packPkcs1({ kty: jwk.kty, n: jwk.n, e: jwk.e });
// Add the CSR pub key header
return ASN1('30', ASN1('30', ASN1('06', '2a864886f70d010101'), ASN1('05')), ASN1.BitStr(asn1pub));
};

213
express-server/node_modules/rsa-compat/lib/rsa.js generated vendored Normal file
View File

@ -0,0 +1,213 @@
// Copyright 2016-2018 AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
var RSA = module.exports;
RSA.utils = {};
try {
require('buffer-v6-polyfill');
} catch(e) {
/* ignore */
}
var Rasha = require('./rasha');
var RSACSR = require('./rsa-csr');
var NOBJ = {};
var DEFAULT_BITLEN = 2048;
var DEFAULT_EXPONENT = 0x10001;
RSA.generateKeypair = function (options, cb, extra1, extra2) {
var length;
var exponent;
if ('function' === typeof extra2) {
length = options || DEFAULT_BITLEN;
exponent = cb || DEFAULT_EXPONENT;
options = extra1 || NOBJ;
cb = extra2;
} else {
if (!options) { options = NOBJ; }
length = options.bitlen || DEFAULT_BITLEN;
exponent = options.exp || DEFAULT_EXPONENT;
}
try {
var keypair = require('./generate-privkey.js')(length, exponent);
keypair.thumbprint = RSA.thumbprint(keypair);
cb(null, keypair);
} catch(e) {
cb(e);
}
};
RSA.import = function (options) {
options = JSON.parse(JSON.stringify(options));
// Private Keys
if (options.privateKeyPem) {
if (!options.privateKeyJwk) {
options.privateKeyJwk = Rasha.importSync({ pem: options.privateKeyPem });
}
}
if (options.privateKeyJwk) {
if (!options.privateKeyPem) {
options.privateKeyPem = Rasha.exportSync({
jwk: options.privateKeyJwk
, format: options.format || 'pkcs1'
, encoding: options.encoding || 'pem'
});
}
}
// Public Keys
if (options.publicKeyPem || options.privateKeyPem) {
if (!options.publicKeyJwk) {
options.publicKeyJwk = Rasha.importSync({
pem: options.publicKeyPem || options.privateKeyPem
, public: true
});
}
}
if (options.publicKeyJwk || options.privateKeyJwk) {
if (!options.publicKeyPem) {
options.publicKeyPem = Rasha.exportSync({
jwk: options.publicKeyJwk || options.privateKeyJwk
, format: options.format || 'pkcs1'
, encoding: options.encoding || 'pem'
, public: true
});
}
}
if (!options.publicKeyPem) {
throw new Error("Error: no keys were present to import");
}
// Consistent CRLF
if (options.privateKeyPem) {
options.privateKeyPem = options.privateKeyPem
.trim().replace(/[\r\n]+/g, '\r\n') + '\r\n';
}
options.publicKeyPem = options.publicKeyPem
.trim().replace(/[\r\n]+/g, '\r\n') + '\r\n';
// Thumbprint
if (!options.thumbprint) {
options.thumbprint = RSA._thumbprint(options);
}
return options;
};
RSA.exportPrivatePem = function (keypair) {
keypair = RSA.import(keypair);
return keypair.privateKeyPem;
};
RSA.exportPublicPem = function (keypair) {
keypair = RSA.import(keypair);
return keypair.publicKeyPem;
};
RSA.exportPrivateJwk = function (keypair) {
keypair = RSA.import(keypair);
return keypair.privateKeyJwk;
};
RSA.exportPublicJwk = function (keypair) {
if (!keypair.publicKeyJwk) {
keypair = RSA.import(keypair);
}
return keypair.publicKeyJwk;
};
RSA.signJws = RSA.generateJws = RSA.generateSignatureJws = RSA.generateSignatureJwk =
function (keypair, header, protect, payload) {
// old (keypair, payload, nonce)
var nonce;
keypair = RSA.import(keypair);
keypair.publicKeyJwk = RSA.exportPublicJwk(keypair);
if ('string' === typeof protect || ('undefined' === typeof protect && 'undefined' === typeof payload)) {
console.warn("deprecation notice: new signature for signJws(keypair, header, protect, payload)");
// old API
payload = header;
nonce = protect;
protect = undefined;
header = {
alg: "RS256"
, jwk: keypair.publicKeyJwk
};
protect = { nonce: nonce };
}
// Compute JWS signature
var protectedHeader = "";
if (protect) {
protectedHeader = JSON.stringify(protect); // { alg: prot.alg, nonce: prot.nonce, url: prot.url });
}
var protected64 = RSA.utils.toWebsafeBase64(Buffer.from(protectedHeader).toString('base64'));
var payload64 = RSA.utils.toWebsafeBase64(payload.toString('base64'));
var raw = protected64 + "." + payload64;
var pem = RSA.exportPrivatePem(keypair);
var signer = require('crypto').createSign("RSA-SHA256");
signer.update(raw);
return {
header: header
, protected: protected64
, payload: payload64
, signature: signer.sign(pem, 'base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '')
};
};
RSA.generateCsrPem = function (keypair, domains) {
keypair = RSA.import(keypair);
return RSACSR.sync({ jwk: keypair.privateKeyJwk, domains: domains });
};
RSA.generateCsrDer = function (keypair, domains) {
keypair = RSA.import(keypair);
return RSACSR.sync({
jwk: keypair.privateKeyJwk
, domains: domains
, encoding: 'der'
});
};
RSA.generateCsrDerWeb64 =RSA.generateCsrWeb64 = function (keypair, names) {
var buf = RSA.generateCsrDer(keypair, names);
var b64 = buf.toString('base64');
return RSA.utils.toWebsafeBase64(b64);
};
RSA._thumbprintInput = function (n, e) {
// #L147 const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}`
return Buffer.from('{"e":"'+ e + '","kty":"RSA","n":"'+ n +'"}', 'ascii');
};
RSA._thumbprint = function (keypair) {
var publicKeyJwk = keypair.publicKeyJwk;
if (!publicKeyJwk.e || !publicKeyJwk.n) {
throw new Error("You must provide an RSA jwk with 'e' and 'n' (the public components)");
}
var input = RSA._thumbprintInput(publicKeyJwk.n, publicKeyJwk.e);
var base64Digest = require('crypto').createHash('sha256').update(input).digest('base64');
return RSA.utils.toWebsafeBase64(base64Digest);
};
RSA.thumbprint = function (keypair) {
if (!keypair.publicKeyJwk) {
keypair.publicKeyJwk = RSA.exportPublicJwk(keypair);
}
return RSA._thumbprint(keypair);
};
RSA.utils.toWebsafeBase64 = function (b64) {
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g,"");
};
RSA.exportPrivateKey = RSA.exportPrivatePem;
RSA.exportPublicKey = RSA.exportPublicPem;

111
express-server/node_modules/rsa-compat/lib/telemetry.js generated vendored Normal file
View File

@ -0,0 +1,111 @@
'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' });
}

108
express-server/node_modules/rsa-compat/package.json generated vendored Normal file
View File

@ -0,0 +1,108 @@
{
"_args": [
[
"rsa-compat@^1.5.0",
"/nodeapps/https-test/greenlock-express.js/node_modules/greenlock"
]
],
"_from": "rsa-compat@>=1.5.0 <2.0.0",
"_hasShrinkwrap": false,
"_id": "rsa-compat@1.9.0",
"_inCache": true,
"_installable": true,
"_location": "/rsa-compat",
"_nodeVersion": "10.13.0",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/rsa-compat_1.9.0_1544954189568_0.26195947533693653"
},
"_npmUser": {
"email": "coolaj86@gmail.com",
"name": "coolaj86"
},
"_npmVersion": "6.4.1",
"_phantomChildren": {},
"_requested": {
"name": "rsa-compat",
"raw": "rsa-compat@^1.5.0",
"rawSpec": "^1.5.0",
"scope": null,
"spec": ">=1.5.0 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/acme-v2",
"/greenlock"
],
"_resolved": "https://registry.npmjs.org/rsa-compat/-/rsa-compat-1.9.0.tgz",
"_shasum": "cd2ffd70d0a02f126342951bc809d40a18a4c0ce",
"_shrinkwrap": null,
"_spec": "rsa-compat@^1.5.0",
"_where": "/nodeapps/https-test/greenlock-express.js/node_modules/greenlock",
"author": {
"email": "coolaj86@gmail.com",
"name": "AJ ONeal",
"url": "https://coolaj86.com/"
},
"bin": {
"rsa-keygen-js": "bin/rsa-keygen.js"
},
"bugs": {
"url": "https://git.coolaj86.com/coolaj86/rsa-compat.js/issues"
},
"dependencies": {
"node-forge": "^0.7.6",
"ursa-optional": "^0.9.10"
},
"description": "RSA utils that work on Windows, Mac, and Linux with or without C compiler",
"devDependencies": {},
"directories": {},
"dist": {
"fileCount": 55,
"integrity": "sha512-0Qx/LE/n2ulCofM2H72CVBptMHcnUXZVKw7F1rOny+nFgTo2aEMFwVIEvS1WfnuT14U42RanuZEIzcYVScQcQA==",
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFiFOCRA9TVsSAnZWagAAzzsP/ig2SAoTOUMLTgRRTBVp\nXXjZ4tGbgaW86shjrzfibZzt44svlvggJad5m4zzc8Oatm36RDVLrgUwbLeQ\nVLInLWiIZX+wJP5ToV4vAwpjfGubFyhi9jbj9YBGw6XJ/zxGHcaLD2/m8Nec\nqemjPASLPhGAbPmDOsyfzDsuJlGiTI89ysBFpyEkpjz00si0ZDZhY4r/0ISB\nvfaWn0ISTnDRVCvYH8Y+BUuCYUCNP/jn2tiXd+18kuCwrIeHsLxk7+mz58NH\nFvQTpk02diP8erkWxRoggWwazXVhq5zQBp5S14BVdLPimgW8ViHv0YUfOrRe\nxn0J1B0M2I0pmkGX2ROBn8AmQExDmYR1BzS22euVxvByEsLMa2zQSxFjPzVG\nZ3kpCta4FHCRQqgau5jI8GAHvogET1w7cANL8di9kiHi18k5C2SbUTmkqSSt\n9/WTzsIfuC7pGI2t211WbEfO313B9hs27B6wkGOO379ckeveVYJryU27VNm6\nBiMt7zR36vR2hvuFUhBSieXjMGup9yfHKq225VcZZDZg07mtx4lK32hNVTG1\n0ZfC09RDcKip9AN2w7eyyw7wd9yWSmVC20wb7ppOVBIl6pfbcu67RyuJWmJ1\nzDA95/JPgtRc1Pl/s99y36dJ7PgThTWNSHi528oYFkek1240UH4vqupLM/du\nmYC3\r\n=b4p+\r\n-----END PGP SIGNATURE-----\r\n",
"shasum": "cd2ffd70d0a02f126342951bc809d40a18a4c0ce",
"tarball": "https://registry.npmjs.org/rsa-compat/-/rsa-compat-1.9.0.tgz",
"unpackedSize": 126979
},
"gitHead": "7580d700bfe9632abae30a4faefc2b3fd10b0b74",
"homepage": "https://git.coolaj86.com/coolaj86/rsa-compat.js#readme",
"keywords": [
"RSA",
"certificate",
"forge",
"jwk",
"key",
"linux",
"mac",
"ssl",
"tls",
"ursa",
"windows"
],
"license": "MPL-2.0",
"main": "index.js",
"maintainers": [
{
"name": "coolaj86",
"email": "coolaj86@gmail.com"
}
],
"name": "rsa-compat",
"optionalDependencies": {
"ursa-optional": "^0.9.10"
},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "https://git.coolaj86.com/coolaj86/rsa-compat.js.git"
},
"scripts": {
"postinstall": "node lib/telemetry.js event:install",
"test": "bash test.sh"
},
"trulyOptionalDependencies": {
"buffer-v6-polyfill": "^1.0.3"
},
"version": "1.9.0"
}

8
express-server/node_modules/rsa-compat/test.sh generated vendored Normal file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
node tests/generate-csr.js
node tests/generate-key.js
node tests/generate-key-new.js
node tests/generate-sig.js
node tests/reciprocate.js

View File

@ -0,0 +1,26 @@
'use strict';
var RSA = require('../').RSA;
var keypair = {
privateKeyJwk: {
"kty": "RSA",
"n": "AMJubTfOtAarnJytLE8fhNsEI8wnpjRvBXGK/Kp0675J10ORzxyMLqzIZF3tcrUkKBrtdc79u4X0GocDUgukpfkY+2UPUS/GxehUYbYrJYWOLkoJWzxn7wfoo9X1JgvBMY6wHQnTKvnzZdkom2FMhGxkLaEUGDSfsNznTTZNBBg9",
"e": "AQAB",
"d": "HT8DCrv69G3n9uFNovE4yMEMqW7lX0m75eJkMze3Jj5xNOa/4qlrc+4IuuA2uuyfY72IVQRxqqqXOuvS8ZForZZk+kWSd6z45hrpbNAAHH2Rf7XwnwHY8VJrOQF3UtbktTWqHX36ITZb9Hmf18hWsIeEp8Ng7Ru9h7hNuVxKMjk=",
"p": "AONjOvZVAvhCM2JnLGWJG3+5Boar3MB5P4ezfExDmuyGET/w0C+PS60jbjB8TivQsSdEcGo7GOaOlmAX6EQtAec=",
"q": "ANrllgJsy4rTMfa3mQ50kMIcNahiEOearhAcJgQUCHuOjuEnhU9FfExA/m5FXjmEFQhRwkuhk0QaIqTGbUzxGDs=",
"dp": "ALuxHOpYIatqeZ+wKiVllx1GTOy8z+rQKnCI5wDMjQTPZU2yKSYY0g6IQFwlPyFLke8nvuLxBQzKhbWsBjzAKeE=",
"dq": "XLhDAmPzE6rBzy+VtXnKl247jEd9wZzTfh9uOuwBa9TG0Lhcz2cvb11YaH0ZnGNGRW/cTQzzxDUN1531TlIRYQ==",
"qi": "AI2apz6ECfGwhsvIcU3+yFt+3CA78CUVsX4NUul5m3Cls2m+5MbGQG5K0hGpxjDC3OmXTq1Y5gnep5yUZvVPZI4="
}
};
var csrPem = RSA.generateCsrPem(keypair, ['example.com', 'www.example.com']);
var csr64 = RSA.generateCsrDerWeb64(keypair, ['example.com', 'www.example.com']);
console.log('');
console.log('DEBUG csrPem');
console.log(csrPem);
console.log('');
console.log('DEBUG csr64');
console.log(csr64);

View File

@ -0,0 +1,33 @@
'use strict';
var RSA = require('../').RSA;
RSA.generateKeypair(null, function (err, keys) {
if (!keys || !keys.privateKeyJwk) {
throw new Error("Expected privateKeyJwk, but it is missing");
}
var options = {
public: true // export public keys
, pem: true // export pems
, jwk: false // export jwks
, internal: true // preserve internal intermediate formats (_ursa, _forge)
//, thumbprint: true // JWK sha256 thumbprint
, bitlen: 2048
, exp: 65537
};
RSA.generateKeypair(options, function (err, keys) {
if (
(keys.publicKeyJwk && !keys.thumbprint)
|| !keys.privateKeyPem
|| !keys.publicKeyPem
//|| !keys.thumbprint
) {
console.error(Object.keys(keys));
throw new Error("Missing expected keys");
}
console.log('All is well!');
});
});

View File

@ -0,0 +1,31 @@
'use strict';
var RSA = require('../').RSA;
RSA.generateKeypair(null, null, null, function (err, keys) {
if (!keys.privateKeyJwk) {
throw new Error("Expected privateKeyJwk, but it is missing");
}
var options = {
public: true // export public keys
, pem: true // export pems
, jwk: false // export jwks
, internal: true // preserve internal intermediate formats (_ursa, _forge)
//, thumbprint: true // JWK sha256 thumbprint
};
RSA.generateKeypair(512, 65537, options, function (err, keys) {
if (
(keys.publicKeyJwk && !keys.thumbprint)
|| !keys.privateKeyPem
|| !keys.publicKeyPem
//|| !keys.thumbprint
) {
console.error(Object.keys(keys));
throw new Error("Missing expected keys");
}
console.log('All is well!');
});
});

View File

@ -0,0 +1,54 @@
'use strict';
var RSA = require('../').RSA;
var keypair = {
privateKeyJwk: {
"kty": "RSA",
"n": "AMJubTfOtAarnJytLE8fhNsEI8wnpjRvBXGK_Kp0675J10ORzxyMLqzIZF3tcrUkKBrtdc79u4X0GocDUgukpfkY-2UPUS_GxehUYbYrJYWOLkoJWzxn7wfoo9X1JgvBMY6wHQnTKvnzZdkom2FMhGxkLaEUGDSfsNznTTZNBBg9",
"e": "AQAB",
"d": "HT8DCrv69G3n9uFNovE4yMEMqW7lX0m75eJkMze3Jj5xNOa_4qlrc-4IuuA2uuyfY72IVQRxqqqXOuvS8ZForZZk-kWSd6z45hrpbNAAHH2Rf7XwnwHY8VJrOQF3UtbktTWqHX36ITZb9Hmf18hWsIeEp8Ng7Ru9h7hNuVxKMjk=",
"p": "AONjOvZVAvhCM2JnLGWJG3-5Boar3MB5P4ezfExDmuyGET_w0C-PS60jbjB8TivQsSdEcGo7GOaOlmAX6EQtAec=",
"q": "ANrllgJsy4rTMfa3mQ50kMIcNahiEOearhAcJgQUCHuOjuEnhU9FfExA_m5FXjmEFQhRwkuhk0QaIqTGbUzxGDs=",
"dp": "ALuxHOpYIatqeZ-wKiVllx1GTOy8z-rQKnCI5wDMjQTPZU2yKSYY0g6IQFwlPyFLke8nvuLxBQzKhbWsBjzAKeE=",
"dq": "XLhDAmPzE6rBzy-VtXnKl247jEd9wZzTfh9uOuwBa9TG0Lhcz2cvb11YaH0ZnGNGRW_cTQzzxDUN1531TlIRYQ==",
"qi": "AI2apz6ECfGwhsvIcU3-yFt-3CA78CUVsX4NUul5m3Cls2m-5MbGQG5K0hGpxjDC3OmXTq1Y5gnep5yUZvVPZI4="
}
};
/*
var ursaResult = {
"header": {
"alg": "RS256",
"jwk": {
"kty": "RSA",
"n": "AMJubTfOtAarnJytLE8fhNsEI8wnpjRvBXGK_Kp0675J10ORzxyMLqzIZF3tcrUkKBrtdc79u4X0GocDUgukpfkY-2UPUS_GxehUYbYrJYWOLkoJWzxn7wfoo9X1JgvBMY6wHQnTKvnzZdkom2FMhGxkLaEUGDSfsNznTTZNBBg9",
"e": "AQAB"
}
},
"protected": "eyJub25jZSI6IjhlZjU2MjRmNWVjOWQzZWYifQ",
"payload": "JLzF1NBNCV3kfbJ5sFaFyX94fJuL2H-IzaoBN-ciiHk",
"signature": "Wb2al5SDyh5gjmkV79MK9m3sfNBBPjntSKor-34BBoGwr6n8qEnBmqB1Y4zbo-5rmvsoPmJsnRlP_hRiUY86zSAQyfbisTGrGBl0IQ7ditpkfYVm0rBWJ8WnYNqYNp8K3qcD7NW72tsy-XoWEjNlz4lWJeRdEG2Nt4CJgnREH4Y"
};
var forgeResult = {
"header": {
"alg": "RS256",
"jwk": {
"kty": "RSA",
"n": "AMJubTfOtAarnJytLE8fhNsEI8wnpjRvBXGK_Kp0675J10ORzxyMLqzIZF3tcrUkKBrtdc79u4X0GocDUgukpfkY-2UPUS_GxehUYbYrJYWOLkoJWzxn7wfoo9X1JgvBMY6wHQnTKvnzZdkom2FMhGxkLaEUGDSfsNznTTZNBBg9",
"e": "AQAB"
}
},
"protected": "eyJub25jZSI6IjhlZjU2MjRmNWVjOWQzZWYifQ",
"payload": "JLzF1NBNCV3kfbJ5sFaFyX94fJuL2H-IzaoBN-ciiHk",
"signature": "Wb2al5SDyh5gjmkV79MK9m3sfNBBPjntSKor-34BBoGwr6n8qEnBmqB1Y4zbo-5rmvsoPmJsnRlP_hRiUY86zSAQyfbisTGrGBl0IQ7ditpkfYVm0rBWJ8WnYNqYNp8K3qcD7NW72tsy-XoWEjNlz4lWJeRdEG2Nt4CJgnREH4Y"
};
*/
var jws = RSA.signJws(
keypair
, Buffer.from('24bcc5d4d04d095de47db279b05685c97f787c9b8bd87f88cdaa0137e7228879', 'hex')
, '8ef5624f5ec9d3ef'
);
console.log(JSON.stringify(jws, null, ' '));

View File

@ -0,0 +1 @@
{"kty":"RSA","n":"sdk_fKUwpygZjNuAt7804KFBZoeBXtJ5cMZyRDRCmPNZRjOqXSdMTY7We7W103Ivs97hz--quMg-R8smv3F1-4Quyfm5vXFQWPxYdrsfJzPkyku4lY8uWbZ8N0X1CM61NgQHOAwe8eHOM0zd-MECxrBASBzLuiq6Df-GpsridFivMYlFj7UTXm92ReFyOXZUZacXneCVWyUMIU6Faj8h2UVpaCCNsopT0fRoqoBIJ-qeXhDpbvfLfhbb25p79msutxN7mpGW9a3wL1LyoCTpMLqIlZSWKKDVCNyoTS9RH7lch-j8_DtL9VfNTPv1rw-n-N-LQDQH6z5j7ZanXiek0Q","e":"AQAB","d":"pQuaej8Gpql1BJ4TlvHvzWJ4wgCQk4G5x2myUw84mp6Cw75gpLA6hGNBMm8QPRjjogX-Ay1EftT9zzx9Sf6uYepqTW_d6ivFR79vDUhcAdKLyRx6QPkuJ31NSzI10qWZz3XviRjegtRtunfkKtxbii24ifOHMM5D-Yef2xu_Cf5RRZVSipZdLRlgttSW2t-lXCaFN1-F8ITPCEqaQGoHn5fk9eEhl2sd3r8BCN1xp_KTAyqxBt9inDr__YlYynXS9Y35Wu4Ofn9CszwiCrZxezWO6Mn0-T-woDYj3jsO7aRvWPOIcMSaL7KqbM1BIWKPJqtSXqc8uVs4ZcVEnB4pXQ","p":"6sx1OM-3ise7cQCOffWLxbB8xNfpQSpyC0_ZzNF-FNSpCfFiJ6GjhBhgddppdyLD8jR34ZO4sTM6ny575ez6XOVrSUHi4Cavnv7DH8PxWT3_IVGKYKbqQt63KdIKpxxC3EoG66VPCQvjJ-TgkjNhqPLFKDW9ihvHVQlsDrVcLss","q":"wehZNfic8h69BUp8ynQIqSaao--N4fKm_fOH8kvmsZQlns0Asiy2jTYnfZy0jN380x_A8Q2Bx15N5byokRzn3NzC3dni_Z87h-c0Upss7G4iIPDngUwAN0EnraXbzVAv1jWC7wdsYJgSOLxJ0QVcLN-yPPs12ESf3hTQS2XNS1M","dp":"OWt0w0bw8Mx1h2YGcsFIxDir2ouerGQP7bpCXjdhKQD0sczJHz4FOfmoJUobfpoO5z1zj8PLZ_JKRMFeZVU1yOAGA61MKVrsmepdl3gYnqVtrh09xPMs2NuUWXLXThS4OR0VYi1UDbdNfuUXEknkeSedlX7G8m5mqeIa_eHWQE8","dq":"P7zGBi2QOS3NZ_3SAyIc7qzXy-ckNg7ywSrZltVIITrXGtm7ztVPtB8G4A0wc9VME4GuhA2yiPwAkvFuXzSnKjwOeanzKPa8NLHUywpiu9x_r2BAWInI0GDEUBnipBwlfT3dqgBAqYeTBb8ubFxcrgthpRXV3x6_hdpFlWMDFi0","qi":"FnwWA6Flj9jp2vy_RL1WEDTSr2xCdVm3guNuUhwXIav5-tXm5kDca-4fJEGUiSYzh_MLApUT9U-97zV5Qfws-OAq6pDu1-34hU-w1xpKh1OKvKEOfvYGX1mdzu3FIaaB7RQSr_TW3AyOl29HP--lOcrsYmIK5xnuqc13MQ6Nss4"}

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAsdk/fKUwpygZjNuAt7804KFBZoeBXtJ5cMZyRDRCmPNZRjOq
XSdMTY7We7W103Ivs97hz++quMg+R8smv3F1+4Quyfm5vXFQWPxYdrsfJzPkyku4
lY8uWbZ8N0X1CM61NgQHOAwe8eHOM0zd+MECxrBASBzLuiq6Df+GpsridFivMYlF
j7UTXm92ReFyOXZUZacXneCVWyUMIU6Faj8h2UVpaCCNsopT0fRoqoBIJ+qeXhDp
bvfLfhbb25p79msutxN7mpGW9a3wL1LyoCTpMLqIlZSWKKDVCNyoTS9RH7lch+j8
/DtL9VfNTPv1rw+n+N+LQDQH6z5j7ZanXiek0QIDAQABAoIBAQClC5p6PwamqXUE
nhOW8e/NYnjCAJCTgbnHabJTDzianoLDvmCksDqEY0EybxA9GOOiBf4DLUR+1P3P
PH1J/q5h6mpNb93qK8VHv28NSFwB0ovJHHpA+S4nfU1LMjXSpZnPde+JGN6C1G26
d+Qq3FuKLbiJ84cwzkP5h5/bG78J/lFFlVKKll0tGWC21Jba36VcJoU3X4XwhM8I
SppAagefl+T14SGXax3evwEI3XGn8pMDKrEG32KcOv/9iVjKddL1jfla7g5+f0Kz
PCIKtnF7NY7oyfT5P7CgNiPeOw7tpG9Y84hwxJovsqpszUEhYo8mq1Jepzy5Wzhl
xUScHildAoGBAOrMdTjPt4rHu3EAjn31i8WwfMTX6UEqcgtP2czRfhTUqQnxYieh
o4QYYHXaaXciw/I0d+GTuLEzOp8ue+Xs+lzla0lB4uAmr57+wx/D8Vk9/yFRimCm
6kLetynSCqccQtxKBuulTwkL4yfk4JIzYajyxSg1vYobx1UJbA61XC7LAoGBAMHo
WTX4nPIevQVKfMp0CKkmmqPvjeHypv3zh/JL5rGUJZ7NALIsto02J32ctIzd/NMf
wPENgcdeTeW8qJEc59zcwt3Z4v2fO4fnNFKbLOxuIiDw54FMADdBJ62l281QL9Y1
gu8HbGCYEji8SdEFXCzfsjz7NdhEn94U0EtlzUtTAoGAOWt0w0bw8Mx1h2YGcsFI
xDir2ouerGQP7bpCXjdhKQD0sczJHz4FOfmoJUobfpoO5z1zj8PLZ/JKRMFeZVU1
yOAGA61MKVrsmepdl3gYnqVtrh09xPMs2NuUWXLXThS4OR0VYi1UDbdNfuUXEknk
eSedlX7G8m5mqeIa/eHWQE8CgYA/vMYGLZA5Lc1n/dIDIhzurNfL5yQ2DvLBKtmW
1UghOtca2bvO1U+0HwbgDTBz1UwTga6EDbKI/ACS8W5fNKcqPA55qfMo9rw0sdTL
CmK73H+vYEBYicjQYMRQGeKkHCV9Pd2qAECph5MFvy5sXFyuC2GlFdXfHr+F2kWV
YwMWLQKBgBZ8FgOhZY/Y6dr8v0S9VhA00q9sQnVZt4LjblIcFyGr+frV5uZA3Gvu
HyRBlIkmM4fzCwKVE/VPve81eUH8LPjgKuqQ7tft+IVPsNcaSodTiryhDn72Bl9Z
nc7txSGmge0UEq/01twMjpdvRz/vpTnK7GJiCucZ7qnNdzEOjbLO
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,86 @@
'use strict';
var RSA = require('../').RSA;
var fs = require('fs');
var path = require('path');
var privkeyPemRef = fs.readFileSync(path.join(__dirname, 'privkey.pem'), 'ascii');
var privkeyJwkRef = JSON.parse(fs.readFileSync(path.join(__dirname, 'privkey.jwk'), 'ascii'));
var refs = {
privPem: RSA.exportPrivatePem({ privateKeyJwk: privkeyJwkRef })
, privJwk: RSA.exportPrivateJwk({ privateKeyPem: privkeyPemRef })
};
var hasUrsa;
var imported;
try {
hasUrsa = require('ursa') && true;
} catch(e) {
hasUrsa = false;
}
//
//
// PEM tests
//
//
console.log('JWK -> PEM ?', privkeyPemRef === refs.privPem);
if (privkeyPemRef !== refs.privPem) {
// Watch out for tricky whitespaces (\n instead of \r\n, trailing \r\n, etc)
console.log('REF:');
console.log(JSON.stringify(privkeyPemRef));
console.log('GEN:');
console.log(JSON.stringify(refs.privPem));
throw new Error("Failed to validate importedJwk against referencePem");
}
console.log('PEM -> _ -> PEM ?', privkeyPemRef === refs.privPem);
if (hasUrsa) {
imported = RSA.import({ privateKeyPem: privkeyPemRef });
refs.privPem2 = RSA.exportPrivatePem({ _ursa: imported._ursa });
}
else {
imported = RSA.import({ privateKeyPem: privkeyPemRef });
refs.privPem2 = RSA.exportPrivatePem(imported);
}
if (privkeyPemRef !== refs.privPem2) {
console.log('REF:');
console.log(JSON.stringify(privkeyPemRef));
console.log('GEN:');
console.log(JSON.stringify(refs.privPem2));
throw new Error("Failed to validate importedPem against referencePem");
}
//
//
// JWK tests
//
//
console.log('PEM -> JWK ?', privkeyJwkRef.n === refs.privJwk.n);
if (![ 'kty', 'n', 'e', 'p', 'q', 'dp', 'dq', 'qi', 'd' ].every(function (k) {
return privkeyJwkRef[k] === refs.privJwk[k];
})) {
console.log('REF:');
console.log(privkeyJwkRef);
console.log('GEN:');
console.log(refs.privJwk);
throw new Error("Failed to validate importedPem against referenceJwk");
}
imported = RSA.import({ privateKeyJwk: privkeyJwkRef });
refs.privJwk2 = RSA.exportPrivateJwk(imported);
console.log('JWK -> _ -> JWK ?', privkeyJwkRef.n === refs.privJwk2.n);
if (privkeyJwkRef.n !== refs.privJwk2.n) {
console.log('REF:');
console.log(privkeyJwkRef);
console.log('GEN:');
console.log(refs.privJwk2);
throw new Error("Failed to validate importedJwk against referenceJwk");
}
console.log('');