UNPKG

selfsigned

Version:

Generate self signed certificates private and public keys

243 lines (197 loc) 8.89 kB
var { assert } = require('chai'); var crypto = require('crypto'); describe('CA signing', function () { var generate = require('../index').generate; it('should generate certificate signed by provided CA', async function () { // First generate a self-signed CA certificate const ca = await generate([ { name: 'commonName', value: 'Test CA' }, { name: 'organizationName', value: 'Test Organization' } ], { algorithm: 'sha256' }); // Generate a certificate signed by the CA const pems = await generate([ { name: 'commonName', value: 'localhost' } ], { algorithm: 'sha256', ca: { key: ca.private, cert: ca.cert } }); assert.ok(!!pems.private, 'has a private key'); assert.ok(!!pems.public, 'has a public key'); assert.ok(!!pems.cert, 'has a certificate'); assert.ok(!!pems.fingerprint, 'has fingerprint'); const cert = new crypto.X509Certificate(pems.cert); const caCert = new crypto.X509Certificate(ca.cert); // Verify issuer is the CA, not self-signed assert.include(cert.issuer, 'CN=Test CA', 'issuer should be the CA'); assert.include(cert.subject, 'CN=localhost', 'subject should be localhost'); assert.notEqual(cert.issuer, cert.subject, 'should not be self-signed'); // Verify the certificate is signed by the CA assert.isTrue(cert.verify(caCert.publicKey), 'certificate should be verified by CA public key'); }); it('should include Subject Alternative Name extension', async function () { const ca = await generate([{ name: 'commonName', value: 'Test CA' }], { algorithm: 'sha256' }); const pems = await generate([ { name: 'commonName', value: 'example.com' } ], { algorithm: 'sha256', ca: { key: ca.private, cert: ca.cert } }); const cert = new crypto.X509Certificate(pems.cert); assert.include(cert.subjectAltName, 'DNS:example.com', 'should have DNS SAN matching CN'); }); it('should include IP SAN for localhost', async function () { const ca = await generate([{ name: 'commonName', value: 'Test CA' }], { algorithm: 'sha256' }); const pems = await generate([ { name: 'commonName', value: 'localhost' } ], { algorithm: 'sha256', ca: { key: ca.private, cert: ca.cert } }); const cert = new crypto.X509Certificate(pems.cert); assert.include(cert.subjectAltName, 'DNS:localhost', 'should have DNS SAN'); assert.include(cert.subjectAltName, 'IP Address:127.0.0.1', 'should have IP SAN for localhost'); }); it('should support different hash algorithms with CA signing', async function () { const ca = await generate([{ name: 'commonName', value: 'Test CA' }], { algorithm: 'sha256' }); // Test sha384 const pems384 = await generate([{ name: 'commonName', value: 'test384.local' }], { algorithm: 'sha384', ca: { key: ca.private, cert: ca.cert } }); const cert384 = new crypto.X509Certificate(pems384.cert); assert.ok(cert384.publicKey, 'should generate sha384 CA-signed cert'); // Test sha512 const pems512 = await generate([{ name: 'commonName', value: 'test512.local' }], { algorithm: 'sha512', ca: { key: ca.private, cert: ca.cert } }); const cert512 = new crypto.X509Certificate(pems512.cert); assert.ok(cert512.publicKey, 'should generate sha512 CA-signed cert'); }); it('should respect notAfterDate option with CA signing', async function () { const ca = await generate([{ name: 'commonName', value: 'Test CA' }], { algorithm: 'sha256' }); const notBefore = new Date('2025-01-01T00:00:00Z'); const notAfter = new Date('2025-01-31T00:00:00Z'); // 30 days validity const pems = await generate([{ name: 'commonName', value: 'short-lived.local' }], { algorithm: 'sha256', notBeforeDate: notBefore, notAfterDate: notAfter, ca: { key: ca.private, cert: ca.cert } }); const cert = new crypto.X509Certificate(pems.cert); const validFrom = new Date(cert.validFrom); const validTo = new Date(cert.validTo); assert.approximately(validFrom.getTime(), notBefore.getTime(), 5000, 'should use custom notBeforeDate'); assert.approximately(validTo.getTime(), notAfter.getTime(), 5000, 'should use custom notAfterDate'); }); it('should generate unique certificates with same CA', async function () { const ca = await generate([{ name: 'commonName', value: 'Test CA' }], { algorithm: 'sha256' }); const pems1 = await generate([{ name: 'commonName', value: 'test1.local' }], { algorithm: 'sha256', ca: { key: ca.private, cert: ca.cert } }); const pems2 = await generate([{ name: 'commonName', value: 'test2.local' }], { algorithm: 'sha256', ca: { key: ca.private, cert: ca.cert } }); const cert1 = new crypto.X509Certificate(pems1.cert); const cert2 = new crypto.X509Certificate(pems2.cert); assert.notEqual(cert1.serialNumber, cert2.serialNumber, 'serial numbers should be unique'); assert.notEqual(pems1.private, pems2.private, 'private keys should be different'); }); it('should work with custom keySize and CA signing', async function () { const ca = await generate([{ name: 'commonName', value: 'Test CA' }], { algorithm: 'sha256', keySize: 4096 }); const pems = await generate([{ name: 'commonName', value: 'bigkey.local' }], { algorithm: 'sha256', keySize: 4096, ca: { key: ca.private, cert: ca.cert } }); const privateKey = crypto.createPrivateKey(pems.private); assert.strictEqual(privateKey.asymmetricKeyDetails.modulusLength, 4096, 'should use custom key size'); const cert = new crypto.X509Certificate(pems.cert); const caCert = new crypto.X509Certificate(ca.cert); assert.isTrue(cert.verify(caCert.publicKey), 'certificate should verify with CA'); }); it('should support existing keyPair with CA signing', async function () { const ca = await generate([{ name: 'commonName', value: 'Test CA' }], { algorithm: 'sha256' }); // Generate a key pair first const keyPair = await generate([{ name: 'commonName', value: 'keypair.local' }], { algorithm: 'sha256' }); // Use existing key pair with CA signing const pems = await generate([{ name: 'commonName', value: 'reused.local' }], { algorithm: 'sha256', keyPair: { privateKey: keyPair.private, publicKey: keyPair.public }, ca: { key: ca.private, cert: ca.cert } }); assert.strictEqual(pems.private, keyPair.private, 'should use provided private key'); assert.strictEqual(pems.public, keyPair.public, 'should use provided public key'); const cert = new crypto.X509Certificate(pems.cert); const caCert = new crypto.X509Certificate(ca.cert); assert.isTrue(cert.verify(caCert.publicKey), 'certificate should verify with CA'); }); it('should include proper extended key usage extensions', async function () { const ca = await generate([{ name: 'commonName', value: 'Test CA' }], { algorithm: 'sha256' }); const pems = await generate([{ name: 'commonName', value: 'server.local' }], { algorithm: 'sha256', ca: { key: ca.private, cert: ca.cert } }); const cert = new crypto.X509Certificate(pems.cert); // Check extended key usage (OIDs) // 1.3.6.1.5.5.7.3.1 = serverAuth // 1.3.6.1.5.5.7.3.2 = clientAuth assert.include(cert.keyUsage, '1.3.6.1.5.5.7.3.1', 'should have serverAuth extended key usage'); assert.include(cert.keyUsage, '1.3.6.1.5.5.7.3.2', 'should have clientAuth extended key usage'); }); it('should work with PKCS#1 RSA key format', async function () { // Generate a CA with PKCS#1 format key (like mkcert uses) const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, privateKeyEncoding: { type: 'pkcs1', format: 'pem' }, publicKeyEncoding: { type: 'spki', format: 'pem' } }); // Create a self-signed CA cert using the PKCS#1 key const ca = await generate([{ name: 'commonName', value: 'PKCS1 CA' }], { algorithm: 'sha256' }); // Now test that we can use a PKCS#1 formatted key as CA // Convert our generated key to PKCS#1 for testing const caKeyObject = crypto.createPrivateKey(ca.private); const pkcs1Key = caKeyObject.export({ type: 'pkcs1', format: 'pem' }); const pems = await generate([{ name: 'commonName', value: 'pkcs1-test.local' }], { algorithm: 'sha256', ca: { key: pkcs1Key, cert: ca.cert } }); const cert = new crypto.X509Certificate(pems.cert); const caCert = new crypto.X509Certificate(ca.cert); assert.isTrue(cert.verify(caCert.publicKey), 'should work with PKCS#1 key format'); }); });