导入ECC密钥 - CngKey.Import() - 参数不正确

时间:2017-03-20 23:45:14

标签: c# jwt x509certificate ecdsa

目的:使用jose-jwt生成 ES256 签名 JWT

步骤:

1.使用openssl生成私钥证书

openssl ecparam -name prime256v1 -genkey > privateKey.pem
openssl req -new -key privateKey.pem -x509 -nodes -days 365 -out public.cer

2.Token generation:

var payload = new Dictionary<string, object>()
{
   { "sub", "mr.x@contoso.com" },
   { "exp", 1300819380   }
};
var certificate = X509Certificate.CreateFromCertFile("public.cer");
byte[] publicKey = certificate.GetPublicKey(); //public key has 65 bytes

//Below step is throwing an error:
var cng = CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob);
var token = JWT.Encode(claims, cng, JwsAlgorithm.ES256);

CngKey.Import()投掷&#34; 参数不正确&#34;尝试生成 Jose.JWT.Encode 函数所需的 CngKey 时出错。不知道我错过了哪一步。感谢。

2 个答案:

答案 0 :(得分:1)

&#34; ECCPUBLICBLOB&#34;格式与&#34;公钥&#34;相同证书中的字段。

another question's answer中解释了ECCPUBLICBLOB的格式,但这里有一个快速摘要:

UINT32 Magic
UINT32 cbKey
<cbKey bytes of public key>

Magic的值取决于您尝试导入的曲线和算法(https://referencesource.microsoft.com/#system.core/System/Security/Cryptography/BCryptNative.cs,fde0749a0a5f70d8,references处的一些提示)。

cbKey是公钥中的字节数。

公钥字节与您从GetPublicKey()获得的字节略有不同。它只是曲线X坐标,它将是(对于NIST P-256)字节1..33(GetPublicKey()的第一个字节将是0x04,表示有效载荷是未压缩的,那么X坐标的32个字节,然后是Y坐标的32个字节。)

IEnumerable<byte> blobBytes = BitConverter.GetBytes(0x31534345);
blobBytes = blobBytes.Append(BitConverter.GetBytes(32));
blobBytes = blobBytes.Append(cert.GetPublicKey().Skip(1).Take(32));

byte[] eccblob = blobBytes.ToArray();

System.Linq扩展方法用于简洁。

但是,如果您只需要一个对象实例,cert.GetECDsaPublicKey()应该为您做正确的事情(每次调用都会返回一个新实例,因此适当地管理生命周期)

答案 1 :(得分:0)

我能够在following post的帮助下使 CngKey导入工作。

现在Jose.JWT.Encode()在以下行投掷“无法签名”错误:

return JWT.Encode(claims, cng, JwsAlgorithm.ES256)

我最后使用 .NET 4.6 GetECDsaPrivateKey()编写了自己的私钥签名实现。

您可以在following post

上看到我的最终解决方案

需要通过丢弃第一个字节来修改公钥,留下64个字节,然后使用 4个字节作为曲线 4个字节作为密钥长度< / em>的。这是完整的解决方案:

var payload = new Dictionary<string, object>()
{
   { "sub", "mr.x@contoso.com" },
   { "exp", 1300819380     }
};
var certificate = X509Certificate.CreateFromCertFile("public.cer");
byte[] publicKey = certificate.GetPublicKey(); //public key has 65 bytes

//Discard the first byte (it is always 0X04 for ECDSA public key)
publicKey = publicKey.Skip(1).ToArray();:

//Generate 4 bytes for curve and 4 bytes for key length [ 69(E), 67(C), 83(S), 49(1), 32(Key length), 0, 0, 0 ]
byte[] x = { 69, 67, 83, 49, 32, 0, 0, 0 };    

//Prefix above generated array to existing public key array
publicKey = x.Concat(publicKey).ToArray();

var cng = CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob); //This works
return JWT.Encode(claims, cng, JwsAlgorithm.ES256); //Fixed, see my final solution link above