Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 转换椭圆曲线参数(BC到MS)_C#_Certificate_Bouncycastle_Ecdsa - Fatal编程技术网

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

C# 转换椭圆曲线参数(BC到MS),c#,certificate,bouncycastle,ecdsa,C#,Certificate,Bouncycastle,Ecdsa,我正在尝试生成ECDSA自签名证书,如中所述。将bartonjs答案中的所有部分放在一起,并使用Net.Framework 4.7(或Net.Core 2.0)以下代码似乎可以正常工作,尽管还存在一些歧义(至少一个): 我不知道如何正确地将私钥('D'参数)从BC biginger转换为MS byte[]。使用BigInteger.ToByteArray()会引发异常: 加密异常:指定的密钥参数无效。 Q.X和Q.Y是必填字段。Q.X,Q.Y的长度必须相同。如果 如果指定了D,则其长度必须与命名

我正在尝试生成ECDSA自签名证书,如中所述。将bartonjs答案中的所有部分放在一起,并使用
Net.Framework 4.7
(或
Net.Core 2.0
)以下代码似乎可以正常工作,尽管还存在一些歧义(至少一个):

我不知道如何正确地将私钥('D'参数)从
BC biginger
转换为
MS byte[]
。使用
BigInteger.ToByteArray()
会引发异常:

加密异常:指定的密钥参数无效。 Q.X和Q.Y是必填字段。Q.X,Q.Y的长度必须相同。如果 如果指定了D,则其长度必须与命名的Q.X和Q.Y的长度相同 曲线或与显式曲线的顺序相同的长度

验证ECParameters时(方法
ECParameters.Validate()
)。使用
biginger.ToByteArrayUnsigned()
可以提供更好的结果(数百个生成的密钥对中有一个失败),但仍然

使用
ToByteArray()
时,转换后的“D”通常长一个字节(“D”有33个字节,而D.X和D.Y有32个字节)。使用
ToByteArrayUnsigned()
D有时短一个字节

因此,我的问题是是否可以使用
ToByteArrayUnsigned()


提前感谢

当您获得的字节太多(本例中为33个)时,第一个字节应为
0x00
,您需要将其删除。当得到的数据太少时(从技术上讲,D=1是有效的),需要插入零来填充数组

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

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

Q.X和Q.Y是可以的,因为ECPoint编码规则指定了一个固定大小的大端整数,其大小由曲线决定;这就是为什么BouncyCastle使用了
GetEncoded
方法,而不仅仅是
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;
}
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.Q.X = bcPublKey.Q.XCoord.GetEncoded();
msEcp.Q.Y = bcPublKey.Q.YCoord.GetEncoded();
msEcp.D = FixSize(bcPrivKey.D.ToByteArrayUnsigned(), msEcp.Q.X.Length);
private static byte[]FixSize(byte[]input,int expectedSize)
{
if(input.Length==expectedSize)
{
返回输入;
}
字节[]tmp;
if(input.LengthexpectedSize+1 | | input[0]!=0)
{
抛出新的InvalidOperationException();
}
tmp=新字节[expectedSize];
Buffer.BlockCopy(输入,1,tmp,0,expectedSize);
返回tmp;
}
...
msEcp=新的ECParameters();
msEcp.Curve=MsCurve;
msEcp.Q.X=bcPublKey.Q.XCoord.GetEncoded();
msEcp.Q.Y=bcPublKey.Q.YCoord.GetEncoded();
msEcp.D=固定大小(bcPrivKey.D.ToByteArrayUnsigned(),msEcp.Q.X.Length);

当您获取的字节太多(本例中为33个)时,第一个字节应为
0x00
,您需要将其删除。当得到的数据太少时(从技术上讲,D=1是有效的),需要插入零来填充数组

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

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

Q.X和Q.Y是可以的,因为ECPoint编码规则指定了一个固定大小的大端整数,其大小由曲线决定;这就是为什么BouncyCastle使用了
GetEncoded
方法,而不仅仅是
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;
}
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.Q.X = bcPublKey.Q.XCoord.GetEncoded();
msEcp.Q.Y = bcPublKey.Q.YCoord.GetEncoded();
msEcp.D = FixSize(bcPrivKey.D.ToByteArrayUnsigned(), msEcp.Q.X.Length);
private static byte[]FixSize(byte[]input,int expectedSize)
{
if(input.Length==expectedSize)
{
返回输入;
}
字节[]tmp;
if(input.LengthexpectedSize+1 | | input[0]!=0)
{
抛出新的InvalidOperationException();
}
tmp=新字节[expectedSize];
Buffer.BlockCopy(输入,1,tmp,0,expectedSize);
返回tmp;
}
...
msEcp=新的ECParameters();
msEcp.Curve=MsCurve;
msEcp.Q.X=bcPublKey.Q.XCoord.GetEncoded();
msEcp.Q.Y=bcPublKey.Q.YCoord.GetEncoded();
msEcp.D=固定大小(bcPrivKey.D.ToByteArrayUnsigned(),msEcp.Q.X.Length);

以下代码将帮助您,您可以使用bouncy castle库生成算法:

private static ECDsa GetEllipticCurveAlgorithm(string privateKey)
    {
        var keyParams = (ECPrivateKeyParameters)PrivateKeyFactory
            .CreateKey(Convert.FromBase64String(privateKey));

        var normalizedECPoint = keyParams.Parameters.G.Multiply(keyParams.D).Normalize();

        return ECDsa.Create(new ECParameters
        {
            Curve = ECCurve.CreateFromValue(keyParams.PublicKeyParamSet.Id),
            D = keyParams.D.ToByteArrayUnsigned(),
            Q =
        {
            X = normalizedECPoint.XCoord.GetEncoded(),
            Y = normalizedECPoint.YCoord.GetEncoded()
        }
        });
    }
并按以下方式生成令牌:

var signatureAlgorithm = GetEllipticCurveAlgorithm(privateKey);

            ECDsaSecurityKey eCDsaSecurityKey = new ECDsaSecurityKey(signatureAlgorithm)
            {
                KeyId = settings.Apple.KeyId
            };

            var handler = new JwtSecurityTokenHandler();   
            var token = handler.CreateJwtSecurityToken(
                issuer: iss,
                audience: AUD,
                subject: new ClaimsIdentity(new List<Claim> { new Claim("sub", sub) }),
                expires: DateTime.UtcNow.AddMinutes(5), 
                issuedAt: DateTime.UtcNow,
                notBefore: DateTime.UtcNow,
                signingCredentials: new SigningCredentials(eCDsaSecurityKey, SecurityAlgorithms.EcdsaSha256));
var-signatureAlgorithm=GetEllipticCurveAlgorithm(privateKey);
ECDsaSecurityKey ECDsaSecurityKey=新的ECDsaSecurityKey(签名算法)
{
KeyId=settings.Apple.KeyId
};
var handler=新的JwtSecurityTokenHandler();
var token=handler.CreateJwtSecurityToken(
发行人:国际空间站,
观众:澳元,
主题:新的索赔实体(新的清单{新的索赔(“sub”,sub)}),
过期:DateTime.UtcNow.AddMinutes(5),
发布日期:DateTime.UtcNow,
notBefore:DateTime.UtcNow,
签名凭证:新的签名凭证(eCDsaSecurityKey,SecurityAlgorithms.EcdsaSha256));
以下内容