0

I am looking for the default options for nodejs internal crypto module functions generateKeyPairSync or generateKeyPair that matches openssl defaults.

I am trying to create a simple https key and crt using the crypto module. I am using the following code. I have just two questions:

References : https://nodejs.org/api/crypto.html#cryptogeneratekeypairsynctype-options and https://www.openssl.org/docs/faq.html

  1. What are the defaults options that is used by openssl for creating a rsa .key and .crt keys for simple https server using the crypto nodejs module.
  2. The keys are specific for a domain www.domain.com. how is that specified if i use the crypto module of nodejs? openssl req -new -newkey rsa:2048 -nodes -keyout yourdomain.key -days 365 -out yourdomain.csr -subj "/C=US/ST=Utah/L=Lehi/O=Your Company, Inc./OU=IT/CN=yourdomain.com"
  3. How is this implemented since the crypto module generates the public and private key seperately and the private domain.key contains the public and private key for domain's https module. Extracting Your Public Key - The private key file contains both the private key and the public key. You can extract your public key from your private key file if needed. Use the following command to extract your public key: openssl rsa -in yourdomain.key -pubout -out yourdomain_public.key
I am looking to replace the ?s and the 0s below in the next code to get the defaults similar to openssl certificates.

const {
    generateKeyPairSync,
} = await import('node:crypto');

const {
    publicKey,
    privateKey,
} = generateKeyPairSync('rsa', {
    modulusLength: 2048, // <number> Key size in bits (RSA, DSA). . 4096 works for me
    publicExponent: 0, // <number> Public exponent (RSA). Default: 0x10001.
    hashAlgorithm: "?", // <string> Name of the message digest (RSA-PSS).
    mgf1HashAlgorithm: "?", // <string> Name of the message digest used by MGF1 (RSA-PSS).
    saltLength: 0, // <number> Minimal salt length in bytes (RSA-PSS).
    divisorLength: 0, // <number> Size of q in bits (DSA).
    namedCurve: "?", // <string> Name of the curve to use (EC).
    prime: new Buffer("?"), // <Buffer> The prime parameter (DH).
    primeLength: 0, // <number> Prime length in bits (DH).
    generator: 0, // <number> Custom generator (DH). Default: 2.
    groupName: "?", // <string> Diffie-Hellman group name (DH). See crypto.getDiffieHellman().
    paramEncoding: "?", // <string> Must be 'named' or 'explicit' (EC). Default: 'named'.
    publicKeyEncoding: { // <Object> See keyObject.export().
        type: 'spki', // two options: spki or pkcs1
        format: 'pem',
    },
    privateKeyEncoding: { // <Object> See keyObject.export().
        type: 'pkcs8', // two options: spki or pkcs1 or >> PBKDF2 as per https://www.openssl.org/docs/man3.0/man1/openssl-pkcs12.html
        format: 'pem',
        cipher: 'aes-256-cbc', // The default encryption algorithm is AES-256-CBC with PBKDF2 for key derivation as per https://www.openssl.org/docs/man3.0/man1/openssl-pkcs12.html
        passphrase: 'top_secret', // do i need this?
    },
});

I am then exporting the key to a file as below. I am dumping the files as .pem files. Just a check is there something else for key extension that I should use?


const fs = require("fs");
var xKpem = key.export({type: "pcks1",  format: "pem"});
fs.writeFileSync("./filename.?", xKpem); 

I am refering to this document here https://www.openssl.org/docs/man3.0/man1/openssl-pkcs12.html and this reference here https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm

I will be using this as follows:

const express = require('express')
const app = express()
const https = require('https')
const fs = require('fs')
const port = 3000

app.get('/', (req, res) => {
  res.send('WORKING!')
})

const httpsOptions = {
  key: fs.readFileSync('./key.pem'),
  cert: fs.readFileSync('./cert.pem')
}
const server = https.createServer(httpsOptions, app).listen(port, () => {
  console.log('server running')
})
Gary
  • 2,293
  • 2
  • 25
  • 47

1 Answers1

1

What are the defaults options that is used by openssl for creating a rsa .key and .crt keys for simple https server using the crypto nodejs module.

.crt is conventionally used for a certificate, and your openssl req command is not creating a certificate. It also is not creating just a public key; rather it is creating a Certificate Signing Request aka CSR (hence the filename .csr) which is not the same as either a certificate or public key.

There are three different ways in openssl commandline to create an RSA keypair, and their defaults are not all the same, so there is no answer to your question as asked. But what you need for an SSL/TLS server (including HTTPS and Express) is a privatekey and certificate, not just a keypair, and also not just a CSR. openssl req can produce a self-signed certificate if you add -x509 -- although only systems/apps you have personally tweaked will trust this cert and therefore connect. Also openssl x509 -req -signkey ... can create a selfsigned cert from a CSR (such as created with your openssl req -newkey ...) and for some reason this clumsier combination seems more popular with SO answerers.

nodejs crypto cannot create either a standard CSR or a certificate. It can create a "SPKAC" (signed public key and challenge) which was created by Netscape years ago as an alternative to the standard (PKCS10) CSR but which no one else agreed to and nowadays nobody at all uses.

The keys are specific for a domain www.domain.com. how is that specified if i use the crypto module of nodejs?

No, the keypair is not for a domain. The CSR and/or certificate is for an identity -- the exact form(s) of that identity vary depending on the use case, and for an HTTPS (Express) server, the identity is either a single domain name or address, a wildcard name that matches several domain names (on one label only), or a list of more than one of the preceding. The simplest case, and a very common one, is a single domain name. But since nodejs crypto doesn't create either a CSR or a certificate, it doesn't have anything to do with the domain name or other identity information.

How is this implemented since the crypto module generates the public and private key seperately and the private domain.key contains the public and private key for domain's https module. Extracting Your Public Key - The private key file contains both the private key and the public key. You can extract your public key from your private key file if needed. [snipped]

The private and public keys are generated together (and must be), but returned as two separate objects (or encodings) by the generateKeyPair[Sync] API. It is true the public key can be rederived from the private key (for all keypairs supported by OpenSSL) and that's what openssl rsa -pubout (or more generally openssl pkey -pubout) does.

I am looking to replace the ?s and the 0s below in the next code to get the defaults similar to openssl certificates.

Everything you have marked ? or 0 is unnecessary and should be omitted or removed. But as repeated many times above, generateKeyPair[Sync] won't generate a certificate or even a CSR.

If what you actually want is to create a certificate, as needed for SSL/TLS/HTTPS/Express, that's a question that has already been asked several times, and the answer is node-forge. See:
Can nodejs generate SSL certificates?
Using Node's crypto Library to Create a Self-signed Certificate?
Programmatically create certificate and certificate key in Node

I am then exporting the key to a file as below. I am dumping the files as .pem files. Just a check is there something else for key extension that I should use?

Software like nodejs doesn't care squat about the extension. You can call it .pem or .rumpelstiltskin or .yabbadabbadoo. People often care, and if anyone else will ever use your code/system, or you will do so more than a few days in the future, they or future-you will prefer to have file extensions, and basenames also, that accurately describe the contents.

The format and what crypto calls type do matter. Your above code block sets two variables publicKey and privateKey neither of which matches key and moreover you apparently specified publicKeyEncoding and privateKeyEncoding so both of those variables are encodings, not key objects -- they don't have an export method at all. So I don't know what you meant here. But you can reasonably export the privatekey either as pkcs1 or pkcs8 -- both are common. If you want encryption (which is your choice), the encryption for pkcs8 is better (particularly the PBKDFs). pem is probably a bit more common than der, although der is not unknown, and pem is also easier to use, being text-like, so I recommend that unless you have a reason to change. In recent versions jwk is also available, but JWK keys can't be used for SSL/TLS.

OTOH you generally shouldn't export the publickey at all, since it is useless by itself. You should use it only to generate the certificate, which you should export as a certificate. And if for some unfathomable reason you really want to export the publickey, use spki not pkcs1 -- the latter corresponds to OpenSSL's RSAPublicKey format which is almost totally unused and will only cause lots of problems.

I am refering to this document here https://www.openssl.org/docs/man3.0/man1/openssl-pkcs12.html

Why? Nothing you are doing has any relationship of any kind to PKCS12. If you use privatekey format pkcs8 the encryption it optionally uses is defined by PKCS8 and PKCS5v2, not PKCS12. PKCS12 uses the PKCS8 format, hiding it under the alias ShroudedKeyBag, but it actually is still PKCS8.

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • Two questions. 1. What is the `complete openssl command` to create public private certificates from a rootCA self signed. 2. when `was the recent version jwk released` - "In recent versions jwk is also available, but JWK keys can't be used for SSL/TLS" – Gary May 31 '23 at 16:30
  • 1
    1: a cert issued by a CA (root _or_ subordinate) is not selfsigned, and a selfsigned cert is not issued by a CA. If you want selfsigned, as I said one way is to add `-x509` to the command you posted, and rename the output file. If you want CA-issued there is no single command but numerous different combinations depending on exactly what you want which I don't know because I can't read your mind. 2: read [the documentation](https://nodejs.org/docs/latest-v18.x/api/crypto.html#keyobjectexportoptions) – dave_thompson_085 Jun 02 '23 at 07:02
  • Informative answer. One thing, "you generally shouldn't export the publickey at all"? Isn't writing the key to file persisting the key in case of server restarts a valid reason to export the public (and private) key? – Ronnie Royston Aug 23 '23 at 02:48
  • @RonnieRoyston a TLS server needs a _certificate_ and privatekey, so you should write those. The publickey _as such_ is unnecessary and useless, and exporting it is a waste of effort and time as well as causing unnecessary confusion. – dave_thompson_085 Aug 23 '23 at 09:37
  • Interesting. I've not written a TLS implementation but I know using RSA keys in a sign/verify fashion the public key is used to verify so I would be saving it for that reason. Different use case though. – Ronnie Royston Aug 23 '23 at 19:06