Невозможно создать подписку на Microsoft Graph API с использованием самозаверяющего сертификата C# X509Certificate2

Я пытаюсь создать подписки в 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? А как насчет var x509PublicKey = exportedCertificate.ExportCertificatePem();

Charlieface 20.06.2024 00:34

При удалении EncryptionCertificateId я получаю сообщение об ошибке: «encryptionCertificateId» не может быть нулевым или пустым». Я думаю, это имеет смысл, поскольку в их документации указано: «Этот идентификатор позволяет вам сопоставлять ваши сертификаты с получаемыми вами уведомлениями об изменениях».

Steve Norwood 20.06.2024 15:20

Я тоже только что попробовал ExportCertificatePem(), но все равно получаю ту же ошибку

Steve Norwood 20.06.2024 15:21
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
79
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я нашел ключ к моей проблеме и почему это не работает. Проведя еще несколько исследований о том, как это сделать, я нашел файл 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());

В этом обновленном коде следует отметить две ключевые вещи:

  • Microsoft допускает использование SHA512 в качестве алгоритма хеширования, поэтому лучше использовать его. Я не уверен, будут ли они использовать SHA3_512, но моя машина не может использовать этот алгоритм хеширования, поэтому я остановился на SHA_512.
  • При экспорте сертификата обязательно используйте X509ContentType.Cert, поскольку при этом экспортируется только открытый ключ, чтобы гарантировать, что вы не отправляете также закрытый ключ в запросе.

Другие вопросы по теме