2019-05-07 07:52:33 +00:00
|
|
|
# Bluecrypt™ [ACME.js](https://git.rootprojects.org/root/bluecrypt-acme.js) | A [Root](https://rootprojects.org/acme/) project
|
2019-04-18 06:20:51 +00:00
|
|
|
|
2019-05-07 07:52:33 +00:00
|
|
|
Free SSL Certificates from Let's Encrypt, right in your Web Browser
|
2019-04-18 06:20:51 +00:00
|
|
|
|
2019-05-07 07:52:33 +00:00
|
|
|
Lightweight. Fast. Modern Crypto. Zero dependecies.
|
|
|
|
|
|
|
|
(a port of [acme.js](https://git.coolaj86.com/coolaj86/acme-v2.js) to the browser)
|
|
|
|
|
|
|
|
# Features
|
|
|
|
|
|
|
|
| 15k gzipped | 55k minified | 88k (2,500 loc) source with comments |
|
|
|
|
|
|
|
|
* [x] Let's Encrypt
|
|
|
|
* [x] ACME draft 15 (supports POST-as-GET)
|
|
|
|
* [x] Secure support for EC and RSA for account and server keys
|
|
|
|
* [x] Simple and lightweight PEM, DER, ASN1, X509, and CSR implementations
|
|
|
|
* [x] VanillaJS, Zero Dependencies
|
|
|
|
|
|
|
|
# Online Demos
|
|
|
|
|
|
|
|
* Greenlock for the Web <https://greenlock.domains>
|
|
|
|
* Bluecrypt ACME Demo <https://rootprojects.org/acme/>
|
|
|
|
|
|
|
|
We expect that our hosted versions will meet all of yours needs.
|
|
|
|
If they don't, please open an issue to let us know why.
|
|
|
|
|
|
|
|
We'd much rather improve the app than have a hundred different versions running in the wild.
|
|
|
|
However, in keeping to our values we've made the source visible for others to inspect, improve, and modify.
|
|
|
|
|
|
|
|
# QuickStart
|
|
|
|
|
|
|
|
Bluecrypt ACME embeds [Keypairs.js](https://git.rootprojects.org/root/bluecrypt-keypairs.js)
|
|
|
|
and [CSR.js](https://git.rootprojects.org/root/bluecrypt-csr.js)
|
|
|
|
|
|
|
|
`bluecrypt-acme.js`
|
|
|
|
```html
|
|
|
|
<script src="https://rootprojects.org/acme/bluecrypt-acme.js"></script>
|
|
|
|
```
|
|
|
|
|
|
|
|
`bluecrypt-acme.min.js`
|
|
|
|
```html
|
|
|
|
<script src="https://rootprojects.org/acme/bluecrypt-acme.min.js"></script>
|
|
|
|
```
|
|
|
|
|
|
|
|
You can see `index.html` and `app.js` in the repo for full example usage.
|
|
|
|
|
|
|
|
### Instantiate Bluecrypt ACME
|
|
|
|
|
|
|
|
Although built for Let's Encrypt, Bluecrypt ACME will work with any server
|
|
|
|
that supports draft-15 of the ACME spec (includes POST-as-GET support).
|
|
|
|
|
|
|
|
The `init()` method takes a _directory url_ and initializes internal state according to its response.
|
|
|
|
|
|
|
|
```js
|
|
|
|
var acme = ACME.create({});
|
|
|
|
acme.init('https://acme-staging-v02.api.letsencrypt.org/directory').then(function () {
|
|
|
|
// Ready to use, show page
|
|
|
|
$('body').hidden = false;
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
### Create ACME Account with Let's Encrypt
|
|
|
|
|
|
|
|
ACME Accounts are key and device based, with an email address as a backup identifier.
|
|
|
|
|
|
|
|
A public account key must be registered before an SSL certificate can be requested.
|
|
|
|
|
|
|
|
```js
|
|
|
|
var accountPrivateKey;
|
|
|
|
var account;
|
|
|
|
|
|
|
|
Keypairs.generate({ kty: 'EC' }).then(function (pair) {
|
|
|
|
accountPrivateKey = pair.private;
|
|
|
|
|
|
|
|
return acme.accounts.create({
|
|
|
|
agreeToTerms: function (tos) {
|
|
|
|
if (window.confirm("Do you agree to the Bluecrypt and Let's Encrypt Terms of Service?")) {
|
|
|
|
return Promise.resolve(tos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
, accountKeypair: { privateKeyJwk: pair.private }
|
|
|
|
, email: $('.js-email-input').value
|
|
|
|
}).then(function (_account) {
|
|
|
|
account = _account;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
### Get Free 90-day SSL Certificate
|
|
|
|
|
|
|
|
Creating an ACME "order" for a 90-day SSL certificate requires use of the account private key,
|
|
|
|
the names of domains to be secured, and a distinctly separate server private key.
|
|
|
|
|
|
|
|
A domain ownership verification "challenge" (uploading a file to an unsecured HTTP url or setting a DNS record)
|
|
|
|
is a required part of the process, which requires `set` and `remove` callbacks/promises.
|
|
|
|
|
|
|
|
```js
|
|
|
|
var serverPrivateKey;
|
|
|
|
|
|
|
|
Keypairs.generate({ kty: 'EC' }).then(function (pair) {
|
|
|
|
serverPrivateKey = pair.private;
|
|
|
|
|
|
|
|
return acme.certificates.create({
|
|
|
|
agreeToTerms: function (tos) {
|
|
|
|
return tos;
|
|
|
|
}
|
|
|
|
, account: account
|
|
|
|
, accountKeypair: { privateKeyJwk: accountPrivateKey }
|
|
|
|
, serverKeypair: { privateKeyJwk: serverPrivateKey }
|
|
|
|
, domains: ['example.com','www.example.com']
|
|
|
|
, challenges: challenges // must be implemented
|
|
|
|
, skipDryRun: true
|
|
|
|
}).then(function (results) {
|
|
|
|
console.log('Got SSL Certificate:');
|
|
|
|
console.log(results.expires);
|
|
|
|
console.log(results.cert);
|
|
|
|
console.log(results.chain);
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
### Example "Challenge" Implementation
|
|
|
|
|
|
|
|
Typically here you're just presenting some sort of dialog to the user to ask them to
|
|
|
|
upload a file or set a DNS record.
|
|
|
|
|
|
|
|
It may be possible to do something fancy like using OAuth2 to login to Google Domanis
|
|
|
|
to set a DNS address, etc, but it seems like that sort of fanciness is probably best
|
|
|
|
reserved for server-side plugins.
|
|
|
|
|
|
|
|
```js
|
|
|
|
var challenges = {
|
|
|
|
'http-01': {
|
|
|
|
set: function (opts) {
|
|
|
|
console.info('http-01 set challenge:');
|
|
|
|
console.info(opts.challengeUrl);
|
|
|
|
console.info(opts.keyAuthorization);
|
|
|
|
while (!window.confirm("Upload the challenge file before continuing.")) {}
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
, remove: function (opts) {
|
|
|
|
console.log('http-01 remove challenge:', opts.challengeUrl);
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
# Full Documentation
|
|
|
|
|
|
|
|
See [acme.js](https://git.coolaj86.com/coolaj86/acme-v2.js).
|
|
|
|
|
|
|
|
Aside from the loading instructions (`npm` and `require` instead of `script` tags),
|
|
|
|
the usage is identical to the node version.
|
|
|
|
|
|
|
|
That said, the two may leap-frog a little from time to time
|
|
|
|
(for example, the browser version is just a touch ahead at the moment).
|
|
|
|
|
|
|
|
# Developing
|
|
|
|
|
|
|
|
You can see `<script>` tags in the `index.html` in the repo, which references the original
|
|
|
|
source files.
|
|
|
|
|
|
|
|
Join `@rootprojects` `#general` on [Keybase](https://keybase.io) if you'd like to chat with us.
|
|
|
|
|
|
|
|
# Commercial Support
|
|
|
|
|
|
|
|
We have both commercial support and commercial licensing available.
|
|
|
|
|
|
|
|
You're welcome to [contact us](mailto:aj@therootcompany.com) in regards to IoT, On-Prem,
|
|
|
|
Enterprise, and Internal installations, integrations, and deployments.
|
|
|
|
|
|
|
|
We also offer consulting for all-things-ACME and Let's Encrypt.
|
|
|
|
|
|
|
|
# Legal & Rules of the Road
|
|
|
|
|
|
|
|
Bluecrypt™ and Greenlock™ are [trademarks](https://rootprojects.org/legal/#trademark) of AJ ONeal
|
|
|
|
|
|
|
|
The rule of thumb is "attribute, but don't confuse". For example:
|
|
|
|
|
2019-05-09 20:55:41 +00:00
|
|
|
> Built with [Bluecrypt ACME](https://git.rootprojects.org/root/bluecrypt-acme.js) (a [Root](https://rootprojects.org) project).
|
2019-05-07 07:52:33 +00:00
|
|
|
|
|
|
|
Please [contact us](mailto:aj@therootcompany.com) if have any questions in regards to our trademark,
|
2019-05-09 20:57:17 +00:00
|
|
|
attribution, and/or visible source policies. We want to build great software and a great community.
|
2019-05-07 07:52:33 +00:00
|
|
|
|
|
|
|
[bluecrypt.js](https://git.coolaj86.com/coolaj86/bluecrypt.js) |
|
|
|
|
MPL-2.0 |
|
|
|
|
[Terms of Use](https://therootcompany.com/legal/#terms) |
|
|
|
|
[Privacy Policy](https://therootcompany.com/legal/#privacy)
|