ECC p-128 не генерирует сигнатуру 32-байтового массива в с #

Значение signature1 всегда является массивом 39 byte [], когда я конвертирую в шестнадцатеричный формат, длина больше 64. Я хотел сгенерировать точное значение шестнадцатеричной подписи длиной 64 из алгоритма P-128 ECC в C #.

Позвольте мне объяснить ниже код:

  1. Создание хеша из обычного текстового сообщения, которое я должен подписать

  2. Чтение 128-битного ключа PEM из локальной папки

  3. Передайте ключ и сгенерируйте подпись.

        public static string GenerateSignature(string message, string privateKeyPath)
        {
                string hashMessage;
                using (SHA256 sha256 = SHA256.Create())
                {
                    byte[] data = sha256.ComputeHash(Encoding.Default.GetBytes(message));
                    hashMessage = convertByteArrayToHexString(data);
                    Console.WriteLine("Signing message: {0}", message);
                    Console.WriteLine("Hash of the signing message: {0}", hashMessage);
                }
                byte[] hashMessageBytes = Encoding.UTF8.GetBytes(hashMessage);
                ISigner signer = SignerUtilities.GetSigner("SHA256withECDSA");
                AsymmetricCipherKeyPair publicKey = getPrivateKeyFromPemFile(privateKeyPath);
                signer.Init(true, publicKey.Private);
                signer.BlockUpdate(hashMessageBytes, 0, hashMessageBytes.Length);
                byte[] signature1 = signer.GenerateSignature();
    
    }
    

person Sai Sherlekar    schedule 22.07.2020    source источник


Ответы (1)


Существуют разные форматы подписи, одним из которых является формат DER ASN.1, который используется в вашем коде и объясняется здесь. Для кривой secp128r1 (P-128) сгенерированные подписи имеют длину от 38 до 40 байтов.

Вы, кажется, ожидаете формат r | s (см. здесь ), который для кривой secp128r1 имеет длину 32 байта.

Вы можете легко конвертировать между двумя форматами вручную.

Более новые версии BouncyCastle для C # также позволяют прямое генерировать подпись в r | s см. здесь, например с SHA-256withPLAIN-ECDSA (вместо SHA-256withECDSA).

Несколько примечаний:

  • Согласно NIST, размер ключа в 128 битов пока что слишком мал (вместо этого рекомендуется минимум 224 на 2019-2030 годы), здесь. secp128r1 по-прежнему рекомендуется в SEC 2: Рекомендуемые параметры домена эллиптической кривой V1.0 (с 2000 г.), но больше не входит в SEC 2: рекомендованная эллиптическая кривая Параметры домена V2.0 (с 2010 г.).
  • SHA-256withECDSA (SHA-256withPLAIN-ECDSA) хеширует сообщение неявно (с помощью SHA-256), т.е. явное хеширование не требуется и приводит к двойному (избыточному) хешированию.
  • Кроме того, в вашем явном хешировании подписывающей стороне передается не фактический двоичный хэш, а хеш-код в шестнадцатеричном коде UTF-8.
  • secp128r1 соответствует размеру ключа (порядку базовой точки) 128 бит (16 байт). Но с SHA256 вы используете дайджест, который в два раза больше (32 байта). В этом случае согласно NIST FIPS 186-4 используются крайние левые n бит хеша, здесь .

Изменить:

Чтобы конкретизировать то, что было сказано до сих пор: следующий код UTF-8 кодирует строку message и генерирует подпись ECDSA в формате r | s (который соответствует IEEE P1363, здесь) на основе кривой secp128r1 и дайджеста SHA256:

public static string GenerateSignature(string message, string privateKeyPath)
{
    byte[] messageBytes = Encoding.UTF8.GetBytes(message);
    ISigner signer = SignerUtilities.GetSigner("SHA-256withPLAIN-ECDSA");
    AsymmetricCipherKeyPair keyPair = getPrivateKeyFromPemFile(privateKeyPath); // get secp128r1 key pair
    signer.Init(true, keyPair.Private);
    signer.BlockUpdate(messageBytes, 0, messageBytes.Length);
    byte[] signature = signer.GenerateSignature();
    return ByteArrayToString(signature); // https://stackoverflow.com/a/311179/9014097
}

Обратите внимание на различия по сравнению с исходным кодом:

  • Поскольку (Org.BouncyCastle.Crypto.) ISigner#GenerateSignature() хешируется неявно, явное хеширование не выполняется.
  • SHA-256withPLAIN-ECDSA генерирует подпись напрямую в формате r | s и существует с версии 1.8.4 (bccrypto-csharp-1.8.4, выпущен 27 октября 2018 г.).

В случае более старой версии BouncyCastle (где доступен только SHA-256withECDSA) подпись должна быть вручную преобразована из формата ASN.1 DER в формат r | s. Это просто, поскольку r и s можно извлечь непосредственно из формата DER ASN.1, см. здесь.

Если проверка на стороне клиента по-прежнему не выполняется с помощью приведенного выше кода, убедитесь, что ключи, кривые и дайджесты на обеих сторонах совпадают, а также сам процесс проверки.

Для полноты:
Конечно, также (Org.BouncyCastle.Crypto.Signers.) ECDsaSigner.GenerateSignature(byte[] msg) можно использовать для генерации подписи. Важно, чтобы этот метод не хешировал неявно, т.е. здесь действительно вы должны хешировать явно. Этот метод возвращает r и s как (Org.BouncyCastle.Math.) BigInteger[], так что обе части могут быть легко закодированы в шестнадцатеричном формате, например toString(16), а затем объединены с r | s.

person user 9014097    schedule 23.07.2020
comment
Tapaco, мне нужен вывод IEEE P1363 с одной шестнадцатеричной строкой длиной 64, я получаю подпись (r, s), и если я присоединился, она преобразует шестнадцатеричную длину 64. По-прежнему его не проверяют на моем клиентском месте. пожалуйста помоги - person Sai Sherlekar; 24.07.2020
comment
Для кривой secp128r1 (P-128) сгенерированные подписи имеют длину от 38 до 40 байтов. Мне нужны точные 32 байта (с использованием IEEE P1363), тогда только он преобразуется в 64 шестнадцатеричного - person Sai Sherlekar; 24.07.2020
comment
Как вы изменили код для создания подписи в формате r | s, заменили ли вы SHA-256withECDSA на SHA-256withPLAIN-ECDSA? Для более сложных изменений, например если вы вручную извлекли формат r | s из формата ASN.1 DER, отредактируйте свой ответ и добавьте текущий код для создания подписи r | s (пожалуйста, не меняйте уже опубликованный код, но добавьте новый код). - person user 9014097; 24.07.2020
comment
В вашем коде есть другие проблемы, особенно пункты 2 и 3 моего списка внизу: ISigner#GenerateSignature() выполняет хеширование неявно (поэтому имя дайджеста включено в спецификатор), поэтому это не необходимо хешировать явно. Таким образом, вы можете попробовать заменить hashMessageBytes в BlockUpdate на Encoding.UTF8.GetBytes(message). - person user 9014097; 24.07.2020
comment
Последние опубликованные коды значительно отличаются от исходного кода. Нет смысла работать с тремя кодами, поэтому я публикую возможное решение, основанное на исходном коде, см. Раздел Правка в моем ответе. - person user 9014097; 24.07.2020