0 votes
by (140 points)

We're using tlsproxy to support multiple URIs running on port 443.

Tunnel config is like this:

- name: 443-127.0.0.1:8189
  in:
    port: 443
    protocol: TLS
    tlsVersions: TLS12,TLS13
  out:
    address: 127.0.0.1
    port: 8189
    protocol: TLS
  certificate: audm.nxodev.intra
  idleTimeout: 120
  serverNames: audm.nxodev.intra

This works just fine in our dev environments, but I recently tried the same approach on a prod system, and all I get stuck with is

An error occurred: System.Security.Cryptography.CryptographicException: Unable to export private key in order to use a more capable algorithm.
---> System.Security.Cryptography.CrytographicException: Private key is not exportable

So, I get the NETWORK_SERVICE full access to the private key of the certificate, and tried again - but ended up with the same problem.

I can indeed not export the private key of the cert. But why would I? the proxy should only forward the traffic, not decode and change the TLS protocol

Applies to: Rebex TLS

2 Answers

0 votes
by (70.2k points)

This is known issue and we are looking into it.

The private key is needed for inbound TLS connection. TLS proxy needs to sign the Certificate TLS message, so the client can verify that it is really connected to the audm.nxodev.intra server.

To workaround the issue either:

  • load the certificate from a .pfx file (please use certificatePath and certificatePassword instead of "certificate: audm.nxodev.intra"),
  • or import your audm.nxodev.intra certificate into the Certificate Store again and check "Mark this key as exportable..." option when importing.
0 votes
by (70.2k points)

We have investigated the issue and this is our conclusion:

When a certificate is imported to the Certificate Store on Windows the associated private key is stored in a Key Storage. However, there is more than one Key Storage on Windows. Every Cryptography Provider has its own Key Storage. More details are available at Understanding Cryptographic Providers.

On Windows, there are two major Cryptographic Providers: legacy Cryptography API (CryptoApi or CSP) and modern Cryptography API: Next Generation (CNG).

The .pfx holds information, which Cryptographic Provider is intended to be used when working with the private key. When importing certificate into Certificate Store the private key is stored in the Key Storage of the intended Cryptographic Provider and the certificate is associated with this provider.

Now, to your particular case:

  • In your case, the certificate was associated with the legacy CryptoApi.
  • TLS 1.3 needs to perform a cryptography operation, which is only available through CNG provider (namely PSS padding).
  • Since the private key is stored in legacy CryptoApi (which does not support PSS) the TLS Proxy tries to export the private key from CryptoApi and import it to CNG in order to perform PSS padding operation.
  • This fails when the private key is not marked as exportable (which is your case).

Solution:

  • Either use one of the previously suggested workarounds.
  • Or associate your certificate with CNG provider.

It can be done either programmatically in C# using Rebex.Common:

using Rebex.Security.Certificates;
...
var cert = Certificate.LoadPfx("cert-CSP.pfx", "xxx", KeySetOptions.Exportable | KeySetOptions.AlwaysCng);
cert.Save("cert-CNG.pfx", CertificateFormat.Pfx, "xxx");

Or using OpenSSL:

openssl pkcs12 -in cert-CSP.pfx -passin pass:xxx -out cert-CSP.pem -passout pass:xxx
openssl pkcs12 -export -in cert-CSP.pem -passin pass:xxx -out cert-CNG.pfx -passout pass:xxx -CSP "Microsoft Software Key Storage Provider"

Then, you can import the converted cert-CNG.pfx into Certificate Store again (without need to mark the private key as exportable). Since the private key is associated with CNG provider, TLS Proxy will not attempt to export it anymore.

Note:
When you try to use TLS 1.1 or previous, you will face the opposite issue. The CNG provider does not support required cryptography operation (namely legacy SHAMD5). The TLS Proxy will try to export the private key from CNG to import it into CryptoApi, which will fail (if not exportable).
However, if you are not going to support TLS 1.1, you are fine.

by (140 points)
So, if I get that right, if I were to limit the TLS Version on the 'in' side to TLS12 (omitting TLS13), I things should work fine, too, correct?
by (70.2k points)
Yes, TLS 1.2 and older are compatible with legacy CryptoApi. If you don't need to support TLS 1.3 in the future, omitting `TLS13` on the inbound tunnel will solve the issue.
...