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阅读路线。或者,您可以尝试以更友好的形式为您的应用程序序列化私钥。


标签:linux
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号