Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signed certificates sometimes does not verify against their CA public key #385

Open
or390 opened this issue Nov 14, 2023 · 0 comments
Open

Comments

@or390
Copy link

or390 commented Nov 14, 2023

I've encountered a weird issue where signed certificates sometimes does not verify against their CA public key. It rarely happens, only once in couple of thousands signatures, and it only happens with crypto.X509Certificate.verify function (pkijs.Certificate verify does return true). I'm not sure if the issue is somewhere in your code or webcrypto's, I was hoping you could confirm that.

Replication code (includes both the check that does work and the one that doesn't):

const pkijs = require("pkijs");
const asn1js = require("asn1js");
const crypto = require("crypto");
const assert = require('node:assert/strict');

const CA_CERT = "MIIBVjCB/aADAgECAgkAvRYDXayXsWswCgYIKoZIzj0EAwIwDzENMAsGA1UEAwwEVGVzdDAeFw0yMzExMDUxNjIwMDBaFw00ODExMDUxNjIwMDBaMA8xDTALBgNVBAMMBFRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATgh9XNXTfcEdQ1tfZwqWiTshUsIv+j2bTLUYLSzI110WD9Pte83iN4OkXL/bK060wwXKmkEXG9I47NH8lwVX1So0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBROogLnS8YM4FqWlz0IVS+AJv3yWjAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwIDSAAwRQIhAJ91UC+6I70mMOwbkDWZR8lVpzZjCs0meyt3InmBe2lVAiAIJ8fXSSgzc9Fl9LOQ4a6aDKOBS49lSbXhUr+fSh2S7Q==";
const CA_PRIVATE_KEY = "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglGJ0AxhOPZlH1recNlmzqEMATJqvZInZUBob2Ij3gcOhRANCAATgh9XNXTfcEdQ1tfZwqWiTshUsIv+j2bTLUYLSzI110WD9Pte83iN4OkXL/bK060wwXKmkEXG9I47NH8lwVX1S";

it('demonstrate webcrypto verify error', async function () {
    this.timeout(600000);

    const cryptoEngine = new pkijs.CryptoEngine({
        name: 'OpenSSL',
        crypto: crypto.webcrypto
    });
    const caCert = pkijs.Certificate.fromBER(new Uint8Array(Buffer.from(CA_CERT, 'base64')).buffer);
    const caPrivateKey = await cryptoEngine.importKey('pkcs8', new Uint8Array(Buffer.from(CA_PRIVATE_KEY, 'base64')).buffer, {
        name: 'ECDSA',
        namedCurve: 'P-256'
    }, false, ['sign']);
    const caPublicKey = crypto.KeyObject.from(await cryptoEngine.importKey('spki', caCert.subjectPublicKeyInfo.toSchema().toBER(false), {
        name: 'ECDSA',
        namedCurve: 'P-256'
    }, true, ['verify']));

    const {publicKey, privateKey} = await cryptoEngine.generateKey(
        {
            name: 'ECDSA',
            namedCurve: 'P-256'
        },
        true,
        ['sign', 'verify']
    );

    const certificateSigningRequest = await getCertificateSigningRequest(cryptoEngine, publicKey, privateKey);

    const certificate = new pkijs.Certificate();

    certificate.version = 2;
    certificate.serialNumber = new asn1js.Integer({value: 1});

    certificate.notBefore.value = new Date();
    certificate.notBefore.value.setDate(certificate.notBefore.value.getDate() - 1);

    certificate.notAfter.value = new Date();
    certificate.notAfter.value.setDate(certificate.notAfter.value.getDate() + 7);

    certificate.subject = certificateSigningRequest.subject;

    certificate.subjectPublicKeyInfo = certificateSigningRequest.subjectPublicKeyInfo;

    certificate.issuer = caCert.subject;


    for (let i = 1; i <= 200000; i++) {
        console.log(`verifying signature [${i}]`)
        await certificate.sign(caPrivateKey, 'SHA-256', cryptoEngine);
        const certDER = certificate.toSchema(true).toBER(false);
        const x509Certificate = new crypto.X509Certificate(Buffer.from(certDER));

        const verified_pkijs = await certificate.verify(caCert, cryptoEngine); // this always works

        assert(verified_pkijs === true)

        const verified_crypto = x509Certificate.verify(caPublicKey); // this sometimes fails

        assert(verified_crypto === true)
    }
});

async function getCertificateSigningRequest(cryptoEngine, publicKey, privateKey) {
    const csr = new pkijs.CertificationRequest();

    csr.version = 0;

    csr.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
        type: '2.5.4.3', // commonName
        value: new asn1js.Utf8String({value: 'Test'})
    }));

    csr.attributes = [];

    await csr.subjectPublicKeyInfo.importKey(publicKey, cryptoEngine);

    await csr.sign(privateKey, 'SHA-256', cryptoEngine);

    return csr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant