Package detail

selfsigned

jfromaniello66.8mMIT5.2.0

Generate self signed certificates private and public keys

openssl, self, signed, certificates

readme

selfsigned

Generate self-signed X.509 certificates using Node.js native crypto.

Install

npm install selfsigned

Requirements

  • Node.js >= 15.6.0 (for native WebCrypto support)

Usage

Version 5.0 is async-only. The generate() function now returns a Promise.

const selfsigned = require('selfsigned');

const attrs = [{ name: 'commonName', value: 'contoso.com' }];
const pems = await selfsigned.generate(attrs);
console.log(pems);

Output

{
  private: '-----BEGIN PRIVATE KEY-----\n...',
  public: '-----BEGIN PUBLIC KEY-----\n...',
  cert: '-----BEGIN CERTIFICATE-----\n...',
  fingerprint: 'XX:XX:XX:...'
}

Options

const pems = await selfsigned.generate(null, {
  keySize: 2048, // the size for the private key in bits (default: 2048)
  notBeforeDate: new Date(), // start of certificate validity (default: now)
  notAfterDate: new Date('2026-01-01'), // end of certificate validity (default: notBeforeDate + 365 days)
  algorithm: 'sha256', // sign the certificate with specified algorithm (default: 'sha1')
  extensions: [{ name: 'basicConstraints', cA: true }], // certificate extensions array
  clientCertificate: true, // generate client cert (default: false) - can also be an options object
  ca: { key: '...', cert: '...' }, // CA key and cert for signing (default: self-signed)
  passphrase: 'secret' // encrypt the private key with a passphrase (default: none)
});

Setting Custom Validity Period

Use notBeforeDate and notAfterDate to control certificate validity:

// Using date-fns
const { addDays, addYears } = require('date-fns');

const pems = await selfsigned.generate(null, {
  notBeforeDate: new Date(),
  notAfterDate: addDays(new Date(), 30) // Valid for 30 days
});

// Or with vanilla JS
const notBefore = new Date();
const notAfter = new Date(notBefore);
notAfter.setFullYear(notAfter.getFullYear() + 2); // Valid for 2 years

const pems = await selfsigned.generate(null, {
  notBeforeDate: notBefore,
  notAfterDate: notAfter
});

Supported Algorithms

  • sha1 (default)
  • sha256
  • sha384
  • sha512

Using Your Own Keys

You can avoid key pair generation by specifying your own keys:

const pems = await selfsigned.generate(null, {
  keyPair: {
    publicKey: '-----BEGIN PUBLIC KEY-----...',
    privateKey: '-----BEGIN PRIVATE KEY-----...'
  }
});

Encrypting the Private Key

You can encrypt the private key with a passphrase using AES-256-CBC:

const pems = await selfsigned.generate(null, {
  passphrase: 'my-secret-passphrase'
});

// The private key will be in encrypted PKCS#8 format:
// -----BEGIN ENCRYPTED PRIVATE KEY-----
// ...
// -----END ENCRYPTED PRIVATE KEY-----

To use the encrypted key, provide the passphrase:

const crypto = require('crypto');

// Decrypt the key
const privateKey = crypto.createPrivateKey({
  key: pems.private,
  passphrase: 'my-secret-passphrase'
});

// Or use directly with HTTPS server
const https = require('https');
https.createServer({
  key: pems.private,
  passphrase: 'my-secret-passphrase',
  cert: pems.cert
}, app).listen(443);

Signing with a CA

You can generate certificates signed by an existing Certificate Authority instead of self-signed certificates. This is useful for development environments where you want browsers to trust your certificates.

const fs = require('fs');
const selfsigned = require('selfsigned');

const pems = await selfsigned.generate([
  { name: 'commonName', value: 'localhost' }
], {
  algorithm: 'sha256',
  ca: {
    key: fs.readFileSync('/path/to/ca.key', 'utf8'),
    cert: fs.readFileSync('/path/to/ca.crt', 'utf8')
  }
});

The generated certificate will be signed by the provided CA and will include:

  • Subject Alternative Name (SAN) extension with DNS name matching the commonName
  • For localhost, an additional IP SAN for 127.0.0.1
  • Key Usage: digitalSignature, keyEncipherment
  • Extended Key Usage: serverAuth, clientAuth

Using with mkcert

mkcert is a simple tool for making locally-trusted development certificates. Combining it with selfsigned provides an excellent developer experience:

  • No certificate files to manage - generate trusted certificates on-the-fly at server startup
  • No git-ignored cert files - nothing to store, share, or accidentally commit
  • Browsers trust the certificates automatically - no security warnings during development
const https = require('https');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const selfsigned = require('selfsigned');

// Get mkcert's CA (requires: brew install mkcert && mkcert -install)
const caroot = execSync('mkcert -CAROOT', { encoding: 'utf8' }).trim();

const pems = await selfsigned.generate([
  { name: 'commonName', value: 'localhost' }
], {
  algorithm: 'sha256',
  ca: {
    key: fs.readFileSync(path.join(caroot, 'rootCA-key.pem'), 'utf8'),
    cert: fs.readFileSync(path.join(caroot, 'rootCA.pem'), 'utf8')
  }
});

// Start server with browser-trusted certificate - no files written to disk
https.createServer({ key: pems.private, cert: pems.cert }, app).listen(443);

See examples/https-server-mkcert.js for a complete working example.

Attributes

Attributes follow the X.509 standard:

const attrs = [
  { name: 'commonName', value: 'example.org' },
  { name: 'countryName', value: 'US' },
  { shortName: 'ST', value: 'Virginia' },
  { name: 'localityName', value: 'Blacksburg' },
  { name: 'organizationName', value: 'Test' },
  { shortName: 'OU', value: 'Test' }
];

Generate Client Certificates

For environments where servers require client certificates, you can generate client keys signed by the original (server) key:

const pems = await selfsigned.generate(null, { clientCertificate: true });
console.log(pems);

Output includes additional client certificate fields:

{
  private: '-----BEGIN PRIVATE KEY-----\n...',
  public: '-----BEGIN PUBLIC KEY-----\n...',
  cert: '-----BEGIN CERTIFICATE-----\n...',
  fingerprint: 'XX:XX:XX:...',
  clientprivate: '-----BEGIN PRIVATE KEY-----\n...',
  clientpublic: '-----BEGIN PUBLIC KEY-----\n...',
  clientcert: '-----BEGIN CERTIFICATE-----\n...'
}

Client Certificate Options

The clientCertificate option can be true for defaults, or an options object for full control:

const pems = await selfsigned.generate(null, {
  clientCertificate: {
    cn: 'jdoe',                              // common name (default: 'John Doe jdoe123')
    keySize: 4096,                           // key size in bits (default: 2048)
    algorithm: 'sha256',                     // signature algorithm (default: inherits from parent or 'sha1')
    notBeforeDate: new Date(),               // validity start (default: now)
    notAfterDate: new Date('2026-01-01')     // validity end (default: notBeforeDate + 1 year)
  }
});

Simple example with just a custom CN:

const pems = await selfsigned.generate(null, {
  clientCertificate: { cn: 'FooBar' }
});

PKCS#7 Support

PKCS#7 formatting is available through a separate module for better tree-shaking:

const selfsigned = require('selfsigned');
const { createPkcs7 } = require('selfsigned/pkcs7');

const pems = await selfsigned.generate(attrs);
const pkcs7 = createPkcs7(pems.cert);
console.log(pkcs7); // PKCS#7 formatted certificate

You can also create PKCS#7 for client certificates:

const pems = await selfsigned.generate(null, { clientCertificate: true });
const clientPkcs7 = createPkcs7(pems.clientcert);

Migration from v4.x

Version 5.0 introduces breaking changes:

Breaking Changes

  1. Async-only API: The generate() function is now async and returns a Promise. Synchronous generation is no longer supported.
  2. No callback support: Callbacks have been removed. Use async/await or .then().
  3. Minimum Node.js version: Now requires Node.js >= 15.6.0 (was >= 10).
  4. Dependencies: Replaced node-forge with @peculiar/x509 and pkijs (66% smaller bundle size).
  5. days option removed: Use notAfterDate instead. Default validity is 365 days from notBeforeDate.

Migration Examples

Old (v4.x):

// Sync
const pems = selfsigned.generate(attrs, { days: 365 });

// Callback
selfsigned.generate(attrs, { days: 365 }, function(err, pems) {
  if (err) throw err;
  console.log(pems);
});

New (v5.x):

// Async/await (default 365 days validity)
const pems = await selfsigned.generate(attrs);

// Custom validity with notAfterDate
const notAfter = new Date();
notAfter.setDate(notAfter.getDate() + 30); // 30 days
const pems = await selfsigned.generate(attrs, { notAfterDate: notAfter });

// Or with .then()
selfsigned.generate(attrs)
  .then(pems => console.log(pems))
  .catch(err => console.error(err));

License

MIT

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[5.0.0] - 2025-11-26

🚀 Major Rewrite

Complete rewrite replacing node-forge with modern @peculiar/x509 and pkijs libraries.

✨ Added

  • Native WebCrypto API support for better performance and security
  • TypeScript examples in documentation
  • Async/await support as the primary API
  • Support for keyPair option to use existing keys
  • Updated to use Node.js native crypto for all operations
  • Separate selfsigned/pkcs7 module for tree-shakeable PKCS#7 support

💥 BREAKING CHANGES

  1. Async-only API: The generate() function now returns a Promise. Synchronous generation has been removed.

    // Old (v4.x)
    const pems = selfsigned.generate(attrs, options);
    
    // New (v5.x)
    const pems = await selfsigned.generate(attrs, options);
  2. No callback support: Callbacks have been completely removed in favor of Promises.

    // Old (v4.x)
    selfsigned.generate(attrs, options, function(err, pems) { ... });
    
    // New (v5.x)
    const pems = await selfsigned.generate(attrs, options);
  3. Minimum Node.js version: Now requires Node.js >= 15.6.0 (was >= 10)

    • Required for native WebCrypto support
  4. Dependencies changed:

    • ❌ Removed: node-forge (1.64 MB)
    • ✅ Added: @peculiar/x509 (551 KB) - 66% smaller!
    • ✅ Added: pkijs (1.94 MB, only for PKCS#7 support)
    • Bundle size reduced by 66% when not using PKCS#7
  5. PKCS#7 API changed:

    • Old: const pems = await generate(attrs, { pkcs7: true }); pems.pkcs7
    • New: const { createPkcs7 } = require('selfsigned/pkcs7'); const pkcs7 = createPkcs7(pems.cert);
    • PKCS#7 is now a separate module for better tree-shaking

🔧 Changed

  • Default key size remains 2048 bits (was incorrectly documented as 1024)
  • PEM output uses \n line endings (was \r\n)
  • Private keys now use PKCS#8 format (BEGIN PRIVATE KEY instead of BEGIN RSA PRIVATE KEY)
  • Certificate generation is now fully async using native WebCrypto
  • PKCS#7 is now tree-shakeable: Moved to separate selfsigned/pkcs7 module so bundlers can exclude it when not used

🐛 Fixed

  • Default key size documentation corrected from 1024 to 2048 bits
  • Improved error handling for certificate generation failures

📦 Dependencies

Removed:

  • node-forge@^1.3.1
  • @types/node-forge@^1.3.0

Added:

  • @peculiar/x509@^1.14.2 (required)
  • pkijs@^3.3.3 (required, but tree-shakeable via separate selfsigned/pkcs7 module)

🔒 Security

  • Now uses Node.js native WebCrypto API instead of JavaScript implementation
  • Better integration with platform security features
  • More secure random number generation

📚 Documentation

  • Complete README rewrite with async/await examples
  • Added migration guide from v4.x to v5.x
  • Updated all code examples to use async/await
  • Added requirements section highlighting Node.js version requirement

[4.0.0] - Previous Release

See git history for changes in 4.x and earlier versions.