Сбой подписи C # ECDSA, запущенной в онлайн-компиляторе

#c# #sign #ecdsa #online-compilation

#c# #sign #ecdsa #онлайн-компиляция

Вопрос:

Я успешно запускаю этот код на своем компьютере с Windows (Win 10 x64, работает dotnet 4.7.2). Он генерирует пару ключей EC («P-256»), хэширует открытый текст с помощью SHA-256, подписывает хэш с помощью закрытого ключа ec и проверяет подпись на соответствие хэшированному открытому тексту с помощью открытого ключа ec.

Я получаю этот вывод, так что все работает нормально:

 EC signature curve secp256r1 / P-256 string
dataToSign: The quick brown fox jumps over the lazy dog
* * * sign the plaintext with the EC private key * * *
EC keysize: 256
signature (Base64): cwLBRSt1vtO33tHWcTdx1OTu9lBFXHEJgvdRyDUynLLE5eMakUZUAKLwaJvYoS7NBylx2Zz0 G6dvgJ6xv5qNA==
* * *verify the signature against hash of plaintext with the EC public key * * *
signature verified: True
 

Теперь я пытаюсь найти любой онлайн-компилятор, который смог запустить код. Мой любимый компилятор
(https://repl.it /, Компилятор Mono C # версии 6.8.0.123, полный код: https://repl.it/@javacrypto/EcSignatureFull#main.cs ) сталкивается с этой ошибкой:

 Unhandled Exception:
System.NotImplementedException: The method or operation is not implemented.
  at EcSignatureString.Main () [0x00036] in <13e2ad358a924efc874a89efad35ffe7>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.NotImplementedException: The method or operation is not implemented.
  at EcSignatureString.Main () [0x00036] in <13e2ad358a924efc874a89efad35ffe7>:0
exit status 1
 

Использование другой платформы (https://dotnetfiddle.net /, Компилятор .net 5, полный код: https://dotnetfiddle.net/lSPpjz ) выдает эту аналогичную ошибку:

 Unhandled exception. System.PlatformNotSupportedException: Windows Cryptography Next Generation (CNG) is not supported on this platform.
   at System.Security.Cryptography.ECDsaCng..ctor(Int32 keySize)
   at EcSignatureString.Main()
Command terminated by signal 6
 

Итак, мой вопрос: есть ли какой-либо доступный онлайн-компилятор, который может запускать код?

Я предполагаю, что мой вопрос может быть не по теме для SO — в данном случае — есть ли какой-либо другой сайт stackexchange, который был бы лучшим местом для моего вопроса?

Предупреждение: следующий код не имеет обработки исключений и предназначен только для образовательных целей:

 using System;
using System.Security.Cryptography;

class EcSignatureString {
    static void Main() {

    Console.WriteLine("EC signature curve secp256r1 / P-256 string");
    string dataToSignString = "The quick brown fox jumps over the lazy dog";
    byte[] dataToSign = System.Text.Encoding.UTF8.GetBytes(dataToSignString);
    Console.WriteLine("dataToSign: "   dataToSignString);
    try {
        Console.WriteLine("n* * * sign the plaintext with the EC private key * * *");

        ECDsaCng ecDsaKeypair = new ECDsaCng(256);
        Console.WriteLine("EC keysize: "   ecDsaKeypair.KeySize);

        byte[] hashedData = null;
        byte[] signature = null;
        // create new instance of SHA256 hash algorithm to compute hash
        HashAlgorithm hashAlgo = new SHA256Managed();
        hashedData = hashAlgo.ComputeHash(dataToSign);

        // sign Data using private key
        signature = ecDsaKeypair.SignHash(hashedData);
        string signatureBase64 = Convert.ToBase64String(signature);
        Console.WriteLine("signature (Base64): "   signatureBase64);

        // get public key from private key
        string ecDsaPublicKeyParametersXml = ecDsaKeypair.ToXmlString(ECKeyXmlFormat.Rfc4050);

        // verify
        Console.WriteLine("n* * *verify the signature against hash of plaintext with the EC public key * * *");
        ECDsaCng ecDsaVerify = new ECDsaCng();
        bool signatureVerified = false;
        ecDsaVerify.FromXmlString(ecDsaPublicKeyParametersXml, ECKeyXmlFormat.Rfc4050);
        signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
        Console.WriteLine("signature verified: "   signatureVerified);
        }
        catch(ArgumentNullException) {
            Console.WriteLine("The data was not signed or verified");
        }
    }
}
 

Комментарии:

1. Microsoft решила «разгрузить» шифрование .NET 5.0 в ОС. Это означает, что не все поддерживается везде. Существует таблица, показывающая, что поддерживается где: docs.microsoft.com/it-it/dotnet/standard/security /…

2. Если вы хотите быть мультиплатформенным, вы должны использовать ECDsa.Create(...) полученный объект, а затем работать с ним. Не уверен, что все, что вам нужно, реализовано в «общем» ECDsa

3. ToXmlString Не работает, и неясно, как экспортировать сгенерированный ключ в формат PEM. Есть некоторые Import*Pem для импорта ключа.

4. Возможно, вам потребуется обновить операционную систему и ядро на основе ответа xantaos выше. Более старые версии многих операционных систем не поддерживают новейшие режимы шифрования.

5. @jdweng: спасибо за ваш ответ. Поскольку мне нужно запустить свой код в онлайн-среде компилятора , я не в состоянии обновить онлайн-компилятор, извините.

Ответ №1:

Microsoft решила, что шифрование и хеширование должны быть полностью делегированы ОС (в .NET Framework это было наполовину), поэтому теперь .NET 5 (и .NET Core) имеют несколько серверных систем для шифрования (например, для ECDsa ИТ ECDsaCng -служб, использующих службы Windows, и ECDsaOpenSsl для Linux / macOS, использующих OpenSSL (см. MSDN)

Теперь … решение вашей проблемы — использовать ECDsa класс и позволить ему выбрать свой сервер. С этим есть некоторые проблемы. Вы не можете легко экспортировать ключи в формат xml, и вы не можете легко экспортировать их в формат PEM. Вы можете легко экспортировать их в a byte[] , и вы можете легко импортировать их из формата PEM. На самом деле это не большая проблема, потому что вам редко нужно генерировать ключи, и обычно ваша программа получает свои ключи из внешнего источника, или, если она генерирует их сама, она может сохранить их в двоичном формате, чтобы использовать их позже.

 var dataToSignString = "Hello world!";
var dataToSign = Encoding.UTF8.GetBytes(dataToSignString);

Console.WriteLine("dataToSign: "   dataToSignString);

try
{
    Console.WriteLine("n* * * sign the plaintext with the EC private key * * *");

    var ecDsaKeypair = ECDsa.Create(ECCurve.NamedCurves.nistP256);

    // Normally here:
    //ecDsaKeypair.ImportFromPem()

    Console.WriteLine("EC keysize: "   ecDsaKeypair.KeySize);

    byte[] hashedData = null;
    byte[] signature = null;
    // create new instance of SHA256 hash algorithm to compute hash
    HashAlgorithm hashAlgo = new SHA256Managed();
    hashedData = hashAlgo.ComputeHash(dataToSign);

    // sign Data using private key
    signature = ecDsaKeypair.SignHash(hashedData);
    string signatureBase64 = Convert.ToBase64String(signature);
    Console.WriteLine("signature (Base64): "   signatureBase64);

    // get public key from private key
    string ecDsaPublicKeyParameters = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());

    // verify
    Console.WriteLine("n* * *verify the signature against hash of plaintext with the EC public key * * *");

    var ecDsaVerify = ECDsa.Create(ECCurve.NamedCurves.nistP256);
    bool signatureVerified = false;

    // Normally here:
    //ecDsaKeypair.ImportFromPem()
    var publicKey = Convert.FromBase64String(ecDsaPublicKeyParameters);
    ecDsaVerify.ImportSubjectPublicKeyInfo(publicKey, out _);

    signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
    Console.WriteLine("signature verified: "   signatureVerified);
}
catch (ArgumentNullException)
{
    Console.WriteLine("The data was not signed or verified");
}
 

О From/ToXmlFormat , текущий комментарий к ним на github .NET Core:

 // There is currently not a standard XML format for ECC keys, so we will not implement the default
// To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
// use an overload which allows the user to specify the format they'd like to serialize into.
 

Из некоторых выполненных тестов экспорт в формате PEM кажется довольно простым:

 public static IEnumerable<string> Split(string str, int chunkSize)
{
    for (int i = 0; i < str.Length; i  = chunkSize)
    {
        yield return str.Substring(i, Math.Min(chunkSize, str.Length - i));
    }
}
 

и затем

 string b64privateKey = Convert.ToBase64String(ecDsaKeypair.ExportPkcs8PrivateKey());
b64privateKey = string.Join("rn", Split(b64privateKey, 64));
string pemPrivateKey = "-----BEGIN PRIVATE KEY-----rn"   b64privateKey   "rn-----END PRIVATE KEY-----";
 

или

 string b64publicKey = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());
b64publicKey = string.Join("rn", Split(b64publicKey, 64));
string pemPublicKey = "-----BEGIN PUBLIC KEY-----rn"   b64publicKey   "rn-----END PUBLIC KEY-----";
 

(обратите внимание, что мне пришлось вручную разделить строку на блоки по 64 символа, это точное число, указанное в rfc7468, поскольку Convert.ToBase64String() поддерживается только длина строки 76)

Комментарии:

1. Большое спасибо. Поскольку я работаю над кросс-платформенным проектом (подписание и проверка на C #, Java, WebCrypto, PHP и NodeJS) Я генерирую свою пару ключей один раз в кодировке PEM и использую ее для всей платформы. Ваш код выполняется на dotnetfiddle.net так что я могу предложить работающие решения.

2. Просто примечание: код больше не выполняется в Windows (сообщение об ошибке: «‘ECDsa’ не содержит определения для ‘ImportPkcs8PrivateKey’ и не удалось найти метод расширения ‘ImportPkcs8PrivateKey’, принимающий первый аргумент типа ‘ECDsa’ (вам не хватает директивы using или ссылки на сборку?)») но для меня это не важно — важнее то, что онлайн-версия запущена.

3. @MichaelFehr Этот метод присутствует в .NET 5… Вероятно, они представили его в этой версии.