【问题标题】:Import ECC key - CngKey.Import() - Parameter is incorrect导入 ECC 密钥 - CngKey.Import() - 参数不正确
【发布时间】:2017-03-20 23:45:14
【问题描述】:

目的:使用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() 在尝试生成 所需的 CngKey 时抛出“参数不正确”错误Jose.JWT.Encode 函数。不知道我错过了什么步骤。谢谢。

【问题讨论】:

    标签: c# jwt x509certificate ecdsa


    【解决方案1】:

    “ECCPUBLICBLOB”格式与证书中的“公钥”字段不同。

    ECCPUBLICBLOB 的格式在another question's answer 中进行了解释,但这里有一个简短的总结:

    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,这表示有效负载是未压缩的,然后是 32 个字节的X 坐标,然后是 32 字节的 Y 坐标)。

    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() 应该为您做正确的事情(每次调用都会返回一个新实例,因此请妥善管理生命周期)

    【讨论】:

    • 感谢您的回答。它部分接近我正在寻找的东西。 cert.GetECDsaPublicKey() 在 .NET 4.5 中也不可用
    【解决方案2】:

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

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

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

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

    你可以在following post看到我的最终解决方案

    修改公钥需要丢弃第一个字节,留下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
    

    【讨论】:

      猜你喜欢
      • 2015-06-13
      • 1970-01-01
      • 1970-01-01
      • 2020-03-15
      • 2017-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-25
      相关资源
      最近更新 更多