npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

letiny-core

v2.0.3

Published

A framework for building letsencrypt clients, forked from letiny

Downloads

287

Readme

le-acme-core

Looking for letiny-core? Check the v1.x branch.

A framework for building letsencrypt clients, forked from letiny.

Supports all of:

  • node with ursa (works fast)
  • node with forge (works on windows)
  • browser WebCrypto (not implemented, but... Let's Encrypt over WebRTC anyone?)
  • any javascript implementation

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:

Install & Usage:

npm install --save le-acme-core

To use the default dependencies:

'use strict';

var ACME = require('le-acme-core').ACME.create();

For testing and development, you can also inject the dependencies you want to use:

'use strict';

var ACME = require('le-acme-core').ACME.create({
  request: require('request')
, RSA: require('rsa-compat').RSA
});

// now uses node `request` (could also use jQuery or Angular in the browser)
ACME.getAcmeUrls(discoveryUrl, function (err, urls) {
  console.log(urls);
});

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
  • 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

Demo

You can see this working for yourself, but you'll need to be on an internet connected computer with a domain.

Get a temporary domain for testing

npm install -g ddns-cli
ddns --random --email [email protected] --agree

Note: use YOUR EMAIL and accept the terms of service (run ddns --help to see them).

Install le-acme-core and its dependencies. Note: it's okay if you're on windows and ursa fails to compile. It'll still work.

git clone https://github.com/Daplie/le-acme-core.git ~/le-acme-core
pushd ~/le-acme-core

npm install

Run the demo:

node examples/letsencrypt.js [email protected] example.com

Note: use YOUR TEMPORARY DOMAIN and YOUR EMAIL.

API

The Goodies

// Accounts
ACME.registerNewAccount(options, cb)        // returns "regr" registration data

    { newRegUrl: '<url>'                      //    no defaults, specify acmeUrls.newAuthz
    , email: '<email>'                        //    valid email (server checks MX records)
    , accountKeypair: {                       //    privateKeyPem or privateKeyJwt
        privateKeyPem: '<ASCII PEM>'
      }
    , agreeToTerms: fn (tosUrl, cb) {}        //    must specify agree=tosUrl to continue (or falsey to end)
    }

// Registration
ACME.getCertificate(options, cb)            // returns (err, pems={ privkey (key), cert, chain (ca) })

    { newAuthzUrl: '<url>'                    //    specify acmeUrls.newAuthz
    , newCertUrl: '<url>'                     //    specify acmeUrls.newCert

    , domainKeypair: {
        privateKeyPem: '<ASCII PEM>'
      }
    , accountKeypair: {
        privateKeyPem: '<ASCII PEM>'
      }
    , domains: ['example.com']

    , setChallenge: fn (hostname, key, val, cb)
    , removeChallenge: fn (hostname, key, cb)
    }

// Discovery URLs
ACME.getAcmeUrls(acmeDiscoveryUrl, cb)      // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert})

Helpers & Stuff

// Constants
ACME.productionServerUrl                // https://acme-v01.api.letsencrypt.org/directory
ACME.stagingServerUrl                   // https://acme-staging.api.letsencrypt.org/directory
ACME.acmeChallengePrefix                // /.well-known/acme-challenge/
ACME.knownEndpoints                     // new-authz, new-cert, new-reg, revoke-cert


// HTTP Client Helpers
ACME.Acme                               // Signs requests with JWK
    acme = new Acme(keypair)                // 'keypair' is an object with `privateKeyPem` and/or `privateKeyJwk`
    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

Example

Below you'll find a stripped-down example. You can see the full example in the example folder.

Register Account & Domain

This is how you register an ACME account and get an HTTPS certificate

'use strict';

var ACME = require('le-acme-core').ACME.create();
var RSA = require('rsa-compat').RSA;

var email = '[email protected]';                   // CHANGE TO YOUR EMAIL
var domains = 'example.com';                      // CHANGE TO YOUR DOMAIN
var acmeDiscoveryUrl = ACME.stagingServerUrl;   // CHANGE to production, when ready

var accountKeypair = null;                        // { privateKeyPem: null, privateKeyJwk: null };
var domainKeypair = null;                         // same as above
var acmeUrls = null;

RSA.generateKeypair(2048, 65537, function (err, keypair) {
    accountKeypair = keypair;
    // ...
    ACME.getAcmeUrls(acmeDiscoveryUrl, function (err, urls) {
        // ...
        runDemo();
    });
});

function runDemo() {
    ACME.registerNewAccount(
        { newRegUrl: acmeUrls.newReg
        , email: email
        , accountKeypair: accountKeypair
        , agreeToTerms: function (tosUrl, done) {

              // agree to the exact version of these terms
              done(null, tosUrl);
          }
        }
      , function (err, regr) {

            ACME.getCertificate(
                { newAuthzUrl: acmeUrls.newAuthz
                , newCertUrl: acmeUrls.newCert

                , domainKeypair: domainKeypair
                , accountKeypair: accountKeypair
                , domains: domains

                , setChallenge: challengeStore.set
                , removeChallenge: challengeStore.remove
                }
              , function (err, certs) {

                  // Note: you should save certs to disk (or db)
                  certStore.set(domains[0], certs, function () {

                      // ...

                  });

                }
            );
        }
    );
}

But wait, there's more! See example/letsencrypt.js

Run a Server on 80, 443, and 5001 (https/tls)

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

var https = require('https');
var http = require('http');


var LeCore = deps.LeCore;
var httpsOptions = deps.httpsOptions;
var challengeStore = deps.challengeStore;
var certStore = deps.certStore;


//
// Challenge Handler
//
function acmeResponder(req, res) {
  if (0 !== req.url.indexOf(LeCore.acmeChallengePrefix)) {
    res.end('Hello World!');
    return;
  }

  var key = req.url.slice(LeCore.acmeChallengePrefix.length);

  challengeStore.get(req.hostname, key, function (err, val) {
    res.end(val || 'Error');
  });
}


//
// Server
//
https.createServer(httpsOptions, acmeResponder).listen(5001, function () {
  console.log('Listening https on', this.address());
});
http.createServer(acmeResponder).listen(80, function () {
  console.log('Listening http on', this.address());
});

But wait, there's more! See example/serve.js

Put some storage in place

Finally, you need an implementation of challengeStore:

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);
  }
};

var certCache = {};
var certStore = {
  set: function (hostname, certs, cb) {
    certCache[hostname] = certs;
    cb(null);
  }
, get: function (hostname, cb) {
    cb(null, certCache[hostname]);
  }
, remove: function (hostname, cb) {
    delete certCache[hostname];
    cb(null);
  }
};

But wait, there's more! See

Authors

  • ISRG
  • Anatol Sommer (https://github.com/anatolsommer)
  • AJ ONeal [email protected] (https://daplie.com)

Licence

MPL 2.0

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 and Apache-2.0 as well (check file headers).