转换椭圆曲线参数(BC到MS)
我正在尝试生成ECDSA自签名证书,如使用ECDSA生成证书中所述。 将所有来自bartonjs的答案放在一起并使用Net.Framework 4.7
(或Net.Core 2.0
)代码似乎正在起作用,尽管有一些含糊不清(至少有一个):
我不确定如何正确地将私钥(’D’参数)从BC-BigInteger
为MS-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);
- 如何在C#/ Bouncy Castle中创建PBKDF2-SHA256密码哈希
- 使用C#,BouncyCastle和导入的RSA密钥进行RSA签名和validation – 内部使用Python示例和非工作C#代码示例
- 加密BouncyCastle RSA密钥对并存储在SQL2008数据库中
- BouncyCastle在C#threadsafe中的SecureRandom?
- C#BouncyCastle – 使用公钥/私钥进行RSA加密
- 仅使用公钥创建AsymmetricCipherKeyPair
- JavaScript中的加密不会在C#中解密
- 开始使用BouncyCastle加密dll c#
- 需要C#中的BouncyCastle PGP文件加密示例