Как я могу зашифровать данные с помощью открытого ключа из сертификата ECC X509 в.net Framework на Windows?

Я использую:

  • Windows 10 (версия 1709, сборка ОС 17025.1000)
  • .net Framework 4.7
  • VS 2017 (версия: 15.3.5)

Вот что я сделал:

  1. Получил самозаверяющий сертификат ECC с использованием OpenSSL и действия, описанные в сценарии на https://gist.github.com/sidshetye/4759690 с изменениями:

    а) Используемая кривая NIST/P-256 для простого 256-битного поля

    б) Используется SHA-256

  2. Загрузить сертификат из файла (созданного на предыдущем шаге) в объект X509Certificate2

  3. Импортировал файл PFX в хранилище доверия Windows (для тестирования). Это успешно.

  4. Проверка импортированного сертификата показывает поле открытого ключа как "ECC (256 бит)" и параметры открытого ключа как "ECDSA_P256".
  5. Далее попытался выяснить, как шифровать с помощью этого сертификата.

Я застрял на последнем шаге, потому что все примеры, которые используют объект X509Certificate2, преимущественно используют только RSA, и я использую сертификат ECC. Для сертификата RSA в X509Certificate2 есть метод расширения GetRSAPublicKey, а в классе RSA есть метод Encrypt. Однако для сертификатов ECC такого метода не существует.

Затем я наткнулся на этот пост ( Загрузка сертификата с использованием X509Certificate2 с открытым ключом ECC) и попытался сделать следующее (даже если это показалось странным, почему открытый ключ сертификата ECC приводится к типу RSA):

RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key

Я получил следующее исключение: алгоритм ключа сертификата не поддерживается.

Затем я наткнулся на этот пост ( Импорт сертификата на основе ECC из хранилища сертификатов Windows в CngKey), в котором в основном пытались создать тип CNGKey и создать экземпляр ECDsaCng с ним. Однако, даже если я могу сделать это с ECDiffieHellmanCng, на нем нет метода Encrypt.

Поэтому я не совсем уверен, как я могу продолжить использовать открытый ключ сертификата ECC X509 для шифрования данных.

1 ответ

Решение

Фон

Асимметричные алгоритмы имеют три разные цели (о которых я знаю)

  1. шифрование
    • RSA - единственный "стандартный" алгоритм, который может сделать это напрямую.
  2. Подпись
    • RSA
    • DSA
    • ECDSA
    • ЭльГамаль Подпись
  3. Соглашение о ключе
    • Диффи-Хеллман (DH)
    • ECDH
    • Шифрование ElGamal (асимметричная фаза запуска)
    • MQV
    • ECMQV

Поскольку шифрование RSA ограничено в пространстве и было трудным для компьютеров в 90-х годах, основное использование шифрования RSA было в "Передаче ключей", то есть "зашифрованное сообщение" было просто симметричным ключом шифрования для DES/3DES (AES еще не изобретен) - https://tools.ietf.org/html/rfc2313.

Схемы согласования (или передачи) ключей всегда должны быть объединены с протоколом / схемой, чтобы привести к операции шифрования. Такие схемы включают

  • TLS (урожденный SSL)
  • CMS или S/MIME зашифрованные данные
  • IES (интегрированная схема шифрования)
  • ECIES (схема шифрования с эллиптической кривой)
  • Эль-Гамальское шифрование (целостное)
  • PGP шифрование

Так что вы, вероятно, хотите, ECIES.

ECIES.Net

В настоящее время (.NET Framework 4.7.1, .NET Core 2.0) нет поддержки для получения объекта ECDiffieHellman из сертификата в.NET.

Игра окончена, верно? Ну, наверное, нет. Если сертификат, несущий ключ ECDH, явно не использует идентификатор алгоритма id-ecDH (по сравнению с более стандартным идентификатором id-ecc), его можно открыть как ECDSA. Затем вы можете заставить этот объект стать ECDH:

using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
    return ECDiffieHellman.Create(ecdsa.ExportParameters(false));
}

(то же самое можно сделать для закрытого ключа, если ключ можно экспортировать, в противном случае требуются сложные вещи, но вам это не нужно)

Давайте продолжим и вырезаем публичный объект получателя:

ECDiffieHellmanPublicKey recipientPublic = GetECDHFromCertificate(cert).PublicKey;
ECCurve curve = recipientPublic.ExportParameters().Curve;

Итак, теперь мы переходим к http://www.secg.org/sec1-v2.pdf разделу 5.1 (Схема интегрированного шифрования эллиптической кривой)

Настроить

  1. Выберите ANSI-X9.63-KDF с SHA-2-256 в качестве хэш-функции.
  2. Выберите HMAC–SHA-256–256.
  3. Выберите AES–256 в режиме CBC.
  4. Выберите Примитив Диффи-Хеллмана с эллиптической кривой.
  5. Вы уже выбрали secp256r1.
  6. Hard кодировки. Готово.
  7. Точка сжатия раздражает, решите не использовать ее.
  8. Я опускаю SharedInfo. Это, вероятно, делает меня плохим человеком.
  9. Не используется XOR, N/A.

шифровать

  1. Сделайте эфемерный ключ на правильной кривой.

    ECDiffieHellman ephem = ECDiffieHellman.Create(curve);
    
  2. Мы решили нет.

    ECParameters ephemPublicParams = ephem.ExportParameters(false);
    int pointLen = ephemPublicParams.Q.X.Length;
    byte[] rBar = new byte[pointLen * 2 + 1];
    rBar[0] = 0x04;
    Buffer.BlockCopy(ephemPublicParams.Q.X, 0, rBar, 1, pointLen);
    Buffer.BlockCopy(ephemPublicParams.Q.Y, 0, rBar, 1 + pointLen, pointLen);
    
  3. Не могу напрямую сделать это, двигаясь дальше.

  4. Не могу напрямую сделать это, двигаясь дальше.
  5. Так как мы здесь контролируем, мы просто сделаем 3, 4, 5 и 6 как одно.
  6. Время KDF.

    // This is why we picked AES 256, HMAC-SHA-2-256(-256) and SHA-2-256,
    // the KDF is dead simple.
    byte[] ek = ephem.DeriveKeyFromHash(
        recipientPublic,
        HashAlgorithmName.SHA256,
        null,
        new byte[] { 0, 0, 0, 1 });
    
    byte[] mk = ephem.DeriveKeyFromHash(
        recipientPublic,
        HashAlgorithmName.SHA256,
        null,
        new byte[] { 0, 0, 0, 2 });
    
  7. Шифровать вещи.

    byte[] em;
    
    // ECIES uses AES with the all zero IV. Since the key is never reused,
    // there's not risk in that.
    using (Aes aes = Aes.Create())
    using (ICryptoTransform encryptor = aes.CreateEncryptor(ek, new byte[16]))
    {
        if (!encryptor.CanTransformMultipleBlocks)
        {
            throw new InvalidOperationException();
        }
    
        em = encryptor.TransformFinalBlock(message, 0, message.Length);
    }
    
  8. MAC это

    byte[] d;
    
    using (HMAC hmac = new HMACSHA256(mk))
    {
        d = hmac.ComputeHash(em);
    }
    
  9. Конец

    // Either
    return Tuple.Create(rBar, em, d);
    // Or
    return rBar.Concat(em).Concat(d).ToArray();
    

расшифровывать

Оставлено в качестве упражнения для читателя.

Для получения ECDiffieHellman закрытый ключ из сертификата, используйте следующий метод:

  • Установите пакет NuGet Security.Cryptography (CLR Security). (Пакет находится под лицензией MIT.)
  • Используйте следующий метод расширения, чтобы получить экземпляр CngKey: CngKey cngKey = certificate.GetCngPrivateKey(); (Примечание: метод расширения certificate.GetECDsaPrivateKey(), изначально поддерживается в.NET, возвращает ECDsaCng пример; нет метода расширения для возврата ECDiffieHellmanCng.)
  • cngKey Экземпляр может быть использован для создания либо ECDsaCng или ECDiffieHellmanCng пример: var sa = new ECDsaCng(cngKey); var sa = new ECDiffieHellmanCng(cngKey);
Другие вопросы по тегам