Joe Alvini
Joe Alvini

Reputation: 299

How To Create SSL For GCP Load Balancer Using NodeJS?

Just as a prelude, I already know how to manually create an SSL cert to add to the GCP load balancer. What Im trying to do is use acme-client to create a certificate that I can then push to the GCP load balancer and automatically have people verify their domain (white labeling).

So here is my code:

const createEncryptedCert = () => {
    const { generateKeyPairSync, createSign } = require('crypto');

    const { privateKey, publicKey } = generateKeyPairSync('ec', {
        namedCurve: 'prime256v1', // P-256
        publicKeyEncoding: {
            type: 'spki',
            format: 'pem'
        },
        privateKeyEncoding: {
            type: 'pkcs8',
            format: 'pem'
        }
    });

    console.log('Private Key:', privateKey);
    console.log('Public Key:', publicKey);

    const csrSign = createSign('SHA256');
    const csrData = `-----BEGIN CERTIFICATE REQUEST-----
Subject: CN=example.com
-----END CERTIFICATE REQUEST-----`;
    csrSign.update(csrData);
    csrSign.end();

    const csr = csrSign.sign(privateKey).toString();

    console.log('CSR:', csr);

    return {csr, privateKey}
}

async function initiateCertificateProcess(domain) {
    const { privateKey } = createEncryptedCert();

    const client = new acmeClient.Client({
        directoryUrl: acmeClient.directory.letsencrypt.staging,
        accountKey: privateKey.toString(),
    });

    await client.createAccount({ termsOfServiceAgreed: true });

    const [key, csr] = await acmeClient.forge.createCsr({
        commonName: domain,
    });

    const order = await client.createOrder({ identifiers: [{ type: 'dns', value: domain }] });

    const authorizations = await client.getAuthorizations(order);
    const challenge = authorizations[0].challenges.find(ch => ch.type === 'dns-01');
    const dnsRecordValue = await client.getChallengeKeyAuthorization(challenge);
    
    return {
        recordName: `_acme-challenge.${domain}.`,
        recordType: 'TXT',
        recordValue: dnsRecordValue,
        accountKey: privateKey, // Note: This might need adjusting based on intended use.
        order: order,
        accountUrl: client.getAccountUrl(),
        csr: csr.toString(),
        authorizations: authorizations,
        challenge: challenge
    };
}

After the user calls this endpoint they then call a second endpoint to let me know that they have finished adding the Acme TXT record to their DNS.

When they do this I then call this endpoint

exports.verifyAndCompleteSSLVerification = async (domain, accountKeyPem, orderUrl, accountUrl, csr, authorizations, challenge) => {
    const acmeClient = require('acme-client');

    const client = new acmeClient.Client({
        directoryUrl: acmeClient.directory.letsencrypt.staging,
        accountKey: accountKeyPem,
        accountUrl: accountUrl
    });

    await client.verifyChallenge(authorizations[0], challenge);
    await client.completeChallenge(challenge);

    await client.waitForValidStatus(challenge);

    const order = await client.getOrder(orderUrl);
    await client.finalizeOrder(order, csr);

    await client.waitForValidStatus(order);

    const certificate = await client.getCertificate(order);

    return certificate;
}

It seems as though everything creates as I get a private key from the first call and then in the second call the getCertificate method does return a certificate. The problem is that when I try to add these into GCP I get the following message

The SSL certificate and key do not match.

Bar the obvious, that the key obviously does not match the certificate, can anyone please help if there are any glaring problems with what I am doing? Or maybe even suggest an easier way to do this within the GCP eco system as I do not want to switch to Cloud Flare or some other service.

Thanks in advance for any help.

Upvotes: 0

Views: 56

Answers (0)

Related Questions