Description
Description
Hi all,
We are working on a way to allow our dotnet api to sign some data using a private key that is stored on an on-premise NShield Connect HSM from Entrust. We are using pkcs11 to communicate with the HSM with a module (libcknfast.so
) provided by the vendor which is compatible.
We also already tested the new feature added in dotnet 8 concerning the easier way to open keys from OpenSSL that works really well.
An exception is raised when we call SignData
on the RSAOpenSsl
instance created from the SafeEvpPKeyHandle
returned by the new dotnet 8 feature OpenPrivateKeyFromEngine
.
Timeline
The issue is not critical for now, we were already planning to wait for the official release of dotnet 8 before starting to code the new api that will be using this.
We will update proactively if the status changes.
We understand full well that we have an on-premise HSM that is not easily accessible for you to test.
We will make us available if there is a need to interact or experiment things.
Reproduction Steps
Using this snippet of code :
byte[] data = ...;
using SafeEvpPKeyHandle privateKeyHandle = SafeEvpPKeyHandle.OpenPrivateKeyFromEngine("pkcs11", "pkcs11:type=private;object=my-private-key");
using RSAOpenSsl rsaPri = new(privateKeyHandle);
rsaPri.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Expected behavior
The data is signed using the private key stored into the HSM.
Actual behavior
rsaPri.SignData(...)
raises the following exception :
Interop+Crypto+OpenSslCryptographicException: error:04084093:rsa routines::value missing
at Interop.Crypto.RsaSignHash(SafeEvpPKeyHandle pkey, RSASignaturePaddingMode paddingMode, IntPtr digestAlgorithm, ReadOnlySpan`1 hash, Span`1 destination)
at System.Security.Cryptography.RSAOpenSsl.TrySignHash(ReadOnlySpan`1 hash, Span`1 destination, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, Boolean allocateSignature, Int32& bytesWritten, Byte[]& signature)
at System.Security.Cryptography.RSAOpenSsl.SignHash(Byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
at System.Security.Cryptography.RSA.SignData(Byte[] data, Int32 offset, Int32 count, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
Configuration
OS
sh-4.4# cat /etc/os-release
NAME="Red Hat Enterprise Linux"
VERSION="8.8 (Ootpa)"
ID="rhel"
ID_LIKE="fedora"
VERSION_ID="8.8"
PLATFORM_ID="platform:el8"
PRETTY_NAME="Red Hat Enterprise Linux 8.8 (Ootpa)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:redhat:enterprise_linux:8::baseos"
HOME_URL="https://www.redhat.com/"
DOCUMENTATION_URL="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8"
REDHAT_BUGZILLA_PRODUCT_VERSION=8.8
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="8.8"
Dotnet
sh-4.4# dotnet --info
Host:
Version: 8.0.0-rc.2.23470.7
Architecture: x64
Commit: 49bf70a429
RID: linux-x64
.NET SDKs installed:
No SDKs were found.
.NET runtimes installed:
Microsoft.AspNetCore.App 8.0.0-rtm.23471.8 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 8.0.0-rc.2.23470.7 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Other architectures found:
None
Environment variables:
Not set
global.json file:
Not found
Learn more:
https://aka.ms/dotnet/info
Download .NET:
https://aka.ms/dotnet/download
OpenSSL
sh-4.4# openssl version
OpenSSL 1.1.1k FIPS 25 Mar 2021
sh-4.4# openssl engine pkcs11 -t -v
(pkcs11) pkcs11 engine
[ available ]
SO_PATH, MODULE_PATH, PIN, VERBOSE, QUIET, INIT_ARGS, FORCE_LOGIN
sh-4.4# cat /etc/pki/tls/openssl.cnf
openssl_conf = openssl_def
[openssl_def]
engines = engine_section
[engine_section]
pkcs11 = pkcs11_section
[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib64/engines-1.1/pkcs11.so
MODULE_PATH = /opt/nfast/toolkits/pkcs11/libcknfast.so
init = 0
Key
p11tool --provider /opt/nfast/toolkits/pkcs11/libcknfast.so --list-privkeys
Object 0:
URL: pkcs11:...object=my-private-key;type=private
Type: Private key (RSA-2048)
Label: my-private-key
Flags: CKA_WRAP/UNWRAP; CKA_SENSITIVE;
ID: ...
Other information
Following the above stack trace, it brings us here :
https://github.com/dotnet/runtime/blob/df8cbd11b84a5930ec71666ad87479d559872718/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c#L254C1-L263C6
We did some trial and error to determine where the root cause was :
- We tried using purely OpenSSL commands.
- We tried using C code, that was provided to us by @krwq (greatly appreciated 🥇 )
We made contact through our Microsoft account manager
Please note that we cut the pkcs11 uri in the following samples for brevity.
The ones we are using were provided by tools like p11tool or from the HSM-vendor tool.
Using OpenSSL
Output
echo -n "Hello, world!" > /tmp/test.txt
(No output)
openssl dgst -engine pkcs11 -sign "pkcs11:type=private;object=my-private-key" -sha256 -keyform engine -sigopt rsa_padding_mode:pkcs1 -out /tmp/test.sig -binary /tmp/test.txt
engine "pkcs11" set.
openssl dgst -engine pkcs11 -verify "pkcs11:type=public;object=my-public-key" -sha256 -keyform engine -signature /tmp/test.sig -binary /tmp/test.txt
engine "pkcs11" set.
Verified OK
Everything went well.
So we believe this would eliminate potential issues that could be on our end with OpenSSL configurations and that the proprietary module is not fiddling with the process.
Using C code
Output
sh-4.4# ./build.sh
Compilation successful. Executable is named ossl_test.
sh-4.4# ./ossl_test
Main - Got Private Key!
Got RSA!
RSA_FLAG_EXT_PKEY:32
d:1
Factors - p:1 q:1
CRT Params - dmp1:1 dmq1:1 iqmp:1
Main - HasNoPrivateKey Result:1
Signature verification succeeded.
Main - Got Signature!
Let's explain the output a little :
Main - Got Private Key!
means that the pkcs11 engine was initialized and the private key was loaded accordingly.Got RSA!
untilMain - HasNoPrivateKey...
are printf used to debug theHasNoPrivateKey
function.The value next to each key property (d, p, q, etc) are boolean that checks if it is NULL.
In that case, everything is expected to be NULL because the key is not exportable from the HSM.Main - Got Signature!
means the signature was done correctly using the pkcs11 engine.
Everything went well for the signature.
However, HasNoPrivateKey
returns 1.
Which means it looks like there are no private key and then dotnet will raise the exception above : error:04084093:rsa routines::value missing
We added the
RSA_FLAG_EXT_PKEY
flag in the output because we found an older issue that looked like ours with a different context.
We need some guidance if there is something that could be done about the HasNoPrivateKey
function or if there something to do with the RSA_FLAG_EXT_PKEY
flag.
We created the issue as requested by our Microsoft account manager to allow you and/or the community to have a handle on something more tangible.