question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Bug] "System.Security.Cryptography.CryptographicException: Invalid algorithm specified." when using certificate on NET 4.8

See original GitHub issue

Which 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:closed
  • Created 4 years ago
  • Comments:8 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
poizan42commented, Aug 8, 2019

If anyone else encounters this issue then the following workaround can be used until it is fixed:

    private static RSACryptoServiceProvider GetCryptoProviderForSha256(RSACryptoServiceProvider rsaProvider)
    {
        const int PROV_RSA_AES = 24;    // CryptoApi provider type for an RSA provider supporting sha-256 digital signatures

        // ProviderType == 1(PROV_RSA_FULL) and providerType == 12(PROV_RSA_SCHANNEL) are provider types that only support SHA1.
        // Change them to PROV_RSA_AES=24 that supports SHA2 also. Only levels up if the associated key is not a hardware key.
        // Another provider type related to rsa, PROV_RSA_SIG == 2 that only supports Sha1 is no longer supported
        if ((rsaProvider.CspKeyContainerInfo.ProviderType == 1 || rsaProvider.CspKeyContainerInfo.ProviderType == 12) && !rsaProvider.CspKeyContainerInfo.HardwareDevice)
        {
            CspParameters csp = new CspParameters
            {
                ProviderType = PROV_RSA_AES,
                KeyContainerName = rsaProvider.CspKeyContainerInfo.KeyContainerName,
                KeyNumber = (int)rsaProvider.CspKeyContainerInfo.KeyNumber
            };

            if (rsaProvider.CspKeyContainerInfo.MachineKeyStore)
            {
                csp.Flags = CspProviderFlags.UseMachineKeyStore;
            }

            //
            // If UseExistingKey is not specified, the CLR will generate a key for a non-existent group.
            // With this flag, a CryptographicException is thrown instead.
            //
            csp.Flags |= CspProviderFlags.UseExistingKey;
            return new RSACryptoServiceProvider(csp);
        }

        return rsaProvider;
    }

    private static void Sha256NotSupportedWorkaround(X509Certificate2 cert)
    {
      if (cert.PrivateKey is RSACryptoServiceProvider orgKey)
      {
        try
        {
          cert.PrivateKey = GetCryptoProviderForSha256(orgKey);
        }
        finally
        {
          if (!ReferenceEquals(orgKey, cert.PrivateKey))
            orgKey.Dispose();
        }
      }
    }

Call Sha256NotSupportedWorkaround on the certificate before handing it off to the builder.

0reactions
bgavrilMScommented, Aug 29, 2019

Closing as per Travis’s Fixed label - @trwalke - please reopen if not fixed.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why am I getting "Invalid algorithm specified" exception
For dot net framework 4.7.0 or higher is not taking the sha1 so configure the below in application start. it's worked fine for...
Read more >
Invalid algorithm specified when using actual cert file
The Microsoft RSA SChannel Cryptographic Provider supports SHA-1 signatures but not SHA-256. In the latest release (v2.6.0.14) of the SAML2 DLL ...
Read more >
Accessing and using certificate private keys in .NET ...
An X509Certificate2 class has a PrivateKey property of AsymmetricAlgorithm type. AsymmetricAlgorithm class is abstract class for any asymmetric ...
Read more >
NET 4.7.1 (and higher) no longer supports SHA1 in SignedXml
Cryptography.CryptographicException : Invalid algorithm specified ... at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey ...
Read more >
SAML logins are failing with the error 'Invalid algorithm ...
The problem was solved in both cases, by re-importing the site's SSL certificate after it'd been modified to specify the 'Microsoft Enhanced ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found