AzureAD - 仅限单声道 - System.Security.Cryptography.CryptographicException:密钥集不存在

AzureAD - mono only - System.Security.Cryptography.CryptographicException: Keyset does not exist

我正在尝试使用 AzureAD(仅限单声道)作为 AD 应用程序用户进行身份验证以向 SharePoint 发出请求。

Azure AD 应用用户基本上需要您提供[clientID、证书路径、证书密码]。

以下代码适用于 Windows:

  string siteUrl = "https://xxxxxxx.sharepoint.com";
  string clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx";
  string domain = "xxxxxxx.onmicrosoft.com";
  string certificatePath = "/path/to/xxxxxxx.pfx";
  string certificatePassword = "xxxxxxx";

  using (var cc = new AuthenticationManager().GetAzureADAppOnlyAuthenticatedContext(siteUrl, clientId, domain, certificatePath, certificatePassword)) {
    cc.Load(cc.Web, p => p.Title);
    cc.ExecuteQuery();
    Console.WriteLine(cc.Web.Title);
  };

但是在 Mono 上你会得到这个错误:

System.Security.Cryptography.CryptographicException: Keyset does not exist

似乎与:

有关

但这些应该是固定的,但我仍然遇到这些问题。

错误堆栈已满:

System.Security.Cryptography.CryptographicException: Keyset does not exist
  at System.Security.Cryptography.RSACryptoServiceProvider.Common (System.Security.Cryptography.CspParameters p) [0x00039] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (System.Int32 dwKeySize, System.Security.Cryptography.CspParameters parameters) [0x0001d] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (System.Security.Cryptography.CspParameters parameters) [0x00000] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.CryptographyHelper.GetCryptoProviderForSha256 (System.Security.Cryptography.RSACryptoServiceProvider rsaProvider) [0x0007e] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.CryptographyHelper.SignWithCertificate (System.String message, System.Security.Cryptography.X509Certificates.X509Certificate2 x509Certificate) [0x0001b] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate.Sign (System.String message) [0x00007] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.JsonWebToken.Sign (Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate credential) [0x0002b] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.RequestParameters.AddClientKey (Microsoft.IdentityModel.Clients.ActiveDirectory.ClientKey clientKey) [0x000b7] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.RequestParameters..ctor (System.String resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientKey clientKey) [0x0001a] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase+<SendTokenRequestAsync>d__9.MoveNext () [0x00024] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase+<RunAsync>d__0.MoveNext () [0x004f3] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
--- End of stack trace from previous location where exception was thrown ---
  at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.RunAsyncTask[T] (System.Threading.Tasks.Task`1[TResult] task) [0x00031] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.AcquireToken (System.String resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate clientCertificate) [0x00014] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at pnp_test_2.Program.Main (System.String[] args) [0x000a8] in <8c5b1bd4cf9047a3868c8cacd6143dd1>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: System.Security.Cryptography.CryptographicException: Keyset does not exist
  at System.Security.Cryptography.RSACryptoServiceProvider.Common (System.Security.Cryptography.CspParameters p) [0x00039] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (System.Int32 dwKeySize, System.Security.Cryptography.CspParameters parameters) [0x0001d] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (System.Security.Cryptography.CspParameters parameters) [0x00000] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.CryptographyHelper.GetCryptoProviderForSha256 (System.Security.Cryptography.RSACryptoServiceProvider rsaProvider) [0x0007e] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.CryptographyHelper.SignWithCertificate (System.String message, System.Security.Cryptography.X509Certificates.X509Certificate2 x509Certificate) [0x0001b] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate.Sign (System.String message) [0x00007] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.JsonWebToken.Sign (Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate credential) [0x0002b] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.RequestParameters.AddClientKey (Microsoft.IdentityModel.Clients.ActiveDirectory.ClientKey clientKey) [0x000b7] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.RequestParameters..ctor (System.String resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientKey clientKey) [0x0001a] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase+<SendTokenRequestAsync>d__9.MoveNext () [0x00024] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <bb7b695b8c6246b3ac1646577aea7650>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase+<RunAsync>d__0.MoveNext () [0x004f3] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
--- End of stack trace from previous location where exception was thrown ---
  at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.RunAsyncTask[T] (System.Threading.Tasks.Task`1[TResult] task) [0x00031] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.AcquireToken (System.String resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate clientCertificate) [0x00014] in <211fb7a0ce9049e5a2768849f2fd6a88>:0 
  at pnp_test_2.Program.Main (System.String[] args) [0x000a8] in <8c5b1bd4cf9047a3868c8cacd6143dd1>:0 

如何使用 Azure AD 纯应用帐户 + pfx 密钥在单声道上进行身份验证?

在这里查看我的评论:https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues/509

我现在已经找到了适用于 SharePoint Online 的解决方法:

如果需要,您可以删除 CorePNP 库。但不要将 OfficeDevPnP.Core.AuthenticationManager 用于 Linux,因为它不起作用!但是,该库确实适用于 Windows。

添加 nuget 依赖项:

Microsoft.IdentityModel.Clients.ActiveDirectory
Microsoft.IdentityModel.Tokens

通过 auth 正确获取客户端上下文的 c# 代码:

using System;
using System.Text;
using System.Globalization;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
using Microsoft.SharePoint.Client;

namespace PnpTest {

  class ClientAssertionCertificate : IClientAssertionCertificate {

    X509Certificate2 certificate;
    public string ClientId { get; private set; }

    public string Thumbprint {
      get {
        return Base64UrlEncoder.Encode(certificate.GetCertHash());
      }
    }

    public ClientAssertionCertificate(string clientId, X509Certificate2 certificate) {
      ClientId = clientId;
      this.certificate = certificate;
    }

    public byte[] Sign(string message) {
      using (var key = certificate.GetRSAPrivateKey()) {
        return key.SignData(Encoding.UTF8.GetBytes(message), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
      }
    }
  }

  class Program {
    static void Main(string[] args) {
      string siteUrl = "https://xxxxxxxxxxxxxxx.sharepoint.com";
      string clientId = "xxxxxxxxxxxxxxx";
      string tenant = "xxxxxxxxxxxxxxx.onmicrosoft.com";
      string certificatePath = "resources/xxxxxxxx.pfx";
      string certificatePassword = "xxxxxxxx";

      var certfile = System.IO.File.OpenRead(certificatePath);
      var certificateBytes = new byte[certfile.Length];
      certfile.Read(certificateBytes, 0, (int)certfile.Length);
      var certificate = new X509Certificate2(
          certificateBytes,
          certificatePassword,
          X509KeyStorageFlags.Exportable |
          X509KeyStorageFlags.MachineKeySet |
          X509KeyStorageFlags.PersistKeySet);

      var clientAssertionCertificate = new ClientAssertionCertificate(clientId, certificate);

      string authority = string.Format(CultureInfo.InvariantCulture, "{0}/{1}/", "https://login.windows.net", tenant);

      var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);

      var host = new Uri(siteUrl);

      using (var clientContext = new ClientContext(siteUrl)) {
        clientContext.ExecutingWebRequest += (sender, webRequestArgs) => {
          var arFuture = authContext.AcquireTokenAsync(host.Scheme + "://" + host.Host + "/", clientAssertionCertificate);
          var ar = arFuture.Result;

          webRequestArgs.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + ar.AccessToken;
        };
        clientContext.Load(clientContext.Web, p => p.Title);
        clientContext.ExecuteQuery();
        Console.WriteLine(clientContext.Web.Title);
      }
    }
  }
}