Bouncy Castle SHA-384withECDSA 签名验证给出异常
Bouncy Castle SHA-384withECDSA Signature Verification Giving an Exception
我正在使用 BouncyCastle 使用 C# 和 .NET 4.7 进行签名验证。
我正在关注 SO 上的一个答案,该答案解释了使用 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);
}
我正在尝试使其适应我的用例,该用例正在验证 SHA-384withECDSA 签名。我发现的唯一区别是密钥长度不同。对于 SHA-256withECDSA,使用的 public 密钥长度转换为 64 字节数组。因此在实现中我正在做:
BigInteger x = new BigInteger(1, pubkey.Take(32).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(32).ToArray());
对于 SHA-384withECDSA,我已经调整如下。
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-384withECDSA 签名的代码抛出异常:
System.ArgumentException
HResult=0x80070057
Message=value invalid in Fp field element
Parameter name: x
Source=BouncyCastle.Crypto
StackTrace:
at Org.BouncyCastle.Math.EC.FpFieldElement..ctor(BigInteger q, BigInteger r, BigInteger x)
at Org.BouncyCastle.Math.EC.FpCurve.FromBigInteger(BigInteger x)
at Org.BouncyCastle.Math.EC.ECCurve.CreatePoint(BigInteger x, BigInteger y, Boolean withCompression)
at Org.BouncyCastle.Math.EC.ECCurve.CreatePoint(BigInteger x, BigInteger y)
at SignatureVerification.Program.VerifySHA384Bouncy(Byte[] message, Byte[] signature, Byte[] pubkey)
我不确定我是否在处理问题时偏离了方向,或者我正在比较苹果和橙子,我没有找到足够的 SHA-384withECDSA 示例来解决这个问题。任何帮助将不胜感激。
有几个问题:
- 使用 ASN.1 解析器的分析表明 public 密钥以 X.509 格式给出,s。例如here。 IE。原始密钥
x|y
结果为最后 2 * 48 = 96
个字节:
pub384 = pub384.Substring(pub384.Length - 96 * 2); // 96 * 2 due to the hex encoding
- 确定
x
和y
坐标为(x
和y
各48
字节):
BigInteger x = new BigInteger(1, pubkey.Take(48).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(48).ToArray());
- 此外,使用 ASN.1 解析器的分析表明 public 键属于曲线
brainpoolp384t1
:
X9ECParameters ecParams = ECNamedCurveTable.GetByName("brainpoolp384t1");
- 另外,签名
sig384
已经指定为ASN.1/DER格式,而不是r|s(IEEE P1363)格式,所以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);
}
我正在使用 BouncyCastle 使用 C# 和 .NET 4.7 进行签名验证。
我正在关注 SO 上的一个答案,该答案解释了使用 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);
}
我正在尝试使其适应我的用例,该用例正在验证 SHA-384withECDSA 签名。我发现的唯一区别是密钥长度不同。对于 SHA-256withECDSA,使用的 public 密钥长度转换为 64 字节数组。因此在实现中我正在做:
BigInteger x = new BigInteger(1, pubkey.Take(32).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(32).ToArray());
对于 SHA-384withECDSA,我已经调整如下。
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-384withECDSA 签名的代码抛出异常:
System.ArgumentException HResult=0x80070057 Message=value invalid in Fp field element Parameter name: x Source=BouncyCastle.Crypto StackTrace: at Org.BouncyCastle.Math.EC.FpFieldElement..ctor(BigInteger q, BigInteger r, BigInteger x) at Org.BouncyCastle.Math.EC.FpCurve.FromBigInteger(BigInteger x) at Org.BouncyCastle.Math.EC.ECCurve.CreatePoint(BigInteger x, BigInteger y, Boolean withCompression) at Org.BouncyCastle.Math.EC.ECCurve.CreatePoint(BigInteger x, BigInteger y) at SignatureVerification.Program.VerifySHA384Bouncy(Byte[] message, Byte[] signature, Byte[] pubkey)
我不确定我是否在处理问题时偏离了方向,或者我正在比较苹果和橙子,我没有找到足够的 SHA-384withECDSA 示例来解决这个问题。任何帮助将不胜感激。
有几个问题:
- 使用 ASN.1 解析器的分析表明 public 密钥以 X.509 格式给出,s。例如here。 IE。原始密钥
x|y
结果为最后2 * 48 = 96
个字节:
pub384 = pub384.Substring(pub384.Length - 96 * 2); // 96 * 2 due to the hex encoding
- 确定
x
和y
坐标为(x
和y
各48
字节):
BigInteger x = new BigInteger(1, pubkey.Take(48).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(48).ToArray());
- 此外,使用 ASN.1 解析器的分析表明 public 键属于曲线
brainpoolp384t1
:
X9ECParameters ecParams = ECNamedCurveTable.GetByName("brainpoolp384t1");
- 另外,签名
sig384
已经指定为ASN.1/DER格式,而不是r|s(IEEE P1363)格式,所以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);
}