ECC p-128 不在 c# 中生成 32 字节数组签名

ECC p-128 is not generating 32 byte array signature in c#

当我转换为十六进制时,signature1 值总是 39 byte[] 数组,长度大于 64。 我想从 C# 中的 P-128 ECC 算法生成精确的 64 位十六进制签名值。

让我解释一下下面的代码:

  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();
    
    }
    

有不同的签名格式,一种是 ASN.1 DER 格式,它在您的代码中使用并进行了解释 here。对于曲线 secp128r1 (P-128),生成的签名长度在 38 到 40 字节之间。

您似乎期望的格式是 r | s(参见here),曲线secp128r1的长度为32字节。

您可以轻松地手动在两种格式之间进行转换。

用于 C# 的较新版本的 BouncyCastle 还允许 直接r | 中生成签名。 s 格式,参见 ,例如使用 SHA-256withPLAIN-ECDSA(而不是 SHA-256withECDSA)。

一些注意事项:

  • 根据 NIST,128 位的密钥大小同时太小(相反,建议 2019-2030 至少使用 224 位),here. secp128r1 was still recommended in SEC 2: Recommended Elliptic Curve Domain Parameters V1.0 (from 2000), but no longer in SEC 2: Recommended Elliptic Curve Domain Parameters V2.0(从 2010 年开始)。
  • SHA-256withECDSA (SHA-256withPLAIN-ECDSA) 隐式散列消息(使用 SHA-256),即显式散列不是必需的,会导致双重(冗余)散列。
  • 此外,在您的显式哈希中,不是实际的二进制哈希传递给签名者,而是 UTF-8 编码的十六进制编码哈希。
  • secp128r1对应128位(16字节)的密钥大小(基点顺序)。但是对于 SHA256,您使用的是两倍大的摘要(32 字节)。在这种情况下,根据 NIST FIPS 186-4,使用哈希的最左边 n 位,here.

编辑:

将目前所说的内容具体化:以下代码对字符串 message 进行 UTF-8 编码并生成 r|s 格式的 ECDSA 签名(其中对应于 IEEE P1363,here) 基于曲线 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); // 
}

注意与您最初发布的代码相比的差异:

  • 由于 (Org.BouncyCastle.Crypto.) ISigner#GenerateSignature() 隐式散列,因此不执行显式散列。
  • SHA-256withPLAIN-ECDSAr|s 格式直接 生成签名,自 v1.8.4 (bccrypto-csharp- 1.8.4,2018 年 10 月 27 日发布)。

对于较旧的 BouncyCastle 版本(只有 SHA-256withECDSA 可用),必须手动将签名从 ASN.1 DER 转换为 r|s格式。这很容易,因为 rs 可以直接从 ASN.1 DER 格式中提取,参见 here.

如果使用上述代码在客户端验证仍然失败,请检查双方的密钥、曲线和摘要是否匹配以及验证过程本身。

完整性:
当然也可以用(Org.BouncyCastle.Crypto.Signers.)ECDsaSigner.GenerateSignature(byte[] msg)来生成签名。重要的是此方法不会隐式散列,即这里确实必须显式散列。这种方法 returns rs as (Org.BouncyCastle.Math.) BigInteger[] 这样两个部分都可以很容易十六进制编码,例如toString(16) 然后连接到 r|s.