目的:使用 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.代币生成:
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()在尝试生成Jose.JWT.Encode函数所需的CngKey时抛出">参数不正确"错误。不知道我错过了什么步骤。谢谢。
"ECCPUBLICBLOB"格式与证书中的"公钥"字段不同。
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()
应该为您做正确的事情(每次调用都会返回一个新实例,因此请适当地管理生存期)
我能够在以下帖子的帮助下使CngKey 导入工作。
现在Jose.JWT.Encode()
在以下行抛出"无法签名"错误:
return JWT.Encode(claims, cng, JwsAlgorithm.ES256)
我最终使用 .NET 4.6GetECDsaPrivateKey()
编写了自己的私钥签名实现。
您可以在以下帖子中看到我的最终解决方案
公钥需要通过丢弃第一个字节来修改,留下 64 个字节,然后前缀为4 个字节作为曲线,4个字节作为密钥长度。以下是完整的解决方案:
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