我正在处理 Apple Pay 的应用内钱包配置。我做了一个测试项目,它与我从苹果文档中获得的测试向量相匹配(利用 System.Security 和 BouncyCastle),所以很确定我在正确的轨道上。
但是,当我切换到我生成的临时密钥对(而不是给定的用于测试的密钥对)时,私钥在生成 ECDH 共享密钥时经常会导致错误。奇怪的是,它似乎有一半的时间没有错误。
这里是生成临时密钥对的代码:
ephemKeyPair = ECDiffieHellman.Create(CURVE); // System.Security.Cryptography.ECDiffieHellman
// save our public and private key bytes
ephemPublicKey = GetPublicKeyFromEncodedBytes(ephemKeyPair.PublicKey.ExportSubjectPublicKeyInfo());
ephemPrivateKey = ephemKeyPair.ExportParameters(true).D;
...
private static byte[] GetPublicKeyFromEncodedBytes(byte[] publicKeyEncoded)
{
byte[] bytes = new byte[65];
Array.Copy(publicKeyEncoded, 26, bytes, 0, bytes.Length);
return bytes;
}
当我尝试生成我的共享密钥(基于 Apple 公钥 + 我的临时私钥)时,它会在大约一半的时间内抛出错误
Scalar is not in the interval [1, n - 1] (Parameter 'd')
。这是调用代码:
GenerateSharedSecret(ephemPrivateKey /* generated above */, applePublicKey /* extracted from input, currently a test .pem file. this has been verified in the Apple docs*/);
以及生成共享密钥的代码:
public void GenerateSharedSecret(byte[] privateKeyIn, byte[] publicKeyIn)
{
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
X9ECParameters? curve = null;
ECDomainParameters? ecParam = null;
ECPrivateKeyParameters? privKey = null;
ECPublicKeyParameters? pubKey = null;
Org.BouncyCastle.Math.EC.ECPoint point;
curve = NistNamedCurves.GetByName("P-256");
ecParam = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());
BigInteger bigInt = new BigInteger(privateKeyIn);
privKey = new ECPrivateKeyParameters(bigInt, ecParam); // *** ERROR HERE ***
point = ecParam.Curve.DecodePoint(publicKeyIn);
pubKey = new ECPublicKeyParameters(point, ecParam);
agreement.Init(privKey);
BigInteger secret = agreement.CalculateAgreement(pubKey);
sharedSecret = secret.ToByteArrayUnsigned();
}
我是不是以某种方式不正确地生成了临时密钥,或者完全是其他原因?
**编辑 3/15/23
好的,所以我根据 Maarten 的反馈重写了部分内容。我现在使用所有 BC,而不是使用一些
System.Security
对象和一些 BouncyCastle
对象,它使事情变得更加顺利。我还没有验证加密数据的有效性,但我不再收到这些错误,现在更直接了。
更新代码:
private readonly X9ECParameters curve = NistNamedCurves.GetByName("P-256");
private ECDomainParameters ecParam { get; set; }
private ECPrivateKeyParameters privateKeyParameters { get; set; }
private AsymmetricCipherKeyPair ephemKeyPair { get; set; }
...
private void GenerateEphemeralKeyPair()
{
ecParam = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());
var secureRandom = new SecureRandom();
var keyParams = new ECKeyGenerationParameters(ecParam, secureRandom);
var generator = new ECKeyPairGenerator("ECDH");
generator.Init(keyParams);
ephemKeyPair = generator.GenerateKeyPair();
ECPublicKeyParameters publicKeyParameters = (ECPublicKeyParameters)
ephemKeyPair.Public;
privateKeyParameters = (ECPrivateKeyParameters) ephemKeyPair.Private;
ephemPublicKey = publicKeyParameters.Q.GetEncoded();
ephemPrivateKey = privateKeyParameters.D.ToByteArrayUnsigned();
}
...
private void GenerateSharedSecret(byte[] publicKeyIn)
{
Org.BouncyCastle.Math.EC.ECPoint point = ecParam.Curve.DecodePoint(publicKeyIn);
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(point, ecParam);
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
agreement.Init(privateKeyParameters);
BigInteger secret = agreement.CalculateAgreement(pubKey);
sharedSecret = secret.ToByteArrayUnsigned();
sharedSecretAsHex = Convert.ToHexString(sharedSecret);
}