DuendeSoftware / Support

Support for Duende Software products
21 stars 0 forks source link

System.Security.Cryptography.CryptographicException #1428

Open gunnars04 opened 5 days ago

gunnars04 commented 5 days ago

Which version of Duende BFF are you using? 6.3.10 Which version of .NET are you using? .net 6

db: I'm using azure sql database (hyperscale).

Describe the bug:

The error: { "Exception": {"Type":"System.Security.Cryptography.CryptographicException", "TargetSite":"Byte[] UnprotectCore(Byte[], Boolean, UnprotectStatus ByRef)", "Message":"The key [thekey] was not found in the key ring. For more information go to http://aka.ms/dataprotectionwarning", "Data":{}, "Source":"Microsoft.AspNetCore.DataProtection", "HResult":-2146233087, "StackTrace":" at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, Boolean allowOperationsOnRevokedKeys, UnprotectStatus& status)\n at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData)\n at Microsoft.AspNetCore.DataProtection.DataProtectionCommonExtensions.Unprotect(IDataProtector protector, String protectedData)\n at Duende.IdentityServer.Services.KeyManagement.DataProtectionKeyProtector.Unprotect(SerializedKey key) in /_/src/IdentityServer/Services/Default/KeyManagement/DataProtectionKeyProtector.cs:line 56\n at Duende.IdentityServer.Services.KeyManagement.KeyManager.<GetAllKeysFromStoreAsync>b__20_0(SerializedKey x) in /_/src/IdentityServer/Services/Default/KeyManagement/KeyManager.cs:line 436"}, "Level": "ERROR", "Date": "2024-10-03T06:00:09.0003937Z", "Logger": "Duende.IdentityServer.Services.KeyManagement.KeyManager", "Message": "Error unprotecting the IdentityServer signing key with kid 2D6745287627ABDB5C59513DCC155E0A. This is likely due to the ASP.NET Core data protection key that was used to protect it is not available. This could occur because data protection has not been configured properly for your load balanced environment, or the IdentityServer signing key store was populated with keys from a different environment with different ASP.NET Core data protection keys. Once you have corrected the problem and if you keep getting this error then it is safe to delete the specific IdentityServer signing key with that kid.", "Application": "[myidentityserver]", "CallSite": "Duende.IdentityServer.Services.KeyManagement.KeyManager.GetAllKeysFromStoreAsync(/_/src/IdentityServer/Services/Default/KeyManagement/KeyManager.cs:436)", "Thread": "8" }

If I lookup the db table for [thekey], I can't find it (empty result).

SELECT top 100 *
  FROM [dbo].[PersistedGrants]
  where [key] = '[thekey]'
  order by CreationTime desc

In my startup.cs I added:

            // using Microsoft.AspNetCore.DataProtection;
            services.AddDataProtection()
                .SetApplicationName("IdentityServer")
                .PersistKeysToDbContext<MyKeysContext>();

According to the docs, it should look like this:

builder.Services.AddDataProtection()
  .PersistKeysToFoo()

  // Choose an extension method for key protection, such as 
  // ProtectKeysWithCertificate, ProtectKeysWithAzureKeyVault
  .ProtectKeysWithBar() -> I'm skipping this part, is it necessary?

  .SetApplicationName("IdentityServer");

https://docs.duendesoftware.com/identityserver/v7/deployment/data_protection/

josephdecock commented 5 days ago

The signing keys are stored in a separate Keys table, rather than in the persisted grants table that you're querying - that's why you're not seeing any keys. Please try again with the keys table, and let us know if you need more assistance.

gunnars04 commented 5 days ago

@josephdecock It's also not found here: SELECT TOP (1000) * FROM [dbo].[Keys] where id = '[thekey]'

thekey is here: The error: { "Exception": {"Type":"System.Security.Cryptography.CryptographicException", "TargetSite":"Byte[] UnprotectCore(Byte[], Boolean, UnprotectStatus ByRef)", "Message":"The key [thekey] was not found in the key ring. For more information go to

josephdecock commented 5 days ago

There are 2 types of cryptographic keys involved here - signing keys are managed by identity server and create the signatures on your jwts, while data protection keys are managed by asp.net itself, and protect data that shouldn't be read or written by anything other than the web server. The connection between the two is that the signing key is protected at rest by data protection.

"Error unprotecting the IdentityServer signing key with kid 2D6745287627ABDB5C59513DCC155E0A"

This log message is coming from IdentityServer itself. It's telling you that your signing key can't be decrypted and that it has id 2D6745287627ABDB5C59513DCC155E0A. select * from keys where id = '2D6745287627ABDB5C59513DCC155E0A' should find it.

The signing key is not stored in plain text in the keys table however. It is encrypted and signed with data protection, which again has its own cryptographic key material.

"The key [thekey] was not found in the key ring. For more information go to http://aka.ms/dataprotectionwarning"

This log message is coming from the ASP.NET data protection calls that IdentityServer made when it tried to unprotect your signing key. It's telling you which data protection key was not found. Those data protection keys are stored in a separate table (whatever you named it when you implemented IDataProtectionKeyContext), so that's why querying for its id in the signing keys table isn't finding anything.

josephdecock commented 5 days ago

Separately, you have a comment above wondering if you need to call a ProtectKeysWith method, and yes, I strongly recommend it. Without that, your data protection keys are stored in plain text in the database. The data protection keys are critical secrets for an IdentityServer implementation because they encrypt a great deal of sensitive data at rest (including your signing keys) and prevent sensitive data that is round-tripped through the browser from being tampered with. The private key component of the signing keys are also critical secrets for IdentityServer because a valid signature provides integrity and non-repudiation guarantees that allow client applications and APIs to trust those tokens.