apple-push-notifications之ECDsa在Linux上登录.Net Core
duanxz
阅读:16
2025-06-02 22:19:02
评论:0
我正在尝试创建一个C#实现,以通过Docker中具有.Net核心的HTTP/2 APNS端点将推送发送给苹果。其中一部分要求发送加密的JWT授权 token 以及有效载荷。使用.Net内核,我可以在Windows上运行时对 token 进行签名,但是在Linux Docker镜像中运行时,它将提示加载 key 。
在.net Core Docker镜像中运行时,在加载 key 时遇到平台不支持的异常。
public static string SignES256(string privateKey, string header, string payload)
{
// This is the failing Call
CngKey key = CngKey.Import(Convert.FromBase64String(privateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
using (ECDsaCng dsa = new ECDsaCng(key))
{
var unsignedJwtData =
System.Convert.ToBase64String(Encoding.UTF8.GetBytes(header)) + "." + System.Convert.ToBase64String(Encoding.UTF8.GetBytes(payload));
var unsignedJwtDataBytes = Encoding.UTF8.GetBytes(unsignedJwtData);
var signature =
dsa.SignData(unsignedJwtDataBytes, 0, unsignedJwtDataBytes.Length, HashAlgorithmName.SHA256 );
return unsignedJwtData + "." + System.Convert.ToBase64String(signature);
}
}
如何在Linux上的.Net Core中执行此操作?
谢谢。
请您参考如下方法:
ECDsaCng是使用Windows CNG的ECDSA实现。它特定于Windows,因此在Linux上不受支持。
跨平台的方法是
using (ECDsa ecdsa = ECDsa.Create())
{
ecdsa.ImportParameters(Pkcs8ToParameters(privateKey));
// the stuff in your current using
}
当然,ECParameters的PKCS#8不是世界上最简单的事情。但是我们可以放手一搏。在 another answer中,有一个为RSA构建PKCS#8的细目。
让我们看一下这个blob:
308187020100301306072A8648CE3D020106082A8648CE3D030107046D306B02
0101042070A12C2DB16845ED56FF68CFC21A472B3F04D7D6851BF6349F2D7D5B
3452B38AA144034200048101ECE47464A6EAD70CF69A6E2BD3D88691A3262D22
CBA4F7635EAFF26680A8D8A12BA61D599235F67D9CB4D58F1783D3CA43E78F0A
5ABAA624079936C0C3A9
它分解像
30 /* SEQUENCE */
81 87 (payload is 0x87 bytes)
02 /* INTEGER */ 01 (1 byte) 00 // Integer: 0. // validate this
30 /* SEQUENCE */ 13 (0x13 bytes)
06 /* OBJECT IDENTIFIER */ 07 (7 bytes)
2A8648CE3D0201 (1.2.840.10045.2.1 / ecPublicKey) // validate this
06 /* OBJECT IDENTIFIER */ 08 (8 bytes)
2A8648CE3D030107 (1.2.840.10045.3.1.7 / secp256r1) // save this, curveName
04 /* OCTET STREAM (byte[]) */ 6D (0x6D bytes)
// Since the constructed (0x20) bit isn't set in the tag normally we stop here,
// but we know from the ecPublicKey context that this is also DER data.
30 /* SEQUENCE */ 6B (0x6B bytes)
02 /* Integer */ 01 (1 byte) 01 // Integer: 1. // validate this.
04 /* OCTET STREAM (byte[]) */ 20 (0x20 bytes / 256 bits)
70A12C2DB16845ED56FF68CFC21A472B3F04D7D6851BF6349F2D7D5B3452B38A // save this: D
A1 /* CONSTRUCTED CONTEXT SPECIFIC 1 */ 44 (0x44 bytes)
03 /* BIT STRING (byte[] if the first byte is 0x00) */ 66 (0x66 bytes)
00 // Oh, good, it's a normal byte[]. Validate this.
// Formatting will become apparent. Save this.
04
8101ECE47464A6EAD70CF69A6E2BD3D88691A3262D22CBA4F7635EAFF26680A8
D8A12BA61D599235F67D9CB4D58F1783D3CA43E78F0A5ABAA624079936C0C3A9
最后的BIT STRING是“公钥”。由于它以
04
开头(通常会这样,除非发件人对您发狂,除非它发疯了),它表示一个“未压缩点”,这意味着剩下的前半部分是X坐标,其余部分是Y坐标。因此,从这种结构中,您可能会得到类似
string curveOid;
// You can decode the OID, or special case it.
switch (curveName)
{
case "2A8648CE3D030107":
// secp256r1
curveOid = "1.2.840.10045.3.1.7";
break;
case "2B81040022"
// secp384r1
curveOid = "1.3.132.0.34";
break;
case "2B81040023":
// secp521r1
curveOid = "1.3.132.0.35";
break;
default:
throw new InvalidOperationException();
}
return new ECParameters
{
Curve = ECCurve.CreateFromValid(curveOid),
// We saved this.
D = d,
Q = new ECPoint
{
X = x,
Y = y
},
}
这恰好是 Suite B Implementer’s Guide to FIPS 186-3 (ECDSA)的D.1节(NIST P-256/secp256r1)中使用的 key 。
由于EC key 格式非常缺乏INTEGER值(可能需要填充字节),因此您可以为要支持的每个 key 大小构建一个手动提取器。或者,您可以进行实时DER阅读路线。或者,您可以尝试以更友好的形式为您的应用程序序列化私钥。
声明
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。