nodejs / help

:sparkles: Need help with Node.js? File an Issue here. :rocket:
1.48k stars 284 forks source link

How to use private keys persisted in the TPM with tpm2-openssl OpenSSL Provider #4413

Open nuts-i77 opened 5 months ago

nuts-i77 commented 5 months ago

Node.js Version

v18.19.1

NPM Version

v9.2.0

Operating System

Linux test1 6.8.0-35-generic #35-Ubuntu SMP PREEMPT_DYNAMIC Mon May 20 15:51:52 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

https

Description

I am trying to use the OpenSSL Provider's tpm2-openssl and the TPM for client certificate authentication in a Node.js application. I was able to use a private key in PEM (TSS2 PRIVATE KEY) key format. However, when I try to use a private key persisted in the TPM from Node.js, I get the ERR_OSSL_UNSUPPORTED error.

Does anyone have any ideas on how to use a private key persisted in a TPM from Node.js?

tpm2-openssl https://github.com/tpm2-software/tpm2-openssl

Client certificate authentication using a PEM (TSS2 PRIVATE KEY) format key was successful using the following procedure.

Generate a private key openssl genpkey -provider tpm2 -algorithm RSA -out clkey

Generate a CSR and issue a certificate from a CA using client.csr and save it as clcert. openssl req -new -key clkey -provider tpm2 -provider base -out client.csr

Creating openssl.cnf

nodejs_conf = nodejs_init

[nodejs_init]
providers = provider_sect

[provider_sect]
default = default_sect
tpm2 = tpm2_section

[tpm2_section]
dynamic_path=/usr/lib/x86_64-linux-gnu/ossl-modules/tpm2.so
activate = 1

[default_sect]
activate = 1

Creating a Node.js test program

const https = require('https');
const fs    = require('fs');

console.time('request');

const options = {
  hostname: 'example.com',
  port: 443,
  path: '/',
  method: 'GET',

  // private key file(TSS2 PRIVATE KEY format)
  key: fs.readFileSync('clkey'),

  // client cert
  cert: fs.readFileSync('clcert'),
};

const req = https.request(options, (res) => {
    res.on('data', (d) => process.stdout.write(d));
    console.timeEnd('request');
});

req.on('error', (e) => console.error(e))

req.end();

Start test program

request: 211.767ms
{}

However, when I try to use a private key persisted in the TPM from Node.js, I get the ERR_OSSL_UNSUPPORTED error.

Persist the private key in the TPM handler

tpm2_createek -G rsa -c ek_rsa.ctx
tpm2_createak -C ek_rsa.ctx -G rsa -g sha256 -s rsassa -c ak_rsa.ctx
tpm2_evictcontrol -c ak_rsa.ctx 0x81008100

Generate a CSR and issue a certificate from a CA using client.csr and save it as clcert. openssl req -new -key handle:0x81008100 -provider tpm2 -provider base -out client.csr

Creating openssl.cnf

nodejs_conf = nodejs_init

[nodejs_init]
providers = provider_sect

[provider_sect]
default = default_sect
tpm2 = tpm2_section

[tpm2_section]
dynamic_path=/usr/lib/x86_64-linux-gnu/ossl-modules/tpm2.so
activate = 1

[default_sect]
activate = 1

Creating a Node.js test program

const https = require('https');
const fs    = require('fs');

console.time('request');

const options = {
  hostname: 'example.com',
  port: 443,
  path: '/',
  method: 'GET',

  // handle of the private key
  key: "handle:0x81008100",

  // client cert
  cert: fs.readFileSync('clcert'),
};

const req = https.request(options, (res) => {
    res.on('data', (d) => process.stdout.write(d));
    console.timeEnd('request');
});

req.on('error', (e) => console.error(e))

req.end();

Start test program

node --openssl-config=openssl.cnf test.js
request: 211.767ms
node:internal/tls/secure-context:93
  context.setKey(key, passphrase);
          ^

Error: error:1E08010C:DECODER routines::unsupported
    at setKey (node:internal/tls/secure-context:93:11)
    at configSecureContext (node:internal/tls/secure-context:175:7)
    at Object.createSecureContext (node:_tls_common:117:3)
    at Object.connect (node:_tls_wrap:1750:48)
    at Agent.createConnection (node:https:158:22)
    at Agent.createSocket (node:_http_agent:341:26)
    at Agent.addRequest (node:_http_agent:288:10)
    at new ClientRequest (node:_http_client:342:16)
    at Object.request (node:https:366:10)
    at Object.<anonymous> (/root/test2.js:42:19) {
  library: 'DECODER routines',
  reason: 'unsupported',
  code: 'ERR_OSSL_UNSUPPORTED'
}

Minimal Reproduction

No response

Output

No response

Before You Submit

OSkrk commented 3 months ago

@nuts-i77 in the first case, the command below generates a standard PEM key not a TSS2 PRIVATE KEY. You can check the content of the clkey file:

openssl genpkey -provider tpm2 -algorithm RSA -out clkey

So it works for you with the PEM file as you are using the standard procedure (not using the TPM). I'm also trying to use a TSS2 PRIVATE KEY for a client mTLS session, so please let me know if you found a solution. In my case I'm getting the following error for both handle and TSS2 file:

node:internal/tls/secure-context:93
  context.setKey(key, passphrase);
          ^

Error: error:1E08010C:DECODER routines::unsupported
    at setKey (node:internal/tls/secure-context:93:11)
    at configSecureContext (node:internal/tls/secure-context:175:7)
    at Object.createSecureContext (node:_tls_common:117:3)
    at Object.connect (node:_tls_wrap:1750:48)
    at Agent.createConnection (node:https:158:22)
    at Agent.createSocket (node:_http_agent:341:26)
    at Agent.addRequest (node:_http_agent:288:10)
    at new ClientRequest (node:_http_client:342:16)
    at Object.request (node:https:366:10)
    at Object.<anonymous> (/home/oscar/app.js:19:19) {
  library: 'DECODER routines',
  reason: 'unsupported',
  code: 'ERR_OSSL_UNSUPPORTED'
}