0

I have a Node.js server that receives a request with the Client TLS certificate supplied in the XFCC header.

I would like to perform the Mutual TLS at the Application level, i.e. validate Client TLS cert against the server's CA truststore - all of this done in application code, rather than relying on a web proxy configuration.

I am using NPM's pem dependency, which is essentially a bunch of JS wrappers around openssl. In particular, the verification needed to resemble mTLS is the verify method:

openssl verify -CAfile /my/server/ca-chain.crt client-chain.crt

This works in the simplest case:

  • ca-chain.crt: Root CA -> Int 1 CA
  • client-chain.crt Root CA -> Int 1 CA -> Leaf 1

But it fails in the more complex cases where Int CA's are different:

  • ca-chain.crt: Root CA -> Int 1 CA
  • client-chain.crt Root CA -> Int 2 CA -> Leaf 2

With the following:

openssl verify -CAfile /my/server/ca-chain.crt client-chain.crt
error 20 at 0 depth lookup:unable to get local issuer certificate

As far as I understand mTLS would be successfully performed as long as all certs are valid and lead up to the same Root CA, despite different Int CA's, which means verify doesn't work as-is for the purpose of doing mTLS equivalent at the App level.

I know about s_client and s_server capabilities, but they seem like hacks for what I need, rather than a proper solution.

I guess my question is then this:

  • Is it possible to use openssl to verify certificate against CA chain according to the mTLS rules?
  • And if not possible, then what would be the way to do it without resorting to writing it from scratch?
Dmitry Kankalovich
  • 553
  • 2
  • 8
  • 19
  • @user207421 IDK even where to begin to address such a hostile comment, but it's not as unusual as you think, and in fact, some of the very well-known solutions perform mTLS at the application level. Check out how Kong does it, for example. – Dmitry Kankalovich Aug 26 '21 at 10:40
  • Also: `verify that the identity established by the authenticated client certificate is authorized to access this part of your application` - Well, in my case the application itself *is* the Auth server, and its sole responsibility is to do such verification. However it sits behind the L7 API Gateway, and there is no simple way to pass through the underlying TLS cert except for the header. – Dmitry Kankalovich Aug 26 '21 at 10:54
  • 1
    Not visibly about programming or development, and **dupe** https://stackoverflow.com/questions/65204616/why-does-openssl-verify-fail-with-a-certificate-chain-file- and several more linked there (by me), mostly in security.SX where they belong. – dave_thompson_085 Aug 26 '21 at 11:45
  • @dave_thompson_085 I wouldn't necessarily agree it's not a programming question since it is in the scope of Node.js and it's module `pem` - for example, one of the suggestions could've been to resort to the usage of the different module. However, you're right that one of your SO answers might contain what I need, specifically this one very helpful https://stackoverflow.com/questions/44375300/openssl-verify-with-chained-ca-and-chained-cert. I am going to take a look and then resolve this question, if it works afterwards. – Dmitry Kankalovich Aug 26 '21 at 12:04

1 Answers1

0

As dave_thompson_085 pointed out in his other answer, for the openssl verify to make it work you need to be aware that it does not read the entire certificate chain from the supplied client cert file, only the last (leaf) certificate.

So I believe that this method in pem package is not entirely correct (in fact they do have open issue on that), but that's another thing for discussion.

The supposed openssl command should've been translated to is this:

openssl verify -CAfile /my/server/ca-chain.crt -untrusted client-ca-chain.crt client-leaf.crt

Here I split the leaf client cert from the rest of the chain which is passed in the -untrusted param, while -CAfile contains a chain with different a Int CA, but which eventually leads up to the same Root CA - and this is what effectively makes the client cert chain valid.

This should be fairly trivial to implement with the Node.js' openssl or similar wrapper.

Dmitry Kankalovich
  • 553
  • 2
  • 8
  • 19