Существуют разные форматы подписи, одним из которых является формат 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