PeculiarVentures / x509

@peculiar/x509 is an easy to use TypeScript/Javascript library based on @peculiar/asn1-schema that makes generating X.509 Certificates and Certificate Requests as well as validating certificate chains easy

Home Page:https://peculiarventures.github.io/x509/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can I create a set of certificates?

kogratte opened this issue · comments

Hey :)

I'm trying to create a set of certificates, which can be drawn as below:

  • RootCert
  • Child cert, signed by root cert
    

I'm struggling with that...

For RootCert, I'm building it like that:

const alg = {
    name: 'RSASSA-PKCS1-v1_5',
    hash: 'SHA-256',
    publicExponent: new Uint8Array([1, 0, 1]),
    modulusLength: 4096,
};

const keys = await crypto.subtle.generateKey(alg, true, ['sign', 'verify']);

const cert = await x509.X509CertificateGenerator.createSelfSigned({
        serialNumber: '01',
        name: 'CN=Test',
        notBefore: new Date('2020/01/01'),
        notAfter: new Date('2020/01/02'),
        signingAlgorithm: alg,
        keys,
        extensions: [
            new x509.BasicConstraintsExtension(true, 2, true),
            new x509.ExtendedKeyUsageExtension(
                ['1.2.3.4.5.6.7', '2.3.4.5.6.7.8'],
                true
            ),
            new x509.KeyUsagesExtension(
                x509.KeyUsageFlags.digitalSignature |
                    x509.KeyUsageFlags.keyEncipherment |
                    x509.KeyUsageFlags.cRLSign |
                    x509.KeyUsageFlags.keyCertSign,
                true
            ),
            await x509.SubjectKeyIdentifierExtension.create(keys.publicKey),
        ],
    });

When it comes to create a child cert, I'm trying to do the following:

const keys2 = await crypto.subtle.generateKey(alg, false, ['decrypt']);

const c2 = await x509.X509CertificateGenerator.create({
        publicKey: keys2.publicKey,
        serialNumber: '01',
        signingKey: cert.privateKey!,
        notBefore: new Date('2020/01/01'),
        notAfter: new Date('2020/01/02'),
        signingAlgorithm: alg,
        extensions: [
            await x509.SubjectKeyIdentifierExtension.create(keys2.publicKey),
        ],
    });

But.. I couldn't say I've been successful!

Is it possible to achieve what I'm trying to do? If so, what am I missing?

In the example you provided, a root certificate is created with the name CN=Test, but no issuer name is specified when issuing a user certificate. As per specification requirements, an issued certificate needs to have an issuer's name. The X509ChainBuilder performs this verification, and if the names do not match, it will exclude the certificate from the chain construction.

For a practical example of certificate chain creation, you can refer to the script in this test: https://github.com/PeculiarVentures/x509/blob/master/test/issues.ts#L10-L60.

I'll take a look :) Thanks :) (please, do not close this issue for the time being, I would like to share the result once working as expected)

Q: What is the point of the intermediate cert in the example you point me out? I did a quick research though github using your lib, and found a lot of different details.

A reproduction repo is available here:
https://github.com/kogratte/x509-sandbox

As you may see here, tests are failing. I may have miss-understood something, but I'm not able to find it :/

What is different between my implementation and the one from the example?

I've updated your example. It works.

const alg = {
  name: "RSASSA-PKCS1-v1_5",
  hash: "SHA-256",
  publicExponent: new Uint8Array([1, 0, 1]),
  modulusLength: 4096,
};
const rootKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const rootCert = await x509.X509CertificateGenerator.createSelfSigned({
  serialNumber: "01",
  name: "CN=Root",
  notBefore: new Date("2023-12-19"),
  notAfter: new Date("2033-12-19"),
  keys: rootKeys,
  signingAlgorithm: alg,
  extensions: [
    new x509.BasicConstraintsExtension(true, 2, true),
    new x509.ExtendedKeyUsageExtension(
      ["1.2.3.4.5.6.7", "2.3.4.5.6.7.8"],
      true
    ),
    new x509.KeyUsagesExtension(
      x509.KeyUsageFlags.cRLSign |
      x509.KeyUsageFlags.keyCertSign,
      true
    ),
    await x509.SubjectKeyIdentifierExtension.create(rootKeys.publicKey, false, crypto),
  ],
}, crypto);

const leafKeys = await crypto.subtle.generateKey({
  ...alg,
  name: "RSA-OAEP", // We should use RSA-OAEP for encryption
}, true, ["encrypt", "decrypt"]);
const leafCert = await x509.X509CertificateGenerator.create({
  serialNumber: "03",
  subject: "CN=Leaf",
  issuer: rootCert.subject,
  notBefore: new Date("2023-12-19"),
  notAfter: new Date("2024-12-19"),
  signingKey: rootKeys.privateKey,
  publicKey: leafKeys.publicKey,
  signingAlgorithm: alg,
  extensions: [
    new x509.KeyUsagesExtension(
      x509.KeyUsageFlags.dataEncipherment,
      true,
    ),
    new x509.BasicConstraintsExtension(false),
    await x509.AuthorityKeyIdentifierExtension.create(rootCert, false, crypto),
    await x509.SubjectKeyIdentifierExtension.create(leafKeys.publicKey, false, crypto),
  ],
}, crypto);

// console.log([
//   rootCert.toString("pem"),
//   leafCert.toString("pem"),
// ].join("\n"));

const chain = new x509.X509ChainBuilder({
  certificates: [rootCert],
});
const items = await chain.build(leafCert, crypto);
assert.strictEqual(items.length, 2);

It works!

I pushed the working codebase. I can keep it online or you can grab it if you want, but it will anyway be available for futur users! Thanks for assistance!