Я пытаюсь создать подписки в API Graph и пытаюсь включить данные ресурсов. Я прочитал эту документацию 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, в котором говорится:
Ошибка проверки сертификата: не удалось найти запрошенный объект.
Если у кого-нибудь есть идеи или примеры того, как обойти эту ошибку, мы будем очень признательны.
При удалении EncryptionCertificateId
я получаю сообщение об ошибке: «encryptionCertificateId» не может быть нулевым или пустым». Я думаю, это имеет смысл, поскольку в их документации указано: «Этот идентификатор позволяет вам сопоставлять ваши сертификаты с получаемыми вами уведомлениями об изменениях».
Я тоже только что попробовал ExportCertificatePem()
, но все равно получаю ту же ошибку
Я нашел ключ к моей проблеме и почему это не работает. Проведя еще несколько исследований о том, как это сделать, я нашел файл README в примере кода, в котором содержится более подробная информация о том, чего ожидает Microsoft. Ключевая часть этого README гласит в конце:
Скопируйте содержимое между -----BEGIN CERTIFICATE----- и -----END CERTIFICATE-----. Это будет значение Base64EncodedCertificate в файле appsettings.json.
Покопавшись в коде и в том, что было возвращено, я заметил, что открытый ключ в сертификате представляет собой строку base64, отличную от строки самого сертификата, которая содержит в себе открытый ключ. Посмотрев на документацию Microsoft, в ней говорится:
Экспортируйте сертификат в формате X.509 с кодировкой Base64 и включите только открытый ключ.
Я неправильно понял, о чем они просили, и предположил, что им нужен только открытый ключ от того, из чего был сделан сертификат.
Единственная проблема после этого заключалась в том, как извлечь содержимое сертификата в формате 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 base64PublicKeyCertificate = ExtractBase64Certificate(exportedCertificate.ExportCertificatePem());
var rsaPrivateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey());
В этом обновленном коде следует отметить две ключевые вещи:
X509ContentType.Cert
, поскольку при этом экспортируется только открытый ключ, чтобы гарантировать, что вы не отправляете также закрытый ключ в запросе.
Что произойдет, если вы просто удалите установщик
EncryptionCertificateId
? А как насчетvar x509PublicKey = exportedCertificate.ExportCertificatePem();