无法使用自签名 C# X509Certificate2 创建 Microsoft Graph API 订阅

问题描述 投票:0回答:1

我正在尝试在 Graph API 中创建订阅,并且正在考虑尝试包含资源数据。我一直在阅读这个 Microsoft 文档,尝试在代码中创建密钥,并提出了以下代码,该代码是在 这个堆栈溢出答案的帮助下创建的:

using var rsa = RSA.Create(4096);

var certificateRequest = new CertificateRequest("CN=testCertificate", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

// Make sure the key can only be used for encryption purposes
certificateRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.EncipherOnly, true));

var selfSignedCertificate = certificateRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1));

using var exportedCertificate = new X509Certificate2(selfSignedCertificate.Export(X509ContentType.Pfx));

var x509PublicKey = Convert.ToBase64String(exportedCertificate.GetPublicKey());
var rsaPrivateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey());

我已经尝试过:

  • 使用不同的哈希算法
  • 未指定
    X509KeyUsageExtension
  • 将证书导出为
    X509ContentType.Cert
    并将其用作公钥

创建此证书后,我将使用以下代码尝试创建订阅:

var requestBody = new Subscription
{
    ChangeType = "created,updated",
    NotificationUrl = _notificationUrl,
    LifecycleNotificationUrl = _lifecycleUrl,
    Resource = resource,
    ExpirationDateTime = DateTime.UtcNow.AddDays(3),
    ClientState = _clientState,
    IncludeResourceData = true,
    EncryptionCertificate = x509PublicKey,
    EncryptionCertificateId = certificateId
};

graphClient.Subscriptions.PostAsync(requestBody, cancellationToken: cancellationToken);

如果我删除

includeResourceData
EncryptionCertificate
EncryptionCertificateId
,则此方法有效,但如果没有,我将收到 HTTP 400 响应:

证书验证错误:找不到请求的对象。

如果有人有任何关于如何解决此错误的想法或示例,我们将不胜感激。

c# .net microsoft-graph-api x509certificate x509
1个回答
0
投票

我找到了问题的关键以及为什么这不起作用。在对如何执行此操作进行了更多研究之后,我在一些示例代码中找到了 README 文件,其中更详细地介绍了 Microsoft 的期望。本自述文件的关键部分在最后指出:

复制-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----之间的内容。这将是 appsettings.json 文件中 Base64EncodedCertificate 的值。

在深入研究代码和返回的内容后,我注意到证书中的公钥是与证书本身不同的 base64 字符串,证书本身包含公钥。回顾
Microsoft 文档

后,它指出:

以 Base64 编码的 X.509 格式导出证书,并仅包含公钥

我误解了他们的要求,并假设他们只想要证书制作过程中的公钥。

此后唯一的问题是如何提取证书的 base64 内容,并且我在

X509Certificate2

类中没有找到任何有用的方法来执行此操作,因此我编写了一个小辅助方法来对此进行排序:

private const string _beginCertificate = "-----BEGIN CERTIFICATE-----\n";
private const string _endCertificate = "\n-----END CERTIFICATE-----";

/// <summary>
///     Extract the base64 certificate without the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- strings.
/// </summary>
/// <returns>
///     The base64 contents of the X509 certificate provided
/// </returns>
private static string ExtractBase64Certificate(string x509CertificatePemString)
{
    var indexFrom = x509CertificatePemString.IndexOf(_beginCertificate, StringComparison.OrdinalIgnoreCase) + _beginCertificate.Length;
    var indexTo = x509CertificatePemString.LastIndexOf(_endCertificate, StringComparison.OrdinalIgnoreCase);

    return x509CertificatePemString.Substring(indexFrom, indexTo - indexFrom);
}

使用它来提取证书,我已经能够通过“证书验证错误”并创建订阅。我已经整理了原始问题中的代码,以确保证书只能执行所需的操作,因此代码现在如下所示:

using var rsa = RSA.Create(4096); var certificateRequest = new CertificateRequest("CN=testCertificate", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); // Make sure the key can only be used for encryption purposes certificateRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.EncipherOnly, true)); var selfSignedCertificate = certificateRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1)); using var exportedCertificate = new X509Certificate2(selfSignedCertificate.Export(X509ContentType.Cert)); var x509PublicKey = Convert.ToBase64String(exportedCertificate.GetPublicKey()); var rsaPrivateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey());

此更新后的代码有两个关键事项需要注意:

微软接受使用SHA512作为哈希算法,所以最好使用这个。我不确定他们是否会使用 SHA3_512,但我的机器无法使用此哈希算法,所以我坚持使用 SHA_512。
  • 导出证书时,请确保使用
  • X509ContentType.Cert
  • ,因为这只会导出公钥,以确保您不会在请求中发送私钥。
    
        
© www.soinside.com 2019 - 2024. All rights reserved.