le-acme-core.js/README.md

205 lines
6.3 KiB
Markdown
Raw Normal View History

2015-12-15 22:07:02 +00:00
# letiny-core
2015-12-13 16:50:04 +00:00
2015-12-15 22:07:02 +00:00
A framework for building letsencrypt clients, forked from `letiny`.
* node with `ursa` (works fast)
2015-12-16 00:58:36 +00:00
* node with `forge` (works on windows)
* browser WebCrypto (not implemented, but on the TODO)
2015-12-15 22:07:02 +00:00
* any javascript implementation
2015-12-13 16:50:04 +00:00
2015-12-16 01:33:05 +00:00
### These aren't the droids you're looking for
This is a library / framework for building letsencrypt clients.
You probably want one of these pre-built clients instead:
* `letsencrypt` (100% compatible with the official client)
* `letiny` (lightweight client)
* `letsencrypt-express` (automatic https for express)
2015-12-13 16:50:04 +00:00
## Usage:
2015-12-15 22:07:02 +00:00
```bash
npm install --save letiny-core
2015-12-13 16:50:04 +00:00
```
2015-12-16 00:58:36 +00:00
You will follow these steps to obtain certificates:
* discover ACME registration urls with `getAcmeUrls`
* register a user account with `registerNewAccount`
* implement a method to agree to the terms of service as `agreeToTos`
* get certificates with `getCertificate`
2015-12-16 01:18:40 +00:00
* implement a method to store the challenge token as `setChallenge`
* implement a method to get the challenge token as `getChallenge`
* implement a method to remove the challenge token as `removeChallenge`
2015-12-16 00:58:36 +00:00
2015-12-15 22:07:02 +00:00
```javascript
'use strict';
2015-12-16 00:51:44 +00:00
var LeCore = require('letiny-core');
2015-12-16 01:18:40 +00:00
2015-12-16 00:51:44 +00:00
var accountPrivateKeyPem = '...'; // leCrypto.generateRsaKeypair(bitLen, exp, cb)
var domainPrivateKeyPem = '...'; // (same)
2015-12-16 01:18:40 +00:00
var challengeStore = { /*get, set, remove*/ }; // see below for example
2015-12-16 00:51:44 +00:00
LeCore.getAcmeUrls(
LeCore.stagingServerUrl // or choose LeCore.productionServerUrl
, function (err, urls) {
LeCore.registerNewAccount(
{ newRegUrl: urls.newReg
, email: 'user@example.com'
, accountPrivateKeyPem: accountPrivateKeyPem
, agreeToTerms: function (tosUrl, done) {
// agree to these exact terms
done(null, tosUrl);
}
}
, function (err, regr) {
// Note: you should save the registration
// record to disk (or db)
LeCore.getCertificate(
2015-12-16 01:18:40 +00:00
{ domainPrivateKeyPem: domainPrivateKeyPem
, accountPrivateKeyPem: accountPrivateKeyPem
, setChallenge: challengeStore.set
, removeChallenge: challengeStore.remove
2015-12-16 00:51:44 +00:00
}
, function (err, certs) {
// Note: you should save certs to disk (or db)
}
)
}
);
}
);
2015-12-13 16:50:04 +00:00
```
2015-12-16 01:18:40 +00:00
That will fail unless you have a webserver running on 80 and 443 (or 5001)
to respond to `/.well-known/acme-challenge/xxxxxxxx` with the proper token
```javascript
var localCerts = require('localhost.daplie.com-certificates'); // needs default certificates
var http = require('http');
var httsp = require('https');
function acmeResponder(req, res) {
if (0 !== req.url.indexOf(LeCore.acmeChallengePrefixUrl)) {
res.end('Hello World!');
return;
}
LeCore.
}
http.createServer()
```
Finally, you need an implementation of `challengeStore`:
```javascript
var challengeCache = {};
var challengeStore = {
set: function (hostname, key, value, cb) {
challengeCache[key] = value;
cb(null);
}
, get: function (hostname, key, cb) {
cb(null, challengeCache[key]);
}
, remove: function (hostname, key, cb) {
delete challengeCache[key];
cb(null);
}
};
```
2015-12-15 22:07:02 +00:00
## API
2015-12-16 00:51:44 +00:00
The Goodies
2015-12-16 00:13:07 +00:00
```javascript
2015-12-16 00:51:44 +00:00
{ newRegUrl: '...' // no defaults, specify LeCore.nproductionServerUrl
// Accounts
LeCore.registerNewAccount(options, cb) // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert})
{ newRegUrl: '...' // no defaults, specify LeCore.newAuthz
, email: '...' // valid email (server checks MX records)
, agreeToTerms: fn (tosUrl, cb) {} // callback to allow user interaction for tosUrl
// cb(err=null, agree=tosUrl) // must specify agree=tosUrl to continue (or falsey to end)
}
// Registration
LeCore.getCertificate(options, cb)
{ newAuthzUrl: '...' // no defaults, specify acmeUrls.newAuthz
```
2015-12-15 22:07:02 +00:00
2015-12-16 00:51:44 +00:00
Helpers & Stuff
2015-12-15 22:07:02 +00:00
2015-12-16 00:51:44 +00:00
```javascript
// Constants
LeCore.productionServerUrl // https://acme-v01.api.letsencrypt.org/directory
LeCore.stagingServerUrl // https://acme-staging.api.letsencrypt.org/directory
2015-12-16 01:18:40 +00:00
LeCore.acmeChallengePrefix // /.well-known/acme-challenge/
2015-12-16 00:51:44 +00:00
LeCore.configDir // /etc/letsencrypt/
LeCore.logsDir // /var/log/letsencrypt/
LeCore.workDir // /var/lib/letsencrypt/
2015-12-16 02:51:38 +00:00
LeCore.knownEndpoints // new-authz, new-cert, new-reg, revoke-cert
2015-12-16 00:51:44 +00:00
// HTTP Client Helpers
2015-12-16 00:13:07 +00:00
LeCore.Acme // Signs requests with JWK
acme = new Acme(lePrivateKey) // privateKey format is abstract
acme.post(url, body, cb) // POST with signature
acme.parseLinks(link) // (internal) parses 'link' header
acme.getNonce(url, cb) // (internal) HEAD request to get 'replay-nonce' strings
2015-12-15 22:07:02 +00:00
2015-12-16 00:51:44 +00:00
// Note: some of these are not async,
// but they will be soon. Don't rely
// on their API yet.
// Crypto Helpers
2015-12-15 22:07:02 +00:00
LeCore.leCrypto
2015-12-16 00:51:44 +00:00
generateRsaKeypair(bitLen, exponent, cb); // returns { privateKeyPem, privateKeyJwk, publicKeyPem, publicKeyMd5 }
thumbprint(lePubKey) // generates public key thumbprint
2015-12-16 00:13:07 +00:00
generateSignature(lePrivKey, bodyBuf, nonce) // generates a signature
2015-12-16 00:51:44 +00:00
privateJwkToPems(jwk) // { n: '...', e: '...', iq: '...', ... } to PEMs
privatePemToJwk // PEM to JWK (see line above)
importPemPrivateKey(privateKeyPem) // (internal) returns abstract private key
2015-12-13 16:50:04 +00:00
```
2015-12-15 22:07:02 +00:00
For testing and development, you can also inject the dependencies you want to use:
```javascript
2015-12-16 00:51:44 +00:00
LeCore = LeCore.create({
2015-12-15 22:07:02 +00:00
request: require('request')
, leCrypto: rquire('./lib/letsencrypt-forge')
});
2015-12-16 00:51:44 +00:00
// now uses node `request` (could also use jQuery or Angular in the browser)
2015-12-16 00:58:36 +00:00
LeCore.getAcmeUrls(discoveryUrl, function (err, urls) {
console.log(urls);
2015-12-16 00:51:44 +00:00
});
2015-12-15 22:07:02 +00:00
```
2015-12-13 16:50:04 +00:00
2015-12-16 01:18:40 +00:00
## Authors
* ISRG
* Anatol Sommer (https://github.com/anatolsommer)
* AJ ONeal <aj@daplie.com> (https://daplie.com)
2015-12-13 16:50:04 +00:00
## Licence
2015-12-15 22:07:02 +00:00
2015-12-13 16:50:04 +00:00
MPL 2.0
2015-12-15 22:07:02 +00:00
All of the code is available under the MPL-2.0.
Some of the files are original work not modified from `letiny`
and are made available under MIT as well (check file headers).