但是,在IOS系统上使用BouncyCastle签名和验证签名(模拟器和电话会产生相同的结果)有时(有时是很频繁地)产生看起来无效的签名。
为了确保我没有做错任何事情,我编写了一个简短的测试程序,该程序将随机生成100组数据,对每组进行10000次签名,并验证每个签名。在Windows上实现所需行为的纯BouncyCastle实现-可以工作,一百万次中的一百万次。
此代码在Android上测试时似乎也可以使用;同样,有100%的时间。
但是,在IOS上进行测试时,无法复制相同的结果。
这是在IOS上进行的结果测试(每轮测试包括关闭模拟器,然后通过Visual Studio运行IOS程序。然后,该程序生成一个新的私钥/公钥对,并对一组随机的数据字节进行签名10000次):
该测试最初仅在iPhone XR上进行,但我认为这可能与特定型号的手机有关,因此我也使用iPhone X进行了测试。以前的测试有更多结果,但即使在物理iPhone XR上进行了测试,它们的外观也几乎与下面列出的示例相同。
我不知道测试回合1和2如何以10001次而不是10000次运行-开始测试后,与流程的交互为零,直到达到退出断点为止。 >
第2轮测试设法以某种方式执行了额外的4个测试,与记录的4个失败的测试相对应-在此测试期间唯一改变的是我设置了一个断点中间循环以检查进度,并且然后点击继续
此外,我们决定检查两个系统的字节序;如果它们不匹配怎么办?但是,搜索没有结果,因为通过BitConverter.IsLittleEndian
在调试器的即时窗口中进行的快速检查显示两个系统都使用Little Endian格式。
用于测试的代码,已修改为仅使用一组数据签名10000次
static void SigningTest(byte[] data, byte[] pubkey, byte[] privkey)
{
var curve = SecNamedCurves.GetByName("secp256r1");
var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
var d = new Org.BouncyCastle.Math.BigInteger(privkey);
var xx = new Org.BouncyCastle.Math.BigInteger(1, pubkey.Take(32).ToArray());
var yy = new Org.BouncyCastle.Math.BigInteger(1, pubkey.Skip(32).ToArray());
var q = curve.Curve.CreatePoint(xx, yy);
var publicParams = new ECPublicKeyParameters(q, domain);
var privateParams = new ECPrivateKeyParameters(d, domain);
var cipherkp = new AsymmetricCipherKeyPair(publicParams, privateParams);
var signer = SignerUtilities.GetSigner("SHA256withECDSA");
signer.Init(true, cipherkp.Private);
var ccount = 0;
var icount = 0;
for (var i = 0; i < 10000; i++)
{
signer.BlockUpdate(data, 0, data.Length);
var signature = signer.GenerateSignature();
var der = Asn1Object.FromByteArray(signature) as DerSequence;
var arrList = new List<byte[]>();
foreach (DerInteger theInt in der)
{
var barr = theInt.PositiveValue.ToByteArrayUnsigned();
if (barr.Length == 31)
{
barr = new byte[32];
Array.Copy(theInt.PositiveValue.ToByteArrayUnsigned(), 0, barr, 1, 31);
}
arrList.Add(barr);
}
var realsig = new byte[64];
Array.Copy(arrList[0], realsig, arrList[0].Length);
Array.Copy(arrList[1], 0, realsig, arrList[0].Length, arrList[1].Length);
if (Verify(data, pubkey, realsig))
{
ccount++;
}
else
{
icount++;
}
}
// Add something here like System.Diagnostics.Debugger.Break() so that a break point can be set.
}
static bool Verify(byte[] data, byte[] publicKey, byte[] signature)
{
var curve = SecNamedCurves.GetByName("secp256r1");
var x = new byte[32];
var y = new byte[32];
Array.Copy(publicKey, 0, x, 0, 32);
Array.Copy(publicKey, 32, y, 0, 32);
var derSignature = new DerSequence(
new DerInteger(new Org.BouncyCastle.Math.BigInteger(1, signature.Take(32).ToArray())),
new DerInteger(new Org.BouncyCastle.Math.BigInteger(1, signature.Skip(32).Take(32).ToArray()))
)
.GetDerEncoded();
var xx = new Org.BouncyCastle.Math.BigInteger(1, publicKey.Take(32).ToArray());
var yy = new Org.BouncyCastle.Math.BigInteger(1, publicKey.Skip(32).ToArray());
var domainparams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());
var ecp = curve.Curve.CreatePoint(xx, yy);
var pubkeyparams = new ECPublicKeyParameters(ecp, domainparams);
var verifier = SignerUtilities.GetSigner("SHA256withECDSA");
verifier.Init(false, pubkeyparams);
verifier.BlockUpdate(data, 0, data.Length);
return verifier.VerifySignature(derSignature);
}
答案 0 :(得分:0)
正如Peter Dettman指出的那样,私钥不是使用BigInteger
构造函数的签名版本创建的。
在更改该行以使用构造函数的带符号版本的同时,还对测试代码进行了修改,以将签名者的初始化移动到for循环的主体中。
if (barr.Length == 31)
{
barr = new byte[32];
Array.Copy(theInt.PositiveValue.ToByteArrayUnsigned(), 0, barr, 1, 31);
}
也更改为
if (barr.Length < 32)
{
barr = new byte[32 - barr.Length].Concat(barr).ToArray();
}
为了说明barr
的字节数组长度没有理论下限的事实。
在进行了这些更改之后,该代码在所有情况下似乎都能正常运行。