【发布时间】:2018-07-10 13:36:36
【问题描述】:
我正在尝试生成 ECDSA 自签名证书,如generate certificate using ECDSA 中所述。将 bartonjs 答案中的所有部分放在一起并使用Net.Framework 4.7(或Net.Core 2.0)以下代码似乎可以正常工作,尽管还存在一些歧义(至少一个):
我不确定如何将私钥(“D”参数)从BC-BigInteger 正确转换为MS-byte[]。使用BigInteger.ToByteArray() 会抛出异常:
CryptographicException:指定的密钥参数无效。 Q.X 和 Q.Y 是必填字段。 Q.X、Q.Y 的长度必须相同。如果 D 指定它必须与命名的 Q.X 和 Q.Y 长度相同 曲线或与显式曲线的顺序相同的长度。
在验证 ECParameters 时(方法 ECParameters.Validate())。使用BigInteger.ToByteArrayUnsigned() 可以提供更好的结果(在数百个生成的密钥对上出现一次失败),但仍然...
使用ToByteArray() 时,转换后的“D”通常长一个字节(“D”有 33 个字节,而 D.X 和 D.Y 有 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.Q.X = bcPublKey.Q.XCoord.GetEncoded();
msEcp.Q.Y = bcPublKey.Q.YCoord.GetEncoded();
try
{
msEcp.Validate();
validated = true;
}
catch (Exception e)
{
// Validate() occasionally throws CryptographicException:
// The specified key parameters are not valid. Q.X and Q.Y are required fields. Q.X, Q.Y must be the same length. If D is specified it must be the same length as Q.X and Q.Y for named curves or the same length as Order for explicit curves.
// e.g.: D = 31, Q.X = 32, Q.Y = 32.
validated = false;
Console.WriteLine("D = {0}, Q.X = {1}, Q.Y = {2}. {3}: {4}", msEcp.D.Length, msEcp.Q.X.Length, msEcp.Q.Y.Length, 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;
}
提前谢谢你
【问题讨论】:
-
当 D 为 33 字节时,D[0] 可能为 0。如果是这种情况,您不希望填充为零。当 D 为 31 字节(或更小)时,您需要在开头插入 0。当然,如果没有失败的例子,就很难证明这一点。
-
你说得对。添加/删除前导零可确保验证通过。但是这样可以吗?带有“固定”密钥的密钥/证书可以吗?
标签: c# certificate bouncycastle ecdsa