Compare commits
	
		
			No commits in common. "master" and "v0.0.2" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										217
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								README.md
									
									
									
									
									
								
							@ -1,215 +1,16 @@
 | 
				
			|||||||
[RSA-CSR.js](https://git.coolaj86.com/coolaj86/rsa-csr.js)
 | 
					RSA-CSR.js
 | 
				
			||||||
==========
 | 
					==========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
A [Root](https://therootcompany.com) Project.
 | 
					This is a work in progress.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Built for [ACME.js](https://git.coolaj86.com/coolaj86/acme.js)
 | 
					I recently finished the EC variants:
 | 
				
			||||||
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:
 | 
					* [ECDSA-CSR.js](https://git.coolaj86.com/coolaj86/ecdsa-csr.js)
 | 
				
			||||||
  * Generate a Certificate Signing Requests (CSR), and sign it!
 | 
					* [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| < 300 lines of code | 1.7k gzipped | 4.7k minified | 8.5k with comments |
 | 
					I'm mostly done with [Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js)
 | 
				
			||||||
 | 
					and I already have a working prototype to generate CSRs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Features
 | 
					It'll all wrap up soon - expect it within a week.
 | 
				
			||||||
========
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
* [x] Universal CSR support (RSA signing) that Just Works™
 | 
					In the meantime, I'm squatting the module name.
 | 
				
			||||||
  * 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
 | 
					 | 
				
			||||||
* [ ] JWK-to-PEM
 | 
					 | 
				
			||||||
    * See [Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js)
 | 
					 | 
				
			||||||
* [ ] EC CSR
 | 
					 | 
				
			||||||
    * See [ECSDA-CSR.js](https://git.coolaj86.com/coolaj86/ecdsa-csr.js)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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({ jwk: 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/new) if it doesn't
 | 
					 | 
				
			||||||
do what you need, it may make sense to add it (or otherwise, perhaps to help you create a fork).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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)
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
#!/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({ jwk: 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);
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
@ -1,16 +0,0 @@
 | 
				
			|||||||
-----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-----
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "kty": "RSA",
 | 
					 | 
				
			||||||
  "n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw",
 | 
					 | 
				
			||||||
  "e": "AQAB",
 | 
					 | 
				
			||||||
  "d": "Cpfo7Mm9Nu8YMC_xrZ54W9mKHPkCG9rZ93Ds9PNp-RXUgb-ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSBsJyNv2LJZon-e85X74nv53UlIkmo9SYxdLz2JaJ-iIWEe8Qh-7llLktrTJV_xr98_tbhgSppz_IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF_WtX67XJ0C6-LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV-NbthEn5rmRtVnjRZ3yaxQ0ud8vC-NONn7yvGUlOur1IdDzJ_YfHPt9sHMQQ",
 | 
					 | 
				
			||||||
  "p": "ynG-t9HwKCN3MWRYFdnFzi9-02Qcy3p8B5pu3ary2E70hYn2pHlUG2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU_UYEbAtPv_PxSmzpQp9n9XnYvBLBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCE",
 | 
					 | 
				
			||||||
  "q": "xIkAjgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c_8397_DZDDo46fnFYjs6uPa03HpmKUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnFD2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1c",
 | 
					 | 
				
			||||||
  "dp": "tzDGjECFOU0ehqtuqhcuT63a7h8hj19-7MJqoFwY9HQ-ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyhc_nAVfYPEC_2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k-q2tp3ieBYdVGAXJoGOdv5VpaZ7B1QE",
 | 
					 | 
				
			||||||
  "dq": "kh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3xN7iUVmAg-ToKjwbVnG5-7eXiC779rQVwnrD_0yh1AFJ8wjRPqDIR7ObXGHikIxT1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7-NxLKWzqao_u4lhnDQaX9PKa12HFlny6K1daL48",
 | 
					 | 
				
			||||||
  "qi": "AlHWbx1gp6Z9pbw_1hlS7HuXAgWoX7IjbTUelldf4gkriDWLOrj3QCZcO4ZvZvEwJhVlsny9LO8IkbwGJEL6cXraK08ByVS2mwQyflgTgGNnpzixyEUL_mrQLx6y145FHcxfeqNInMhep-0Mxn1D5nlhmIOgRApS0t9VoXtHhFU"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,27 +0,0 @@
 | 
				
			|||||||
-----BEGIN RSA PRIVATE KEY-----
 | 
					 | 
				
			||||||
MIIEpAIBAAKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhD
 | 
					 | 
				
			||||||
NzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ
 | 
					 | 
				
			||||||
38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57
 | 
					 | 
				
			||||||
qAZM6+I70on0/iDZm7+jcqOPgADAmbWHhy67BXkk4yy/YzD4yOGZFXZcNp915/TW
 | 
					 | 
				
			||||||
5bRd//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By
 | 
					 | 
				
			||||||
5AB0Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQABAoIBAAqX6OzJvTbvGDAv
 | 
					 | 
				
			||||||
8a2eeFvZihz5Ahva2fdw7PTzafkV1IG/pY02xT2VrGMRjSi5ZRc/CzaLK9aU9mQz
 | 
					 | 
				
			||||||
K8DZNAH9Yljbw+oJMl2okHQsogz0gbCcjb9iyWaJ/nvOV++J7+d1JSJJqPUmMXS8
 | 
					 | 
				
			||||||
9iWifoiFhHvEIfu5ZS5La0yVf8a/fP7W4YEqac/yHjspqt0hGWkBzPKU1O8O17wo
 | 
					 | 
				
			||||||
9qW/a/HzdDNF3IFmSGn9yxhH5IRf1rV+u1ydAuvi25E+dY7ZS0TRl+oWhnbHF0tD
 | 
					 | 
				
			||||||
lloyjicyhrlfjW7YRJ+a5kbVZ40Wd8msUNLnfLwvjTjZ+8rxlJTrq9SHQ8yf2Hxz
 | 
					 | 
				
			||||||
7fbBzEECgYEAynG+t9HwKCN3MWRYFdnFzi9+02Qcy3p8B5pu3ary2E70hYn2pHlU
 | 
					 | 
				
			||||||
G2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU/UYEbAtPv/PxSmzpQp9n9XnYvB
 | 
					 | 
				
			||||||
LBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCECgYEAxIkA
 | 
					 | 
				
			||||||
jgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c/8397/DZDDo46fnFYjs6uPa03Hpm
 | 
					 | 
				
			||||||
KUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnF
 | 
					 | 
				
			||||||
D2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1cCgYEAtzDGjECFOU0ehqtuqhcu
 | 
					 | 
				
			||||||
T63a7h8hj19+7MJqoFwY9HQ+ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyh
 | 
					 | 
				
			||||||
c/nAVfYPEC/2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k+q2tp3ie
 | 
					 | 
				
			||||||
BYdVGAXJoGOdv5VpaZ7B1QECgYEAkh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3
 | 
					 | 
				
			||||||
xN7iUVmAg+ToKjwbVnG5+7eXiC779rQVwnrD/0yh1AFJ8wjRPqDIR7ObXGHikIxT
 | 
					 | 
				
			||||||
1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7+NxLKWzqao/u4lhnDQaX9PKa12HFlny6
 | 
					 | 
				
			||||||
K1daL48CgYACUdZvHWCnpn2lvD/WGVLse5cCBahfsiNtNR6WV1/iCSuINYs6uPdA
 | 
					 | 
				
			||||||
Jlw7hm9m8TAmFWWyfL0s7wiRvAYkQvpxetorTwHJVLabBDJ+WBOAY2enOLHIRQv+
 | 
					 | 
				
			||||||
atAvHrLXjkUdzF96o0icyF6n7QzGfUPmeWGYg6BEClLS31Whe0eEVQ==
 | 
					 | 
				
			||||||
-----END RSA PRIVATE KEY-----
 | 
					 | 
				
			||||||
@ -1,17 +0,0 @@
 | 
				
			|||||||
-----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-----
 | 
					 | 
				
			||||||
							
								
								
									
										72
									
								
								lib/asn1.js
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								lib/asn1.js
									
									
									
									
									
								
							@ -1,72 +0,0 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// A dumbed-down, minimal ASN.1 parser / packer combo
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Note: generally I like to write congruent code
 | 
					 | 
				
			||||||
// (i.e. output can be used as input and vice-versa)
 | 
					 | 
				
			||||||
// However, this seemed to be more readable and easier
 | 
					 | 
				
			||||||
// to use written as-is, asymmetrically.
 | 
					 | 
				
			||||||
// (I also generally prefer to export objects rather
 | 
					 | 
				
			||||||
// functions but, yet again, asthetics one in this case)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var Enc = require('./encoding.js');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Packer
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Almost every ASN.1 type that's important for CSR
 | 
					 | 
				
			||||||
// can be represented generically with only a few rules.
 | 
					 | 
				
			||||||
var ASN1 = module.exports = function ASN1(/*type, hexstrings...*/) {
 | 
					 | 
				
			||||||
  var args = Array.prototype.slice.call(arguments);
 | 
					 | 
				
			||||||
  var typ = args.shift();
 | 
					 | 
				
			||||||
  var str = args.join('').replace(/\s+/g, '').toLowerCase();
 | 
					 | 
				
			||||||
  var len = (str.length/2);
 | 
					 | 
				
			||||||
  var lenlen = 0;
 | 
					 | 
				
			||||||
  var hex = typ;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // We can't have an odd number of hex chars
 | 
					 | 
				
			||||||
  if (len !== Math.round(len)) {
 | 
					 | 
				
			||||||
    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);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										122
									
								
								lib/csr.js
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								lib/csr.js
									
									
									
									
									
								
							@ -1,122 +0,0 @@
 | 
				
			|||||||
'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
 | 
					 | 
				
			||||||
  return Promise.resolve().then(function () {
 | 
					 | 
				
			||||||
    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 CSR.create(opts).then(function (bytes) {
 | 
					 | 
				
			||||||
      return PEM.packBlock({
 | 
					 | 
				
			||||||
        type: "CERTIFICATE REQUEST"
 | 
					 | 
				
			||||||
      , bytes: bytes /* { jwk: jwk, domains: opts.domains } */
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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.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) {
 | 
					 | 
				
			||||||
    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
 | 
					 | 
				
			||||||
    , request
 | 
					 | 
				
			||||||
      // The Signature Type
 | 
					 | 
				
			||||||
    , sty
 | 
					 | 
				
			||||||
      // The Signature
 | 
					 | 
				
			||||||
    , ASN1.BitStr(Enc.bufToHex(sig))
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// RSA
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Took some tips from https://gist.github.com/codermapuche/da4f96cdb6d5ff53b7ebc156ec46a10a
 | 
					 | 
				
			||||||
RSA.sign = function signRsa(keypem, ab) {
 | 
					 | 
				
			||||||
  return Promise.resolve().then(function () {
 | 
					 | 
				
			||||||
    // Signer is a stream
 | 
					 | 
				
			||||||
    var sign = crypto.createSign('SHA256');
 | 
					 | 
				
			||||||
    sign.write(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 sig.buffer.slice(sig.byteOffset, sig.byteOffset + sig.byteLength);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,34 +0,0 @@
 | 
				
			|||||||
'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();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										12
									
								
								lib/pem.js
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								lib/pem.js
									
									
									
									
									
								
							@ -1,12 +0,0 @@
 | 
				
			|||||||
'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 + '-----'
 | 
					 | 
				
			||||||
  ;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										111
									
								
								lib/telemetry.js
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								lib/telemetry.js
									
									
									
									
									
								
							@ -1,111 +0,0 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// We believe in a proactive approach to sustainable open source.
 | 
					 | 
				
			||||||
// As part of that we make it easy for you to opt-in to following our progress
 | 
					 | 
				
			||||||
// and we also stay up-to-date on telemetry such as operating system and node
 | 
					 | 
				
			||||||
// version so that we can focus our efforts where they'll have the greatest impact.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Want to learn more about our Terms, Privacy Policy, and Mission?
 | 
					 | 
				
			||||||
// Check out https://therootcompany.com/legal/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var os = require('os');
 | 
					 | 
				
			||||||
var crypto = require('crypto');
 | 
					 | 
				
			||||||
var https = require('https');
 | 
					 | 
				
			||||||
var pkg = require('../package.json');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// to help focus our efforts in the right places
 | 
					 | 
				
			||||||
var data = {
 | 
					 | 
				
			||||||
  package: pkg.name
 | 
					 | 
				
			||||||
, version: pkg.version
 | 
					 | 
				
			||||||
, node: process.version
 | 
					 | 
				
			||||||
, arch: process.arch || os.arch()
 | 
					 | 
				
			||||||
, platform: process.platform || os.platform()
 | 
					 | 
				
			||||||
, release: os.release()
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function addCommunityMember(opts) {
 | 
					 | 
				
			||||||
  setTimeout(function () {
 | 
					 | 
				
			||||||
    var req = https.request({
 | 
					 | 
				
			||||||
      hostname: 'api.therootcompany.com'
 | 
					 | 
				
			||||||
    , port: 443
 | 
					 | 
				
			||||||
    , path: '/api/therootcompany.com/public/community'
 | 
					 | 
				
			||||||
    , method: 'POST'
 | 
					 | 
				
			||||||
    , headers: { 'Content-Type': 'application/json' }
 | 
					 | 
				
			||||||
    }, function (resp) {
 | 
					 | 
				
			||||||
      // let the data flow, so we can ignore it
 | 
					 | 
				
			||||||
      resp.on('data', function () {});
 | 
					 | 
				
			||||||
      //resp.on('data', function (chunk) { console.log(chunk.toString()); });
 | 
					 | 
				
			||||||
      resp.on('error', function () { /*ignore*/ });
 | 
					 | 
				
			||||||
      //resp.on('error', function (err) { console.error(err); });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    var obj = JSON.parse(JSON.stringify(data));
 | 
					 | 
				
			||||||
    obj.action = 'updates';
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      obj.ppid = ppid(obj.action);
 | 
					 | 
				
			||||||
    } catch(e) {
 | 
					 | 
				
			||||||
      // ignore
 | 
					 | 
				
			||||||
      //console.error(e);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    obj.name = opts.name || undefined;
 | 
					 | 
				
			||||||
    obj.address = opts.email;
 | 
					 | 
				
			||||||
    obj.community = 'node.js@therootcompany.com';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    req.write(JSON.stringify(obj, 2, null));
 | 
					 | 
				
			||||||
    req.end();
 | 
					 | 
				
			||||||
    req.on('error', function () { /*ignore*/ });
 | 
					 | 
				
			||||||
    //req.on('error', function (err) { console.error(err); });
 | 
					 | 
				
			||||||
  }, 50);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function ping(action) {
 | 
					 | 
				
			||||||
  setTimeout(function () {
 | 
					 | 
				
			||||||
    var req = https.request({
 | 
					 | 
				
			||||||
      hostname: 'api.therootcompany.com'
 | 
					 | 
				
			||||||
    , port: 443
 | 
					 | 
				
			||||||
    , path: '/api/therootcompany.com/public/ping'
 | 
					 | 
				
			||||||
    , method: 'POST'
 | 
					 | 
				
			||||||
    , headers: { 'Content-Type': 'application/json' }
 | 
					 | 
				
			||||||
    }, function (resp) {
 | 
					 | 
				
			||||||
      // let the data flow, so we can ignore it
 | 
					 | 
				
			||||||
      resp.on('data', function () { });
 | 
					 | 
				
			||||||
      //resp.on('data', function (chunk) { console.log(chunk.toString()); });
 | 
					 | 
				
			||||||
      resp.on('error', function () { /*ignore*/ });
 | 
					 | 
				
			||||||
      //resp.on('error', function (err) { console.error(err); });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    var obj = JSON.parse(JSON.stringify(data));
 | 
					 | 
				
			||||||
    obj.action = action;
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      obj.ppid = ppid(obj.action);
 | 
					 | 
				
			||||||
    } catch(e) {
 | 
					 | 
				
			||||||
      // ignore
 | 
					 | 
				
			||||||
      //console.error(e);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    req.write(JSON.stringify(obj, 2, null));
 | 
					 | 
				
			||||||
    req.end();
 | 
					 | 
				
			||||||
    req.on('error', function (/*e*/) { /*console.error('req.error', e);*/ });
 | 
					 | 
				
			||||||
  }, 50);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// to help identify unique installs without getting
 | 
					 | 
				
			||||||
// the personally identifiable info that we don't want
 | 
					 | 
				
			||||||
function ppid(action) {
 | 
					 | 
				
			||||||
  var parts = [ action, data.package, data.version, data.node, data.arch, data.platform, data.release ];
 | 
					 | 
				
			||||||
  var ifaces = os.networkInterfaces();
 | 
					 | 
				
			||||||
  Object.keys(ifaces).forEach(function (ifname) {
 | 
					 | 
				
			||||||
    if (/^en/.test(ifname) || /^eth/.test(ifname) || /^wl/.test(ifname)) {
 | 
					 | 
				
			||||||
      if  (ifaces[ifname] && ifaces[ifname].length) {
 | 
					 | 
				
			||||||
        parts.push(ifaces[ifname][0].mac);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  return crypto.createHash('sha1').update(parts.join(',')).digest('base64');
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports.ping = ping;
 | 
					 | 
				
			||||||
module.exports.joinCommunity = addCommunityMember;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if (require.main === module) {
 | 
					 | 
				
			||||||
  ping('install');
 | 
					 | 
				
			||||||
  //addCommunityMember({ name: "AJ ONeal", email: 'coolaj86@gmail.com' });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										66
									
								
								lib/x509.js
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								lib/x509.js
									
									
									
									
									
								
							@ -1,66 +0,0 @@
 | 
				
			|||||||
'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));
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										29
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								package.json
									
									
									
									
									
								
							@ -1,34 +1,11 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "rsa-csr",
 | 
					  "name": "rsa-csr",
 | 
				
			||||||
  "version": "1.0.6",
 | 
					  "version": "0.0.2",
 | 
				
			||||||
  "description": "💯 A focused, zero-dependency library to generate a Certificate Signing Request (CSR) and sign it!",
 | 
					  "description": "",
 | 
				
			||||||
  "homepage": "https://git.coolaj86.com/coolaj86/rsa-csr.js",
 | 
					 | 
				
			||||||
  "main": "index.js",
 | 
					  "main": "index.js",
 | 
				
			||||||
  "bin": {
 | 
					 | 
				
			||||||
    "rsa-csr": "bin/rsa-csr.js"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "files": [
 | 
					 | 
				
			||||||
    "bin",
 | 
					 | 
				
			||||||
    "fixtures",
 | 
					 | 
				
			||||||
    "lib"
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  "directories": {
 | 
					 | 
				
			||||||
    "lib": "lib"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "postinstall": "node lib/telemetry.js event:install",
 | 
					    "test": "echo \"Error: no test specified\" && exit 1"
 | 
				
			||||||
    "test": "bash test.sh"
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "repository": {
 | 
					 | 
				
			||||||
    "type": "git",
 | 
					 | 
				
			||||||
    "url": "https://git.coolaj86.com/coolaj86/rsa-csr.js"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "keywords": [
 | 
					 | 
				
			||||||
    "zero-dependency",
 | 
					 | 
				
			||||||
    "CSR",
 | 
					 | 
				
			||||||
    "RSA",
 | 
					 | 
				
			||||||
    "x509"
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
					  "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
				
			||||||
  "license": "MPL-2.0"
 | 
					  "license": "MPL-2.0"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										87
									
								
								test.sh
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								test.sh
									
									
									
									
									
								
							@ -1,87 +0,0 @@
 | 
				
			|||||||
#!/bin/bash
 | 
					 | 
				
			||||||
set -e
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
gencsr2() {
 | 
					 | 
				
			||||||
  keyfile=$1
 | 
					 | 
				
			||||||
  domain=$2
 | 
					 | 
				
			||||||
  csrfile=$3
 | 
					 | 
				
			||||||
  openssl req -key $keyfile -new -nodes \
 | 
					 | 
				
			||||||
    -config <(printf "[req]
 | 
					 | 
				
			||||||
prompt = no
 | 
					 | 
				
			||||||
req_extensions = req_ext
 | 
					 | 
				
			||||||
distinguished_name = dn
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ dn ]
 | 
					 | 
				
			||||||
CN = $domain
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ req_ext ]
 | 
					 | 
				
			||||||
subjectAltName = @alt_names
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ alt_names ]
 | 
					 | 
				
			||||||
DNS.1 = $domain
 | 
					 | 
				
			||||||
DNS.2 = www.$domain") \
 | 
					 | 
				
			||||||
    -out $csrfile
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
gencsr3() {
 | 
					 | 
				
			||||||
  keyfile=$1
 | 
					 | 
				
			||||||
  domain=$2
 | 
					 | 
				
			||||||
  csrfile=$3
 | 
					 | 
				
			||||||
  openssl req -key $keyfile -new -nodes \
 | 
					 | 
				
			||||||
    -config <(printf "[req]
 | 
					 | 
				
			||||||
prompt = no
 | 
					 | 
				
			||||||
req_extensions = req_ext
 | 
					 | 
				
			||||||
distinguished_name = dn
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ dn ]
 | 
					 | 
				
			||||||
CN = $domain
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ req_ext ]
 | 
					 | 
				
			||||||
subjectAltName = @alt_names
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ alt_names ]
 | 
					 | 
				
			||||||
DNS.1 = $domain
 | 
					 | 
				
			||||||
DNS.2 = www.$domain
 | 
					 | 
				
			||||||
DNS.3 = api.$domain") \
 | 
					 | 
				
			||||||
    -out $csrfile
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rndcsr() {
 | 
					 | 
				
			||||||
  keysize=$1
 | 
					 | 
				
			||||||
	openssl genrsa -out fixtures/valid.pkcs1.1.pem $keysize
 | 
					 | 
				
			||||||
  rasha fixtures/valid.pkcs1.1.pem > fixtures/test.jwk.1.json
 | 
					 | 
				
			||||||
  gencsr3 fixtures/valid.pkcs1.1.pem whatever.net fixtures/valid.csr.1.pem
 | 
					 | 
				
			||||||
  node bin/rsa-csr.js fixtures/test.jwk.1.json whatever.net,www.whatever.net,api.whatever.net \
 | 
					 | 
				
			||||||
    > fixtures/test.csr.1.pem
 | 
					 | 
				
			||||||
  diff fixtures/valid.csr.1.pem fixtures/test.csr.1.pem
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo ""
 | 
					 | 
				
			||||||
echo "Generating CSR for example.com,www.example.com"
 | 
					 | 
				
			||||||
gencsr2 fixtures/privkey-rsa-2048.pkcs1.pem example.com fixtures/example.com-www.csr.pem
 | 
					 | 
				
			||||||
node bin/rsa-csr.js fixtures/privkey-rsa-2048.jwk.json example.com,www.example.com \
 | 
					 | 
				
			||||||
  > fixtures/example.com-www.csr.1.pem
 | 
					 | 
				
			||||||
diff fixtures/example.com-www.csr.pem fixtures/example.com-www.csr.1.pem
 | 
					 | 
				
			||||||
echo "Pass"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo ""
 | 
					 | 
				
			||||||
echo "Generating CSR for whatever.net,www.whatever.net,api.whatever.net"
 | 
					 | 
				
			||||||
gencsr3 fixtures/privkey-rsa-2048.pkcs1.pem whatever.net fixtures/whatever.net-www-api.csr.pem
 | 
					 | 
				
			||||||
node bin/rsa-csr.js fixtures/privkey-rsa-2048.jwk.json whatever.net,www.whatever.net,api.whatever.net \
 | 
					 | 
				
			||||||
  > fixtures/whatever.net-www-api.csr.1.pem
 | 
					 | 
				
			||||||
diff fixtures/whatever.net-www-api.csr.pem fixtures/whatever.net-www-api.csr.1.pem
 | 
					 | 
				
			||||||
echo "Pass"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo ""
 | 
					 | 
				
			||||||
echo "Generating random keys of various lengths and re-running tests for each"
 | 
					 | 
				
			||||||
rndcsr 3072
 | 
					 | 
				
			||||||
rndcsr 1024
 | 
					 | 
				
			||||||
rndcsr 512 # minimum size that can reasonably work
 | 
					 | 
				
			||||||
echo "Pass"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rm fixtures/*.1.*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo ""
 | 
					 | 
				
			||||||
echo "All tests passed!"
 | 
					 | 
				
			||||||
echo "  • Fixture CSRs built and do not differ from OpenSSL-generated CSRs"
 | 
					 | 
				
			||||||
echo "  • Random keys and CSRs are also correct"
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user