dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.46k stars 4.76k forks source link

.net core TLS server certificate with private key in TPM #109243

Open appcodr opened 1 month ago

appcodr commented 1 month ago

I'm trying to do an equivalent of this command in a .net core library for doing a TLS server where the private key is in TPM with a reference of 0x8100001:

openssl s_server -cert rsa.crt -key 0x8100002-keyform engine -engine tpm2tss -accept 8

im trying for this to be running in Ubuntu on .net core. In Windows this is abstracted well by the cert store with crypto provider but the same doesn't exist in Ubuntu.

Does anyone have an example of using a package that works in Ubuntu? The below is the equivalent in windows that we use to get from cert store(the cert store abstracts the TPM access)

X509Store store = new X509Store(storeName, StoreLocation.CurrentUser);  
store.Open(OpenFlags.ReadOnly);  
X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindBySubjectName, commonName,false);

I also tried this example(https://github.com/dotnet/runtime/issues/94493) with the 0x8100002 reference but it fails as well

private static X509Certificate2 GetClientCert(string path, ???)
{
    using SafeEvpPKeyHandle keyHandle = ???;
    using RSAOpenSsl rsa = new RSAOpenSsl(keyHandle);
    using X509Certificate2 certOnly = new X509Certificate2(path);

    return certOnly.CopyWithPrivateKey(rsa);
}
bartonjs commented 1 month ago

Since you're using an engine (and not a provider) you should be able to do it with .NET 8+

using (SafeEvpPKeyHandle keyHandle = SafeEvpPKeyHandle.OpenPrivateKeyFromEngine("tpm2tss", "0x8100002"))
using (RSA rsa = new RSAOpenSsl(keyHandle))
using (X509Certificate2 certOnly = new X509Certificate2(path))
{
    return certOnly.CopyWithPrivateKey(rsa);
}

If instead you need an OSSL_PROVIDER (instead of an ENGINE) then you need .NET 9+, where it would look like

using (SafeEvpPKeyHandle keyHandle = SafeEvpPKeyHandle.OpenKeyFromProvider("tpm2tss", "0x8100002"))
using (RSA rsa = new RSAOpenSsl(keyHandle))
using (X509Certificate2 certOnly = X509CertificateLoader.LoadCertificateFromFile(path))
{
    return certOnly.CopyWithPrivateKey(rsa);
}
appcodr commented 1 month ago

so im using .net 8 and openssl version 1.1.1 and have this error when using the engine option as listed above

Unhandled exception. Interop+Crypto+OpenSslCryptographicException: error:13000074:engine routines::no such engine

and with openssl we have the enginer availalbe openssl engine -t -c tpm2tss (tpm2tss) TPM2-TSS engine for OpenSSL [RSA, RAND] [ available ]

Is there any other config needed to use this code using (SafeEvpPKeyHandle keyHandle = SafeEvpPKeyHandle.OpenPrivateKeyFromEngine("tpm2tss", "0x8100002")) using (RSA rsa = new RSAOpenSsl(keyHandle)) using (X509Certificate2 certOnly = new X509Certificate2(path)) { return certOnly.CopyWithPrivateKey(rsa); }

bartonjs commented 1 month ago

Nothing special extra should be required. The argument passed to openssl engine gets passed to ENGINE_by_id, and that's the same as we do with the engineName parameter.

im using .net 8 and openssl version 1.1.1

Is there more than one copy of OpenSSL on your system? (Specifically, libssl.so and libcrypto.so). .NET is capable of using 1.1.1, but if we find 3.0 we prefer it. So if you have a libssl.so.3/libcrypto.so.3 then .NET is almost certainly choosing that version. You can ask what version we got by querying SafeEvpPKeyHandle.OpenSslVersion and printing that in hex... or use a debugger to see what specific copy of libcrypto.so got loaded.

If you have more than one, it's possible that the version we're binding to has a different ENGINE resolver path than the one you're interacting with using the commandline tool.

--

The next thing I can think of is that the ENGINE might be loading successfully, but the ENGINE is incorrectly reporting "no such engine" instead of "no such key" if the key name needs to be formatted in a manner other than what I speculated. That would probably require using a native debugger and seeing that our call to ENGINE_by_id succeeded, but our call to ENGINE_load_private_key failed. We just report whatever error OpenSSL and/or the ENGINE reported, I can't tell you if it's, y'know, accurate.

GuyWithDogs commented 1 month ago

My understanding is that engines are deprecated, but not removed, from OpenSSL 3 (which wants providers, but that support isn't available in .NET 8 and will come in .NET 9). However, the TPM2TSS engine installs in engines-1.1 rather than engines-3. Any idea how to get SafeEvpPKeyHandle.OpenPrivateKeyFromEngine("tpm2tss", "0x8100002") to be looking/loading the older engine using OpenSSL 3?

bartonjs commented 4 weeks ago

I don't know if ENGINEs built against OSSL 1.1 will successfully load in OSSL 3. OSSL 3 changed a lot of the ABI, and while the ENGINE registration and callbacks might be stable, if they call into any other OSSL functions they might have issues. So, I've never tried to get OSSL3 to load from the engines-1.1 path, and don't know how to do it.

If you have both OSSL3 and OSSL1.1 and want .NET to load the older one, you can set an environment variable: export CLR_OPENSSL_VERSION_OVERRIDE=1.1 (assuming your OpenSSL 1.1 has files named libssl.so.1.1 and libcrypto.so.1.1; if they were libcrypto.so.11 then you'd set the environment variable to 11 (whatever is after the .so.)

GuyWithDogs commented 4 weeks ago

That export command did seem to help the program access the engine, but the error changed to

Unhandled exception. Interop+Crypto+OpenSslCryptographicException: error:26096080:engine routines:ENGINE_load_private_key:failed loading private key

That's using the SafeEvpPKeyHandle.OpenPrivateKeyFromEngine("tpm2tss", "0x8100003") form of the call. Any idea on why that might fail?

GuyWithDogs commented 4 weeks ago

An update on the same system: looks like the openssl utility does work with the handle from the command line. But, as shown above, that same handle can't be read by the .NET program.

sudo sh # export CLR_OPENSSL_VERSION_OVERRIDE=1.1 # openssl s_server -cert rsa.crt -key 0x81000003 -keyform engine -engine tpm2tss -accept 8 engine "tpm2tss" set. Enter password for user key: Using default temp DH parameters ACCEPT # openssl version -a OpenSSL 1.1.1 11 Sep 2018 built on: Tue Sep 24 17:37:19 2024 UTC platform: linux-x86_64 options: bn(64,64) rc4(16x,int) des(int) idea(int) blowfish(ptr) compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -O3 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPADLOCK_ASM -DPOLY1305_ASM -DNDEBUG OPENSSLDIR: "/usr/local/ssl" ENGINESDIR: "/usr/local/lib/engines-1.1" Seeding source: os-specific

bartonjs commented 4 weeks ago

Your commandline information suggests you got password/PIN challenged, which the .NET API can't handle. Perhaps tpm2tss wanted to report "password required" and that got lost in "loading the key didn't work"?

GuyWithDogs commented 4 weeks ago

There's no password on the handle. In that script above, only "Enter" was pressed. At least the routine that created the private key at that handle did not explicitly apply a password to it.

GuyWithDogs commented 4 weeks ago

This is the script that was run to create the key/handle that is the target we want:

# Step 1: Generate a private key in the TPM (RSA 2048)
echo "Generating RSA private key in TPM..."
tpm2_createprimary -C o -c primary.ctx

echo "------------tpm2_create -------------"
tpm2_create -C primary.ctx -G rsa -u key.pub -r key.priv 

echo "------------tpm2_load -------------"
tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx

# Make the key persistent in the TPM
echo "------------tpm2_evictcontrol -------------"
tpm2_evictcontrol -C o -c key.ctx 0x81000003
appcodr commented 3 weeks ago

@bartonjs is there an option to pass the empty password in the SafeEvpPKeyHandle.OpenPrivateKeyFromEngine ? I dont see that based on the documentation but based on how the openssl response is where it expects an empty password, do we need to pass that in from .net as well ?

bartonjs commented 3 weeks ago

is there an option to pass the empty password in the SafeEvpPKeyHandle.OpenPrivateKeyFromEngine?

No, as you noted it doesn't take any options beyond the ENGINE name and the key name.

To identify that this is a null-vs-empty problem, you'll probably have to write a test directly against OpenSSL. It would look something like the following (which notably never cleans up anything it allocated)... which I wrote in this text box, so likely doesn't compile, but it's a start.

int main(int argc, const char** argc)
{
  OPENSSL_init_ssl(
    OPENSSL_INIT_ADD_ALL_CIPHERS |
      OPENSSL_INIT_ADD_ALL_DIGESTS |
      OPENSSL_INIT_LOAD_CONFIG |
      OPENSSL_INIT_NO_ATEXIT |
      OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
      OPENSSL_INIT_LOAD_SSL_STRINGS,
    NULL);

  ENGINE* engine = ENGINE_by_id("tpm2tss");
  EVP_PKEY* pkey = NULL;

  if (!engine)
  {
    printf("Engine did not load\n");
    ERR_print_errors_fp(stdout);
    return 1;
  }

  if (!ENGINE_init(engine))
  {
    printf("ENGINE_init failed.\n");
    ERR_print_errors_fp(stdout);
    return 1;
  }

  pkey = ENGINE_load_private_key(engine, "0x81000003", NULL, NULL);

  if (pkey)
  {
    printf("Key loaded the easy way, hurrah!\n");
    return 0;
  }

  printf("Key did not load the easy way.\n");
  ERR_print_errors_fp(stdout);
  printf("\n\n\n");

  UI_METHOD* uimeth = UI_null();

  pkey = ENGINE_load_private_key(engine, "0x81000003", uimeth, NULL);

  if (pkey)
  {
    printf("Key loaded with UI_null\n");
    return 0;
  }

  printf("Key did not load with UI_null.\n");
  ERR_print_errors_fp(stdout);
  printf("\n\n\n");

  uimeth = UI_openssl();

  pkey = ENGINE_load_private_key(engine, "0x81000003", uimeth, NULL);

  if (pkey)
  {
    printf("Key loaded with UI_openssl\n");
    return 0;
  }

  printf("Key did not load with UI_openssl.\n");
  ERR_print_errors_fp(stdout);
  printf("\n\n\n");

  // Exercise to the reader: If you make it this far, you can try
  // creating a custom UI_METHOD based on
  // https://github.com/openssl/openssl/blob/b372b1f76450acdfed1e2301a39810146e28b02c/apps/apps.c#L183-L275

  printf("Key never loaded :(\n");
  return 1;
}
vcsjones commented 2 weeks ago

Let me see if I can reproduce this and figure out what is going on.

vcsjones commented 2 weeks ago

Okay, I got as far as @GuyWithDogs (great handle btw)

WARNING:esys:src/tss2-esys/api/Esys_ReadPublic.c:320:Esys_ReadPublic_Finish() Received TPM Error
ERROR:esys:src/tss2-esys/esys_tr.c:231:Esys_TR_FromTPMPublic_Finish() Error ReadPublic ErrorCode (0x00000084)
ERROR:esys:src/tss2-esys/esys_tr.c:321:Esys_TR_FromTPMPublic() Error TR FromTPMPublic ErrorCode (0x00000084)
Unhandled exception. Interop+Crypto+OpenSslCryptographicException: error:26096080:engine routines:ENGINE_load_private_key:failed loading private key
   at Interop.Crypto.LoadPrivateKeyFromEngine(String engineName, String keyName)
   at Program.<Main>$(String[] args) in /home/vcsjones/Projects/Program.cs:line 2
vcsjones commented 2 weeks ago

The problem is that we are passing in NULL for the ui_method here (the 3rd parameter to the function pointer):

https://github.com/dotnet/runtime/blob/60b84bb13e499e0866731dbe21973b92b7abed15/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.c#L568

the UI_METHOD is passed as-is to the engine here

https://github.com/openssl/openssl/blob/b10cfd93fd58cc1e9c876be159253b5389dc11a5/crypto/engine/eng_pkey.c#L77

The tpm2-tss-engine rejects that, here:

https://github.com/tpm2-software/tpm2-tss-engine/blob/3d010240b5afbabbf54c35d6c0f6e92ed0a0c0ea/src/tpm2-tss-engine.c#L80-L83

The reason it works fro the OpenSSL command line is because it uses UI_get_default_method, which unless someone changed, is UI_OpenSSL, so it passes the check in the tpm2tss engine.


Slightly annoyingly, it's not strictly safe to pass UI_null. That returns a const UI_METHOD* whereas the engine loading API wants a non-const one, presumably because a "bad" engine could make the null engine do non-null things on the singleton.

bartonjs commented 2 weeks ago

@vcsjones I'm fine with making a UI_METHOD that "just doesn't do anything" if you think that's the best path forward. Or maybe we can convince tpm2tss to not call that an invalid state? :smile:

appcodr commented 2 weeks ago

Thanks @vcsjones for analyzing this further. we are happy to try this if you have a fix from a build or someway for us to try as we got stuck on this problem for a while. Its very promising that you got to the root cause!

vcsjones commented 2 weeks ago

@appcodr in order to give you a test build of the native library I would need to know more about your environment and .NET version. The full output from dotnet --info would be helpful.

appcodr commented 2 weeks ago

@vcsjones I will grab that info on the target machine probably tomorrow. But its on .net 8 on Ubuntu 22.x on a x86 based processor. we tried as self contained app. our desire is to run on .net 8

appcodr commented 2 weeks ago

@vcsjones Here is the info from the target machine where we want to run

$ dotnet --info

Host:
  Version:      8.0.10
  Architecture: x64
  Commit:       81cabf2857
  RID:          ubuntu.22.04-x64

.NET SDKs installed:
  No SDKs were found.

.NET runtimes installed:
  Microsoft.AspNetCore.App 8.0.10 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.35 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.10 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  DOTNET_ROOT       [/home/dfs/.dotnet]

global.json file:
  Not found

Learn more:
https://aka.ms/dotnet/info

Download .NET:
https://aka.ms/dotnet/download
vcsjones commented 2 weeks ago

Pull request #109706 will fix this for .NET 10.

If you want to try this on .NET 8, you can do the following. This should be done on the same OS (Ubuntu 22.04) as you mentioned you are using .NET from.

  1. Checkout the dotnet/runtime git repository git clone https://github.com/dotnet/runtime.git

  2. Check out the v8.0.10 tag in the checked out repository. git checkout v8.0.10 - note, .NET 8.0.11 is released. If you upgrade to .NET 8.0.11, make sure you use that tag instead.

  3. Apply the fix. git cherry-pick -n f0896dbbfdeed482855d7bb3dae9475c09dc68f8 git cherry-pick -n 136e5f37c3fe3c825ee5086d99fcfef151cecbb5

    Those two git commit hashes correspond to the commits in this pull request. https://github.com/dotnet/runtime/pull/109782

  4. Build the repository.

    1. See the requirements from the workflow instructions. In my experience, the 24.04 build requirements also work on 22.04. https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/linux-requirements.md#debianubuntu
    2. Once the dependencies are installed, build with ./build.sh -rc release -c release -s clr+libs.native This does a partial build of the runtime repository. It only builds the native things, since for our purposes here, we don't need a full build, which should make the build time much more reasonable.
  5. With the built repository with our changes, you should have a file over at artifacts/bin/native/net8.0-linux-Release-x64/libSystem.Security.Cryptography.Native.OpenSsl.so

  6. Publish your app as self-contained. dotnet publish -c Release -p SelfContained=true, for example.

  7. In your self contained application output in something like bin/Release/net8.0/linux-x64/publish, you should have libSystem.Security.Cryptography.Native.OpenSsl.so. Replace it with the one you built and identified in step 5.

  8. Run your self-contained application.

appcodr commented 2 weeks ago

Thanks @vcsjones . I see on the issue page that there is a backport comment. Is that in .net 8.0 ?

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/11820846271

vcsjones commented 2 weeks ago

Currently, the fix has only been merged for .NET 10.

I have opened backport requests to attempt to get the fixes in .NET 8.0 and .NET 9.0. The presence of a backport pull request does not guarantee anything. The request may be rejected. I will keep this issue updated with information about where the fix lands, if at all, in servicing.

appcodr commented 2 weeks ago

Thanks @vcsjones . with .net 10 its an year out for us to get this in hands. so would be good if it gets in .net 9 considering it just released.

bartonjs commented 2 weeks ago

@appcodr The backport is more likely to happen if you can verify that it solves the problem you are having. If there's another problem hiding behind this one we'd like to know it and put them in at the same time.

serkanturhanxl commented 2 weeks ago

Pull request #109706 will fix this for .NET 10.

If you want to try this on .NET 8, you can do the following. This should be done on the same OS (Ubuntu 22.04) as you mentioned you are using .NET from.

  1. Checkout the dotnet/runtime git repository git clone https://github.com/dotnet/runtime.git
  2. Check out the v8.0.10 tag in the checked out repository. git checkout v8.0.10 - note, .NET 8.0.11 is released. If you upgrade to .NET 8.0.11, make sure you use that tag instead.
  3. Apply the fix. git cherry-pick -n f0896dbbfdeed482855d7bb3dae9475c09dc68f8 git cherry-pick -n 136e5f37c3fe3c825ee5086d99fcfef151cecbb5 Those two git commit hashes correspond to the commits in this pull request. [release/8.0-staging] Re-try loading ENGINE keys with a non-NULL UI_METHOD #109782
  4. Build the repository.

    1. See the requirements from the workflow instructions. In my experience, the 24.04 build requirements also work on 22.04. https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/linux-requirements.md#debianubuntu
    2. Once the dependencies are installed, build with ./build.sh -rc release -c release -s clr+libs.native This does a partial build of the runtime repository. It only builds the native things, since for our purposes here, we don't need a full build, which should make the build time much more reasonable.
  5. With the built repository with our changes, you should have a file over at artifacts/bin/native/net8.0-linux-Release-x64/libSystem.Security.Cryptography.Native.OpenSsl.so
  6. Publish your app as self-contained. dotnet publish -c Release -p SelfContained=true, for example.
  7. In your self contained application output in something like bin/Release/net8.0/linux-x64/publish, you should have libSystem.Security.Cryptography.Native.OpenSsl.so. Replace it with the one you built and identified in step 5.
  8. Run your self-contained application.

I followed the each step for local build but it gives some error. Here is the build logs.

./build.sh -rc release -c release -s clr+libs.native Determining projects to restore... Tool 'coverlet.console' (version '6.0.0') was restored. Available commands: coverlet Tool 'dotnet-reportgenerator-globaltool' (version '5.0.2') was restored. Available commands: reportgenerator Tool 'microsoft.dotnet.xharness.cli' (version '8.0.0-prerelease.24229.2') was restored. Available commands: xharness Tool 'microsoft.visualstudio.slngen.tool' (version '10.0.0') was restored. Available commands: slngen Tool 'dotnet-format' (version '6.0.240501') was restored. Available commands: dotnet-format

Restore was successful. Restored /home/dfs/.nuget/packages/microsoft.dotnet.arcade.sdk/8.0.0-beta.24426.2/tools/Tools.proj (in 3.63 sec). Determining projects to restore... Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/WasmBuildTasks/WasmBuildTasks.csproj (in 1.06 sec). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj (in 1.06 sec). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj (in 1.07 sec). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj (in 1.06 sec). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/Microsoft.NET.WebAssembly.Webcil/Microsoft.NET.WebAssembly.Webcil.csproj (in 114 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/MobileBuildTasks/MobileBuildTasks.csproj (in 102 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks.csproj (in 160 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/LibraryBuilder/LibraryBuilder.csproj (in 188 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj (in 79 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj (in 150 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/Crossgen2Tasks/Crossgen2Tasks.csproj (in 351 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/tasks.proj (in 40 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj (in 88 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/native/libs/build-native.proj (in 48 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/native/corehost/corehost.proj (in 28 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/libraries/native-binplace.proj (in 95 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/installer.tasks/installer.tasks.csproj (in 484 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj (in 407 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj (in 271 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.csproj (in 14 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/tieringtest/tieringtest.csproj (in 22 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/runincontext/runincontext.csproj (in 22 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj (in 99 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/r2rdump/R2RDump.csproj (in 195 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/r2rtest/R2RTest.csproj (in 597 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/crossgen2/crossgen2_inbuild.csproj (in 63 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/crossgen2/crossgen2.csproj (in 63 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/runtime-prereqs.proj (in 65 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj (in 18 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj (in 15 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/System.Private.StackTraceMetadata.csproj (in 20 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj (in 17 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/nativeaot/System.Private.DisabledReflection/src/System.Private.DisabledReflection.csproj (in 15 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj (in 15 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/crossgen-corelib.proj (in 9 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj (in 30 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/.nuget/coreclr-packages.proj (in 22 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/.nuget/Microsoft.NETCore.TestHost/Microsoft.NETCore.TestHost.proj (in 22 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/.nuget/Microsoft.NETCore.ILDAsm/Microsoft.NETCore.ILDAsm.proj (in 17 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/.nuget/Microsoft.NETCore.ILAsm/Microsoft.NETCore.ILAsm.proj (in 18 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/Build.proj (in 10 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler/repro/repro.csproj (in 38 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj (in 243 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj (in 81 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj (in 76 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/dotnet-pgo/dotnet-pgo.csproj (in 1.48 sec). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj (in 100 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj (in 114 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler.MetadataTransform.csproj (in 73 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/ILCompiler.DependencyAnalysisFramework.csproj (in 25 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj (in 66 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ILCompiler.Build.Tasks.csproj (in 69 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj (in 89 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/InjectResource/InjectResource.csproj (in 16 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/runtime.proj (in 12 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/tools/SuperFileCheck/SuperFileCheck.csproj (in 173 ms). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/MonoTargetsTasks/ILStrip/AssemblyStripper/AssemblyStripper.csproj (in 8.98 sec). Restored /home/dfs/Desktop/dotnetbuild/runtime/src/tasks/MonoTargetsTasks/MonoTargetsTasks.csproj (in 9.14 sec). System.Private.CoreLib.Generators -> /home/dfs/Desktop/dotnetbuild/runtime/artifacts/bin/System.Private.CoreLib.Generators/Release/netstandard2.0/System.Private.CoreLib.Generators.dll Microsoft.Interop.SourceGeneration -> /home/dfs/Desktop/dotnetbuild/runtime/artifacts/bin/Microsoft.Interop.SourceGeneration/Release/netstandard2.0/Microsoft.Interop.SourceGeneration.dll LibraryImportGenerator -> /home/dfs/Desktop/dotnetbuild/runtime/artifacts/bin/LibraryImportGenerator/Release/netstandard2.0/Microsoft.Interop.LibraryImportGenerator.dll System.Private.CoreLib -> /home/dfs/Desktop/dotnetbuild/runtime/artifacts/bin/System.Private.CoreLib/ref/Release/net8.0/System.Private.CoreLib.dll CSC : error CS2012: Cannot open '/home/dfs/Desktop/dotnetbuild/runtime/artifacts/obj/coreclr/System.Private.CoreLib/x64/Release/System.Private.CoreLib.dll' for writing -- 'Access to the path '/home/dfs/Desktop/dotnetbuild/runtime/artifacts/obj/coreclr/System.Private.CoreLib/x64/Release/System.Private.CoreLib.dll' is denied.' [/home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj]

Build FAILED.

CSC : error CS2012: Cannot open '/home/dfs/Desktop/dotnetbuild/runtime/artifacts/obj/coreclr/System.Private.CoreLib/x64/Release/System.Private.CoreLib.dll' for writing -- 'Access to the path '/home/dfs/Desktop/dotnetbuild/runtime/artifacts/obj/coreclr/System.Private.CoreLib/x64/Release/System.Private.CoreLib.dll' is denied.' [/home/dfs/Desktop/dotnetbuild/runtime/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj] 0 Warning(s) 1 Error(s)

Time Elapsed 00:05:20.41 Build failed with exit code 1. Check errors above. Image

vcsjones commented 2 weeks ago

@serkanturhanxl based on /dfs/ in your file path, I am guessing you are building on a DFS remote file system. I don't know how well the build system tolerates that - it might only build on an ext4 or similar file system.

serkanturhanxl commented 2 weeks ago

@serkanturhanxl based on /dfs/ in your file path, I am guessing you are building on a DFS remote file system. I don't know how well the build system tolerates that - it might only build on an ext4 or similar file system.

It's a dedicated Linux machine I connected via rdp. dfs is the user name and the folder I created on desktop. Should I build in some specific folder?

I ran under sudo and the error changed.

Image

serkanturhanxl commented 2 weeks ago

@vcsjones here is the error log file it was mentioned on the console. Image

vcsjones commented 2 weeks ago

@serkanturhanxl looking at the console output behind the folder, it looks like it wants zlib. You should do sudo apt-get install zlib1g-dev.

I forgot that .NET 8 had dependency on zlib, but .NET 9 removed the dependency, so is not listed as a build requirement anymore in our documentation.

appcodr commented 2 weeks ago

Thanks @vcsjones. The build worked and we are not getting the error we got with SafeEvpPKeyHandle.OpenPrivateKeyFromEngine before. So thats great news! But we are trying to use that cert for a TLS session and we get an error that

TCP Server started. Listening for incoming connections.. Incoming connection from x.x.x.x System. Security.Cryptography.CryptographicException: m_safeCertContext is an invalid handle. at System.Security.Cryptography.X509Certificates.X509Certificate.ThrowIfInvalid() at System.Security.Cryptography.X509Certificates.509Certificate2.get_HasPrivateKey() at System.Net.Security.SslAuthenticationOptions.UpdateOptions(SslServerAuthenticationOptions s slServerAuthenticationoptions) at System.Net.Security.SslStream.AuthenticateAsServerSslServerAuthenticationOptions sslServer AuthenticationOptions)

this is on OpenSSL version: 1010100F

vcsjones commented 2 weeks ago

@appcodr can you put together a small example application that demonstrates that behavior? That looks like a disposed certificate is trying to be used somewhere.

appcodr commented 1 week ago

Okay, I think we got past the invalid handle error as you pointed out we had the object disposed. Now the connection handshake happens but fails with a wierd openssl error. we have set the TLS version to TLS 1.2, it might be that the certificate chain needs to be placed in a trusted root (Windows makes this easier :) ) . We are making a separate sample app and will share it with you soon,

System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.
---> Interop+Crypto+OpenSslCryptographicException: error:0A00041B:SSL routines::tlsv1 alert decrypt error
   --- End of inner exception stack trace ---
   at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan`1 input, Byte[]& sendBuf, Int32& sendCount)
   at System.Net.Security.SslStreamPal.HandshakeInternal(SafeDeleteSslContext& context, ReadOnlySpan`1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
   at System.Net.Security.SslStream.AuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions)
   at AServerTest.ServerNew.Listen(IPAddress ip, Int32 port) in 
appcodr commented 1 week ago

@vcsjones also if you have a sample program that worked on .net 10 with the TPM on Ubuntu, can you share that as well please?

bartonjs commented 1 week ago

it might be that the certificate chain needs to be placed in a trusted root

If you mean the certificate on your side of the connection, that's never required on Linux (or, really "anything but Windows")... we just do the right thing.

If you mean the certificate from the other side, you can add it to your system trust, or user trust, or use API to add it as contextual trust, or just write a custom validator and accept it there.

(Windows makes this easier :) )

If you mean TLS in general, I disagree :smile:. If you mean TLS with hardware-based private keys, yeah, .NET on Windows has had longer to work the problems out.

appcodr commented 1 week ago

@bartonjs the certificate i was referring to was the server certificate and we are using the TPM to store the private key. and yes I meant the TLS with TPM based private keys - windows abstracts it better.

I think we are close now, only issue is the handshake error we are getting and if we get past it would be good.

serkanturhanxl commented 1 week ago

@bartonjs

@appcodr can you put together a small example application that demonstrates that behavior? That looks like a disposed certificate is trying to be used somewhere.

I created a sample Client and Server application. TPMHandler

Here are the exceptions I'm getting. From Server: TCP Server started. Listening for incoming connections... Incoming connection from 127.0.0.1:53478 System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL. ---> Interop+Crypto+OpenSslCryptographicException: error:0A00041B:SSL routines::tlsv1 alert decrypt error --- End of inner exception stack trace --- at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan1 input, Byte[]& sendBuf, Int32& sendCount) at System.Net.Security.SslStreamPal.HandshakeInternal(SafeDeleteSslContext& context, ReadOnlySpan1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) --- End of inner exception stack trace --- at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken) at System.Net.Security.SslStream.AuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions) at TPMServer.Server.Listen(IPAddress ip, Int32 port) in C:\Users\xxx\source\repos\TPMHandler\TPMServer\Server.cs:line 79 System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL. ---> Interop+Crypto+OpenSslCryptographicException: error:0A00041B:SSL routines::tlsv1 alert decrypt error --- End of inner exception stack trace --- at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan1 input, Byte[]& sendBuf, Int32& sendCount) at System.Net.Security.SslStreamPal.HandshakeInternal(SafeDeleteSslContext& context, ReadOnlySpan1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) --- End of inner exception stack trace --- at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken) at System.Net.Security.SslStream.AuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions) at TPMServer.Server.Listen(IPAddress ip, Int32 port) in C:\Users\xxx\source\repos\TPMHandler\TPMServer\Server.cs:line 79

From Client:

Client connected. Exception: Authentication failed, see inner exception. Inner exception: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL. Authentication failed - closing the connection.

Any suggestion?

bartonjs commented 1 week ago

To make sure things are deterministic, instead of subjecting them to the whims of the finalizer, you should using (or otherwise .Dispose() of) SafeEvpPKeyHandle privateKey, RSAOpenSsl rsa, and X509Certificate2 tmpCert.

After getting the certificate back from GetCertificate(), try to use the private key directly:

using (RSA rsa = cert.GetRSAPrivateKey())
{
    byte[] sig = rsa.SignData(Array.Empty<byte>(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    Console.WriteLine($"Signature of empty data: {Convert.ToHexString(sig)}");
}

If that works, but SslStream does not, you'll probably want to run WireShark or some other network capture to see if the handshake failure had interesting details that didn't manifest in the exception.

vcsjones commented 1 week ago

Another thing you can do run the TPMServer with TSS logging. You can experiment with different levels of logging environment variables, such as export TSS2_LOG=all+DEBUG for debug output, or export TSS2_LOG=all+TRACE for trace-level output.

These are very verbose log outputs that write to /dev/stderr. You can use export TSS2_LOGFILE=/some/file/path to write to a file instead.

These logs will show commands sent and received to the TPM.

Note if you are using sudo for testing purposes to talk to the TPM, you need to use the -E flag with sudo so the environment variable propagates.

serkanturhanxl commented 1 week ago

To make sure things are deterministic, instead of subjecting them to the whims of the finalizer, you should using (or otherwise .Dispose() of) SafeEvpPKeyHandle privateKey, RSAOpenSsl rsa, and X509Certificate2 tmpCert.

After getting the certificate back from GetCertificate(), try to use the private key directly:

using (RSA rsa = cert.GetRSAPrivateKey()) { byte[] sig = rsa.SignData(Array.Empty(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); Console.WriteLine($"Signature of empty data: {Convert.ToHexString(sig)}"); } If that works, but SslStream does not, you'll probably want to run WireShark or some other network capture to see if the handshake failure had interesting details that didn't manifest in the exception.

@bartonjs The signature part seems working. Signature of empty data: 57F6D1C584F9CB7BC1D0693D695452BFFBA01D5520E5F3BE4BEB5D6C5199CD9C7E3070FE1C25389E8D20608C7FA27AE5487E3F2454FCC28DD85703798245B7E3253C31D59F9305DD12663929CE30F641DAB74D43DD941A3745FF913E7D061694DC7402C746B4DDBFA15DE085D94960A70D3C8D2A0926C8464E7917BFA414250C603942DE77580FF7FBA812522C3E450B89EB412E0AA293E65D128381CF38B33F9192A98A70E55CDE16D3243AD13B07189BC2A00BC79B87C8B2FDF27A02F648387A832005BB3F00291AD8A2415BD8FE62D234D7B47A03D0125372095BB36B98700B54FA68B2EDC82C5BD426C4283C5EBBF4796040975BAABF22EE95DDEE0E2C06

Still failing on the Authentication part. I added a parameter to force it to use "enabledSslProtocols:System.Security.Authentication.SslProtocols.Tls12" the error changed a bit I think.

Incoming connection from 127.0.0.1:49012 System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL. ---> Interop+Crypto+OpenSslCryptographicException: error:0A0000F4:SSL routines::unexpected message --- End of inner exception stack trace --- at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan1 input, Byte[]& sendBuf, Int32& sendCount) at System.Net.Security.SslStreamPal.HandshakeInternal(SafeDeleteSslContext& context, ReadOnlySpan1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) --- End of inner exception stack trace --- at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken) at System.Net.Security.SslStream.AuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions) at TPMServer.Server.Listen(IPAddress ip, Int32 port) in C:\Users\xxx\source\repos\TPMHandler\TPMServer\Server.cs:line 104 System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL. ---> Interop+Crypto+OpenSslCryptographicException: error:0A0000F4:SSL routines::unexpected message --- End of inner exception stack trace --- at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan1 input, Byte[]& sendBuf, Int32& sendCount) at System.Net.Security.SslStreamPal.HandshakeInternal(SafeDeleteSslContext& context, ReadOnlySpan1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) --- End of inner exception stack trace --- at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken) at System.Net.Security.SslStream.AuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions) at TPMServer.Server.Listen(IPAddress ip, Int32 port) in C:\Users\xxx\source\repos\TPMHandler\TPMServer\Server.cs:line 104

@vcsjones I tried to get extended logs but wasn't helpful.

I did check with wireshark this is the error I see there also. This error screen for same as previously I was getting( tlsv1 alert decrypt error ) Image

vcsjones commented 1 week ago

Just to double-check, did you actually see this work with openssl s_server?

openssl s_server -cert cert.pem -key 0x81000003 -keyform engine -engine tpm2tss

Then actually make a request to it with curl?

curl https://localhost:4433

For myself, I appear to have reached the limits of what my TPM can do. It fails with:

ERROR:esys:src/tss2-esys/api/Esys_HashSequenceStart.c:95:Esys_HashSequenceStart() Esys Finish ErrorCode (0x000b0143) 
ERROR:esys:src/tss2-esys/esys_iutil.c:1134:iesys_check_sequence_async() Esys called in bad sequence. 
ERROR:esys:src/tss2-esys/api/Esys_FlushContext.c:66:Esys_FlushContext() Error in async function ErrorCode (0x00070007) 

The error codes map to

tpm2_rc_decode 0x000b0143
> rmt:error(2.0): command code not supported

tpm2_rc_decode 0x00070007
> esapi:Function called in the wrong order

That was produced with openssl s_server, not .NET.

I can use cryptographic primitives, like SignHash. It just falls over with TLS.

serkanturhanxl commented 1 week ago

the limits of what my TPM can do. It fails with:

ERROR:esys:src/tss2-esys/api/Esys_HashSequenceStart.c:95:Esys_HashSequenceStart() Esys Finish ErrorCode (0x000b0143) 
ERROR:esys:src/tss2-esys/esys_iutil.c:1134:iesys_check_sequence_async() Esys called in bad sequence. 
ERROR:esys:src/tss2-esys/api/Esys_FlushContext.c:66:Esys_FlushContext() Error in async function ErrorCode (0x00070007) 

Here is the results for same commands as you mentioned.

Server:

openssl s_server -cert _ssl_certificate.pem -key 0x81000006 -keyform engine -engine tpm2tss
engine "tpm2tss" set.
Enter password for user key:
Using default temp DH parameters
ACCEPT
ERROR
127543978133312:error:1409441B:SSL routines:ssl3_read_bytes:tlsv1 alert decrypt error:ssl/record/rec_layer_s3.c:1527:SSL alert number 51
shutting down SSL
CONNECTION CLOSED

Client:

curl https://localhost:4433
curl: (35) error:02000088:rsa routines::salt length check failed
vcsjones commented 1 week ago

127543978133312:error:1409441B:SSL routines:ssl3_read_bytes:tlsv1 alert decrypt error:ssl/record/rec_layer_s3.c:1527:SSL alert number 51

At this point I think we have identified that the issue you are running in to isn't related to .NET. It looks like you are getting the same "alert decrypt error" from openssl s_server as you are in .NET.

I don't think there is a way for us to proceed with any assistance here until you can at least identify a scenario in which it does work with openssl s_server but does not work with .NET.

serkanturhanxl commented 1 week ago

127543978133312:error:1409441B:SSL routines:ssl3_read_bytes:tlsv1 alert decrypt error:ssl/record/rec_layer_s3.c:1527:SSL alert number 51

At this point I think we have identified that the issue you are running in to isn't related to .NET. It looks like you are getting the same "alert decrypt error" from openssl s_server as you are in .NET.

I don't think there is a way for us to proceed with any assistance here until you can at least identify a scenario in which it does work with openssl s_server but does not work with .NET.

Thank you. Can you merge the changes you made it for .NET 10 into .NET 8 please. I think that change you made is working as expected. If we can resolve the openssl issue that part will be ready on .net8 for us.

bartonjs commented 1 week ago

Can you merge the changes you made it for .NET 10 into .NET 8 please.

Could you share your reason for that request? Currently you're the only one asking for it, and we've seen that it won't solve your scenario. So this would be a change in an incremental update that seemingly no one actually wants.

In general "we should fix it just because it's broken" is vNext-only. We've already fixed it for .NET 10; but we'd need a reason to do servicing. (Right now, I'd take this into the meeting and they'd ask "Is this a security thing?" ("Not really, no.") "Is this something reported by a user, or did we find it ourselves?" ("User") "Have they verified it fixes their problem?" ("Sort of. They verified it fixes a problem, but their problem remains unsolved, and may be unsolvable...")... and I don't see it going well after that :smile:)

appcodr commented 6 days ago

@bartonjs @vcsjones - I think we found the root cause of the error and it translates to the cipher suite picked by OpenSSL. So now in command line we are able to successfully connect use it by this parameter

openssl s_server -cert _ssl_certificate.pem -key 0x81000006 -keyform engine -engine tpm2tss -cipher AES128-GCM-SHA256 -debug -msg -min_protocol TLSv1.2 -max_protocol TLSv1.2

openssl s_client -connect localhost:4433 -cipher AES128-GCM-SHA256 -debug -msg -min_protocol TLSv1.2 -max_protocol TLSv1.2

But with .net core code, does specifying the cipher option in parameters get picked when using with openssl ? Probably need your guidance on how we achieve the command line parameters in our code. We tried this but we get errors indicating encryption not supported but that might be not specifying the options right


SslServerAuthenticationOptions sslOptions = new SslServerAuthenticationOptions
{
    EnabledSslProtocols = SslProtocols.Tls12, // Only use these protocols
    CipherSuitesPolicy = new CipherSuitesPolicy(new[]
    {
        TlsCipherSuite.TLS_AES_128_GCM_SHA256, // Include only cipher suites compatible with SHA256
    })
};

Appreciate your inputs on this!

vcsjones commented 6 days ago

@appcodr TlsCipherSuite.TLS_AES_128_GCM_SHA256 corresponds to the TLS v1.3 cipher suite. Assuming you have an RSA certificate, try TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, or, if you are using an ECDSA certificate, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256

appcodr commented 6 days ago

Thanks @vcsjones . So specifying the options this way would carry down to openssl right ?

bartonjs commented 6 days ago
$ openssl ciphers -v -stdname AES128-GCM-SHA256
TLS_AES_256_GCM_SHA384                        - TLS_AES_256_GCM_SHA384         TLSv1.3 Kx=any      Au=any   Enc=AESGCM(256)            Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256                  - TLS_CHACHA20_POLY1305_SHA256   TLSv1.3 Kx=any      Au=any   Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256                        - TLS_AES_128_GCM_SHA256         TLSv1.3 Kx=any      Au=any   Enc=AESGCM(128)            Mac=AEAD
TLS_RSA_WITH_AES_128_GCM_SHA256               - AES128-GCM-SHA256              TLSv1.2 Kx=RSA      Au=RSA   Enc=AESGCM(128)            Mac=AEAD

This output shows that the OpenSSL configuration name of "AES128-GCM-SHA256" maps to IANA's "TLS_RSA_WITH_AES_128_GCM_SHA256"; and we use the IANA names.

bartonjs commented 6 days ago

specifying the options this way would carry down to openssl right ?

Yep.

Although I'd be remiss in saying you're choosing a ciphersuite that doesn't have the Perfect Forward Secrecy (PFS) characteristic. If that works for you, I recommend trying ECDHE-RSA-AES128-GCM-SHA256 (commandline) / TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (API).