转换椭圆曲线参数(BC到MS)

我正在尝试生成ECDSA自签名证书,如使用ECDSA生成证书中所述。 将所有来自bartonjs的答案放在一起并使用Net.Framework 4.7 (或Net.Core 2.0 )代码似乎正在起作用,尽管有一些含糊不清(至少有一个):

我不确定如何正确地将私钥(’D’参数)从BC-BigIntegerMS-byte[] 。 使用BigInteger.ToByteArray()抛出exception:

CryptographicException :指定的键参数无效。 QX和QY是必填字段。 QX,QY必须长度相同。 如果指定了D,则它必须与命名曲线的QX和QY的长度相同,或者与显式曲线的Order的长度相同。

同时validationECParameters(方法ECParameters.Validate() )。 使用BigInteger.ToByteArrayUnsigned()可以提供更好的结果(几百个生成的密钥对失败),但仍然……

当使用ToByteArray()转换’D’时通常长一个字节(’D’与DX相比有33个字节,而DY有32个字节)。 使用ToByteArrayUnsigned() ,’D’有时短一个字节。

所以我的问题是使用ToByteArrayUnsigned()是否可以。

 private const string NCryptExportPolicyProperty = "Export Policy"; private const string SignatureAlgorithm = "Sha256WithECDSA"; private static readonly ECCurve MsCurve = ECCurve.NamedCurves.nistP256; private static readonly DerObjectIdentifier BcCurve = SecObjectIdentifiers.SecP256r1; // must correspond with MsCurve public static X509Certificate2 Create() { // 1. generate keys: IAsymmetricCipherKeyPairGenerator bcKeyGen = GeneratorUtilities.GetKeyPairGenerator("ECDSA"); bcKeyGen.Init(new ECKeyGenerationParameters(BcCurve, new SecureRandom())); ECPrivateKeyParameters bcPrivKey; ECPublicKeyParameters bcPublKey; bool validated; ECParameters msEcp; do { AsymmetricCipherKeyPair bcKeyPair = bcKeyGen.GenerateKeyPair(); bcPrivKey = (ECPrivateKeyParameters)bcKeyPair.Private; bcPublKey = (ECPublicKeyParameters)bcKeyPair.Public; // 2. ensure generated bc-keys can be translated to cng (see exception below) msEcp = new ECParameters(); msEcp.Curve = MsCurve; msEcp.D = bcPrivKey.D.ToByteArrayUnsigned(); // or bcPrivKey.D.ToByteArray() ?? msEcp.QX = bcPublKey.Q.XCoord.GetEncoded(); msEcp.QY = bcPublKey.Q.YCoord.GetEncoded(); try { msEcp.Validate(); validated = true; } catch (Exception e) { // Validate() occasionally throws CryptographicException: // The specified key parameters are not valid. QX and QY are required fields. QX, QY must be the same length. If D is specified it must be the same length as QX and QY for named curves or the same length as Order for explicit curves. // eg: D = 31, QX = 32, QY = 32. validated = false; Console.WriteLine("D = {0}, QX = {1}, QY = {2}. {3}: {4}", msEcp.D.Length, msEcp.QXLength, msEcp.QYLength, e.GetType().Name, e.Message); } } while (!validated); // 3. create x509 certificate: X509V3CertificateGenerator bcCertGen = new X509V3CertificateGenerator(); bcCertGen.SetPublicKey(bcPublKey); // .. set subject, validity period etc ISignatureFactory sigFac = new Asn1SignatureFactory(SignatureAlgorithm, bcPrivKey); Org.BouncyCastle.X509.X509Certificate bcX509Cert = bcCertGen.Generate(sigFac); byte[] x509CertEncoded = bcX509Cert.GetEncoded(); X509Certificate2 msNewCert; // 4. use translated (and validated) parameters: using (ECDsaCng msEcdsa = new ECDsaCng()) { msEcdsa.ImportParameters(msEcp); CngKey msPrivateKey = msEcdsa.Key; // 5. make private key exportable: byte[] bytes = BitConverter.GetBytes((int)(CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport)); CngProperty pty = new CngProperty(NCryptExportPolicyProperty, bytes, CngPropertyOptions.Persist); msPrivateKey.SetProperty(pty); // 6. tie keys together: using (X509Certificate2 msPubCertOnly = new X509Certificate2(x509CertEncoded)) { msNewCert = MateECDsaPrivateKey(msPubCertOnly, msPrivateKey); // method from bartonjs's answer } } return msNewCert; } 

先感谢您

当你得到太多字节(在这种情况下是33)时,第一个字节应该是0x00 ,你需要删除它。 如果你得到的太少(技术上讲D = 1有效),你需要插入零来填充数组。

原因是.NET的结构期望D看起来像底层的Windows CNG导入API,这意味着D是固定的 – 带有无符号的大端大整数。 BouncyCastle为您提供BER INTEGER编码,当最高有效字节(字节[0],大端)的高位设置为应被视为正数的数字时,需要插入0x00字节。

BER还有一个规则,即使用最小字节数,这就是为什么有时BouncyCastle会给出一个太小的数字。

QX和QY是可以的,因为ECPoint编码规则指定一个固定大小的大端整数,其大小由曲线确定; 这就是BouncyCastle具有GetEncoded方法而不仅仅是ToByteArrayUnsigned

 private static byte[] FixSize(byte[] input, int expectedSize) { if (input.Length == expectedSize) { return input; } byte[] tmp; if (input.Length < expectedSize) { tmp = new byte[expectedSize]; Buffer.BlockCopy(input, 0, tmp, expectedSize - input.Length, input.Length); return tmp; } if (input.Length > expectedSize + 1 || input[0] != 0) { throw new InvalidOperationException(); } tmp = new byte[expectedSize]; Buffer.BlockCopy(input, 1, tmp, 0, expectedSize); return tmp; } ... msEcp = new ECParameters(); msEcp.Curve = MsCurve; msEcp.QX = bcPublKey.Q.XCoord.GetEncoded(); msEcp.QY = bcPublKey.Q.YCoord.GetEncoded(); msEcp.D = FixSize(bcPrivKey.D.ToByteArrayUnsigned(), msEcp.QXLength);