当前位置: 首页 > 知识库问答 >
问题:

Bouncy Castle SHA-384带有ECDSA签名验证,出现异常

陆弘光
2023-03-14

我正在使用C#和.NET4.7使用BouncyCastle进行签名验证。

我在这里跟随一个回答,所以在这里解释了使用SHA-256withECDSA的验证过程

因此,我使用两种方法验证签名的代码在下面的代码片段中运行良好。

public static void Main(string[] args)
{
    //SHA256 With ECDSA - Baseline that works. Values received from BC answer on SO
    String sigSHA256
        = "e1f5cecccedfe5228d9331098e84b69a0675cdd9ac066ecfada7fea761f52a4cde902a0abd362883127230326fb556af14e894d39a3e14437aaa4134a3476c84";
    String msgSHA256 = "00000000dcb320137ddd6f825660750ab655219fad66951c64f0420be8ac902975197ed2b0da54cd3d502d34dd04c8d74b2958a0b8792ae4730df6d25a6969bcad9f93a7d6229e5a0100000017cf5242732bba21a0b0e7dad7102cf7bdb2c8d7a665045816a886d7";
    String pubSHA256 = "b679e27513e2fff8fdeb54409c242776f3517f370440d26885de574a0b0e5309a9de4ea055b0bf302d9f00875f80e28cd29bb95a48aa53746d7de9465123dbb7";
    
    VerifySHA256Bouncy(HexStringToByteArray(msgSHA256), HexStringToByteArray(sigSHA256), HexStringToByteArray(pubSHA256));
    
    Console.ReadLine();

}

public static void VerifySHA256Bouncy(byte[] message, byte[] signature, byte[] pubkey)
{
    BigInteger x = new BigInteger(1, pubkey.Take(32).ToArray());
    BigInteger y = new BigInteger(1, pubkey.Skip(32).ToArray());

    X9ECParameters ecParams = NistNamedCurves.GetByName("P-256");
    ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
    var G = ecParams.G;
    Org.BouncyCastle.Math.EC.ECCurve curve = ecParams.Curve;
    Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y);

    ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);
    // expected format is SEQUENCE {INTEGER r, INTEGER s}
    var derSignature = new DerSequence(
        // first 32 bytes is "r" number
        new DerInteger(new BigInteger(1, signature.Take(32).ToArray())),
        // last 32 bytes is "s" number
        new DerInteger(new BigInteger(1, signature.Skip(32).ToArray())))
        .GetDerEncoded();
    var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
    verifier.Init(false, pubkeyParam);
    verifier.BlockUpdate(message, 0, message.Length);
    
    bool result = verifier.VerifySignature(derSignature);
    Console.WriteLine("result: " + result);
}

我正在尝试将其调整到我的用例,该用例正在使用ECDSA签名验证SHA-384。我发现的唯一区别是密钥长度不同。对于 SHA-256 与 ECDSA,使用的公钥长度转换为 64 的字节数组。因此,在实现中,我正在做的是:

BigInteger x = new BigInteger(1, pubkey.Take(32).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(32).ToArray());

对于SHA-384 with ECDSA,我对其进行了如下调整。

public static void Main(string[] args)
{
    //SHA384 With ECDSA - Values received from Java integration example
    String sig384
        = "306402304f070f3cb570f92f573385880aaa58febc06b6842be59e8f56d196c63a5aacbb7124493bee84e0331c36eb9c4e3e27db0230628c89f28a53e4c2ed089abe2ada179cc64e3eb33204b0be07cdd34bd3cd5ed4d6f0aaf380cc0d436faee15509dadc14";
    String msg384 = "{\"transaction\":{\"amount\":\"64.50\",\"id\":\"248686\",\"type\":\"SALE\",\"result\":\"APPROVED\",\"card\":\"XXXXXXXXXXXX1111\",\"csc\":\"999\",\"authorization-code\":\"TAS231\",\"batch-string-id\":\"44\",\"display-message\":\"Transaction approved\",\"result-code\":\"000\",\"exp-date\":\"1218\"},\"payloadType\":\"transaction\"}";
    String pub384 = "307a301406072a8648ce3d020106092b240303020801010c0362000422ffee50bdb73df2698df79b8f62fa06c005acfb5d8e92c3088053620da94eb1f8978c769ace34231b51e41394b873b07a673dfb08e14e975fb26355a639f1be4339e787390ca4c8dd6463c76bc8421457906aafa8b9981445276fde833c136b";
    
    VerifySHA384Bouncy(Encoding.ASCII.GetBytes(msg384), HexStringToByteArray(sig384), HexStringToByteArray(pub384));
    
    Console.ReadLine();

}

public static void VerifySHA384Bouncy(byte[] message, byte[] signature, byte[] pubkey)
{
    BigInteger x = new BigInteger(1, pubkey.Take(62).ToArray());
    BigInteger y = new BigInteger(1, pubkey.Skip(62).ToArray());

    X9ECParameters ecParams = NistNamedCurves.GetByName("P-384");
    ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
    var G = ecParams.G;
    Org.BouncyCastle.Math.EC.ECCurve curve = ecParams.Curve;
    Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y);

    ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);
    // expected format is SEQUENCE {INTEGER r, INTEGER s}
    var derSignature = new DerSequence(
        // first 32 bytes is "r" number
        new DerInteger(new BigInteger(1, signature.Take(62).ToArray())),
        // last 32 bytes is "s" number
        new DerInteger(new BigInteger(1, signature.Skip(62).ToArray())))
        .GetDerEncoded();
    var verifier = SignerUtilities.GetSigner("SHA-384withECDSA");
    verifier.Init(false, pubkeyParam);
    verifier.BlockUpdate(message, 0, message.Length);
    
    bool result = verifier.VerifySignature(derSignature);
    Console.WriteLine("result: " + result);
}

此示例的密钥长度转换为 124 的字节数组。因此,在我的代码中,我正在做

BigInteger x = new BigInteger(1, pubkey.Take(62).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(62).ToArray());

我用于验证SHA-384 with ECDSA签名的代码抛出了一个异常:

系统。argument exception HResult = 0x 80070057 Message =值在Fp字段元素中无效参数名称:x Source=BouncyCastle。加密堆栈跟踪:在组织。bouncy castle . math . EC . fpfieldelement..Org的ctor(BigInteger q,BigInteger r,BigInteger x)。Org上的bouncy castle . math . EC . FP curve . from big integer(big integer x)。Org上的bouncy castle . math . EC . e curve . create point(big integer x,BigInteger y,Boolean withCompression)。SignatureVerification处的bouncy castle . math . EC . e curve . create point(big integer x,BigInteger y)。Program.VerifySHA384Bouncy(字节[]消息,字节[]签名,字节[]公钥)

我不确定我在解决这个问题的方法上是否有很大的偏差,或者我把苹果和橙子进行了比较,我没有找到足够的例子让SHA-384和ECDSA能够解决这个问题。任何帮助都将不胜感激。

共有1个答案

养学
2023-03-14

存在以下几个问题:

  • 使用 ASN.1 解析器进行的分析表明,公钥以 X.509 格式给出,例如此处,即原始密钥 x|y 结果为最后 2 * 48 = 96 字节:
pub384 = pub384.Substring(pub384.Length - 96 * 2); // 96 * 2 due to the hex encoding
  • xy坐标的确定为(xy分别为48字节):
BigInteger x = new BigInteger(1, pubkey.Take(48).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(48).ToArray());
  • 此外,还使用ASN进行了分析。1解析器显示公钥属于曲线brainpoolp384t1
X9ECParameters ecParams = ECNamedCurveTable.GetByName("brainpoolp384t1"); 
    < li >此外,签名< code>sig384已经以ASN.1/DER格式指定,而不是以r|s (IEEE P1363)格式指定,因此可以省略< code>derSignature的确定:
bool result = verifier.VerifySignature(signature); // true

通过这些更改,签名被成功验证。

完整代码:

using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Linq;
using System.Text;
...
public static void Main(string[] args)
{
    string sig384 = "306402304f070f3cb570f92f573385880aaa58febc06b6842be59e8f56d196c63a5aacbb7124493bee84e0331c36eb9c4e3e27db0230628c89f28a53e4c2ed089abe2ada179cc64e3eb33204b0be07cdd34bd3cd5ed4d6f0aaf380cc0d436faee15509dadc14";
    string msg384 = "{\"transaction\":{\"amount\":\"64.50\",\"id\":\"248686\",\"type\":\"SALE\",\"result\":\"APPROVED\",\"card\":\"XXXXXXXXXXXX1111\",\"csc\":\"999\",\"authorization-code\":\"TAS231\",\"batch-string-id\":\"44\",\"display-message\":\"Transaction approved\",\"result-code\":\"000\",\"exp-date\":\"1218\"},\"payloadType\":\"transaction\"}";
    string pub384 = "307a301406072a8648ce3d020106092b240303020801010c0362000422ffee50bdb73df2698df79b8f62fa06c005acfb5d8e92c3088053620da94eb1f8978c769ace34231b51e41394b873b07a673dfb08e14e975fb26355a639f1be4339e787390ca4c8dd6463c76bc8421457906aafa8b9981445276fde833c136b";
    pub384 = pub384.Substring(pub384.Length - 96 * 2); // Fix 1
    VerifySHA384Bouncy(Encoding.ASCII.GetBytes(msg384), HexStringToByteArray(sig384), HexStringToByteArray(pub384));
}

public static void VerifySHA384Bouncy(byte[] message, byte[] signature, byte[] pubkey)
{
    BigInteger x = new BigInteger(1, pubkey.Take(48).ToArray()); // Fix 2
    BigInteger y = new BigInteger(1, pubkey.Skip(48).ToArray());

    X9ECParameters ecParams = ECNamedCurveTable.GetByName("brainpoolp384t1"); // Fix 3
    ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
    ECPoint G = ecParams.G;
    ECCurve curve = ecParams.Curve;
    ECPoint q = curve.CreatePoint(x, y);

    ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);
    ISigner verifier = SignerUtilities.GetSigner("SHA-384withECDSA");
    verifier.Init(false, pubkeyParam);
    verifier.BlockUpdate(message, 0, message.Length);
    bool result = verifier.VerifySignature(signature); // Fix 4
    
    Console.WriteLine("result: " + result); // result: True
}

private static byte[] HexStringToByteArray(string str)
{
    return Hex.Decode(str);
}
 类似资料:
  • 我想创建一个签名并使用openssl验证它。我想有我的签名的十六进制输出。 这是我的密码 我得到这个错误: 如果我在创建签名的过程中删除了-hex,它就可以工作了。

  • 我应该如何更改A方法?任何想法都是好的。提前谢谢。

  • 最近,我一直在尝试构建一个Java库来解释和验证NZ Covid传递。在签名验证之前(这个过程中稍微重要的一部分),我已经让代码变得更好或更坏。完整的代码可以在这里找到,但是仍然很粗糙。 验证器本身在这里可用,有一个配套测试。新冠通行证的技术规范在这里。至少有一个相关部分。 在与另一个开发人员合作后,我想我已经确定了解释提供的公钥。下面的代码(删除了调试输出)。公钥详细信息来自这里 该错误可能存在

  • 我正在尝试使用C#和内置的加密库来验证使用EC密钥SHA256创建的签名。 我使用openssl创建了一个私钥和相应的证书: 以下是我正在使用的密钥(别担心,它们并不重要): 然后,我有一个包含字符串“Hello”的简单数据文件。然后,我使用openssl对该文件进行签名,如下所示: 然后,我可以通过首先从证书中提取公钥,然后使用公钥来验证签名: 然后,我尝试使用 C#(.NET Core 3.1

  • 我正在尝试在python中使用ECDSA验证比特币签名,但发现它非常困难,许多尝试已经失败了。 参数: 注意:我已经根据ECDSA的要求将签名从base64转换为hexstring。每当我试图验证它时,它会说: 预期64字节签名(128十六进制字符串),提供了65字节签名(130十六进制字符串) 我查看了许多关于ECDSA的stackoverflow问题,但没有一个答案与我的qs 100%相关。感

  • 我正在尝试使用公钥验证散列的ECDSA签名。我写了一个小的围棋程序,成功地做到了这一点,但是我不能把它移植到C语言中。 这是我的输入数据: < li >公钥:< code > mfkwewyhkozizj 0 caqyikozij0 dackcdqgaedd 9 vxm phjv 4 vofo 0 zofplr r5 iczxquxsr 9 eka ouo 9 b 7 wl 1 daggi 0 e