[Bug] "System.Security.Cryptography.CryptographicException: Invalid algorithm specified." when using certificate on NET 4.8
See original GitHub issueWhich Version of MSAL are you using ? Note that to get help, you need to run the latest version. Preview version are also ok. For ADAL, please log issues to https://github.com/AzureAD/azure-activedirectory-library-for-dotnet
MSAL 4.2.1
Platform
net48
What authentication flow has the issue?
- Desktop / Mobile
- Interactive
- Integrated Windows Auth
- Username Password
- Device code flow (browserless)
- Client credentials flow (this is missing from the template 😞)
- Web App
- Authorization code
- OBO
- Web API
- OBO
Other? - please describe; Using ConfidentialClientApplicationBuilder with an RSA certificate.
Is this a new or existing app?
c. This is a new app or experiment
Repro
using Microsoft.Identity.Client;
using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace MSALTest
{
class Program
{
static async Task Main(string[] args)
{
const string authorityIdTemplate = "https://login.microsoftonline.com/{0}";
const string clientId = "<insert clientId>";
const string tenantId = "<insert tenantId>";
X509Certificate2 cert = new X509Certificate2(@"rsakey.pfx", "secret", X509KeyStorageFlags.Exportable);
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithCertificate(cert)
.WithAuthority(string.Format(authorityIdTemplate, tenantId))
.Build();
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
AuthenticationResult authResult = await app.AcquireTokenForClient(ewsScopes).ExecuteAsync();
Console.WriteLine(authResult.AccessToken);
}
}
}
Expected behavior An access token is received and printed to the console assuming everything is set up properly (or some authentication error is returned otherwise)
Actual behavior On .net 4.8 this exception is thrown:
Unhandled Exception: System.Security.Cryptography.CryptographicException: Invalid algorithm specified.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash)
at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash)
at Microsoft.Identity.Client.Platforms.net45.NetDesktopCryptographyManager.SignWithCertificate(String message, X509Certificate2 certificate)
at Microsoft.Identity.Client.Internal.JsonWebToken.Sign(ClientCredentialWrapper credential, Boolean sendCertificate)
at Microsoft.Identity.Client.Internal.Requests.ClientCredentialHelper.CreateClientCredentialBodyParameters(ICoreLogger logger, ICryptographyManager cryptographyManager, ClientCredentialWrapper clientCredential, String clientId, AuthorityEndpoints endpoints, Boolean sendX5C)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.<SendTokenRequestAsync>d__21.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.<RunAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Identity.Client.ApiConfig.Executors.ConfidentialClientExecutor.<ExecuteAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at MSALTest.Program.<Main>d__0.MoveNext() in C:\projects\kb-f2-main\MSALTest\Program.cs:line 21
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at MSALTest.Program.<Main>(String[] args)
However when running the same code on netcore2.2 everything works as it should.
Possible Solution
The newRsa
at https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/8bfa15c5db433a3def00ff4588fa274f15579c4f/src/Microsoft.Identity.Client/Platforms/net45/NetDesktopCryptographyManager.cs#L102-L105 has _parameters.ProviderType == 1 (PROV_RSA_FULL)
but to actually be able to use sha2 it needs to be 24 (PROV_RSA_AES). The logic in the branch for .net 4.6 and below actually ensures this, but the one for .net 4.7+ does not, so it is written so it will only work on .net 4.6 and below.
The problem here seems to be that RSA.SignData
was only added in .net 4.6, so it can’t reference that when targeting .net 4.5. Maybe the best solution would just be to use reflection to call the proper method if it absolutely must be built with a single version targeting alle netfx version from 4.5 onwards.
Issue Analytics
- State:
- Created 4 years ago
- Comments:8 (6 by maintainers)
Top GitHub Comments
If anyone else encounters this issue then the following workaround can be used until it is fixed:
Call
Sha256NotSupportedWorkaround
on the certificate before handing it off to the builder.Closing as per Travis’s Fixed label - @trwalke - please reopen if not fixed.