如何在 Windows 上的 .net 框架中使用来自 ECC X509 证书的公钥加密数据?

Rag*_*ghu 2 .net c# encryption cryptography elliptic-curve

我在用:

  • Windows 10(版本 1709,操作系统内部版本 17025.1000)
  • .net 框架 4.7
  • VS 2017(版本:15.3.5)

这是我所做的:

  1. 获得了使用 OpenSSL 的自签名 ECC 证书以及https://gist.github.com/sidshetye/4759690上的脚本中概述的步骤,并进行了修改:

    a) 在 256 位素数字段上使用 NIST/P-256 曲线

    b) 使用 SHA-256

  2. 将文件(在上一步中生成)中的证书加载到 X509Certificate2 对象中

  3. 将 PFX 文件导入 Windows 信任存储(用于测试)。这是成功的。

  4. 导入证书的检查显示公钥字段为“ECC(256 位)”,公钥参数为“ECDSA_P256”。
  5. 接下来试图弄清楚如何使用此证书进行加密。

我被困在最后一步,因为所有使用 X509Certificate2 对象的示例主要仅使用 RSA 而我使用的是 ECC 证书。对于 RSA 证书,X509Certificate2 上有GetRSAPublicKey扩展方法,RSA 类有Encrypt方法。但是,ECC 证书没有这种方法。

接下来,我偶然发现了这篇文章(使用带有 ECC 公钥的 X509Certificate2 加载证书)并尝试遵循以下方法(即使 ECC 证书公钥被强制转换为 RSA 类型的原因看起来很奇怪):

RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key
Run Code Online (Sandbox Code Playgroud)

我遇到以下异常:不支持证书密钥算法。

接下来我偶然发现了这篇文章(将基于 ECC 的证书从 Windows 证书存储导入 CngKey),它基本上试图创建 CNGKey 类型并用它实例化 ECDsaCng。但是,即使我可以用 ECDiffieHellmanCng 做到这一点,也没有 Encrypt 方法。

所以我不确定如何进一步使用 ECC X509 证书的公钥来加密数据。

bar*_*njs 6

背景

非对称算法有三个不同的目的(我知道)

  1. 加密
    • RSA 是唯一可以直接执行此操作的“标准”算法。
  2. 签名
    • RSA
    • 动态安全协议
    • ECDSA
    • ElGamal 签名
  3. 密钥协议
    • 迪菲赫尔曼 (DH)
    • 电子数据中心
    • ElGamal 加密(非对称启动阶段)
    • MQV
    • ECMQV

因为 RSA 加密空间有限,而且在 90 年代对计算机来说很难,所以 RSA 加密的主要用途是“密钥传输”,也就是说“加密消息”只是 DES/3DES (AES) 的对称加密密钥尚未发明)- https://tools.ietf.org/html/rfc2313#section-8

密钥协商(或传输)方案总是必须与协议/方案结合以产生加密操作。此类计划包括

  • TLS(需要 SSL)
  • CMS 或 S/MIME 加密数据
  • IES(集成加密方案)
  • ECIES(椭圆曲线集成加密方案)
  • ElGamal 加密(整体)
  • PGP加密

所以你可能想要的是ECIES。

电子商务网

目前(.NET Framework 4.7.1、.NET Core 2.0)不支持从 .NET 中的证书获取 ECDiffieHellman 对象。

游戏结束了吧?嗯,可能不是。除非携带 ECDH 密钥的证书明确使用 id-ecDH 算法标识符(与更标准的 id-ecc 相比),否则它可以作为 ECDSA 打开。然后,您可以将该对象强制为 ECDH:

using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
    return ECDiffieHellman.Create(ecdsa.ExportParameters(false));
}
Run Code Online (Sandbox Code Playgroud)

(私钥可以做类似的事情,如果密钥是可导出的,否则需要复杂的东西,但你不应该需要它)

让我们继续划分接收者公共对象:

ECDiffieHellmanPublicKey recipientPublic = GetECDHFromCertificate(cert).PublicKey;
ECCurve curve = recipientPublic.ExportParameters().Curve;
Run Code Online (Sandbox Code Playgroud)

所以现在我们转向http://www.secg.org/sec1-v2.pdf第 5.1 节(椭圆曲线集成加密方案)

设置

  1. 选择 ANSI-X9.63-KDF 和 SHA-2-256 作为哈希函数。
  2. 选择 HMAC–SHA-256–256。
  3. 在 CBC 模式下选择 AES–256。
  4. 选择椭圆曲线 Diffie-Hellman Primitive。
  5. 您已经选择了 secp256r1。
  6. 硬编码。完毕。
  7. 点压缩很烦人,选择不使用它。
  8. 我省略了 SharedInfo。这可能使我成为一个坏人。
  9. 不使用异或,不适用。

加密

  1. 在正确的曲线上制作一个临时密钥。

    ECDiffieHellman ephem = ECDiffieHellman.Create(curve);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我们决定不。

    ECParameters ephemPublicParams = ephem.ExportParameters(false);
    int pointLen = ephemPublicParams.Q.X.Length;
    byte[] rBar = new byte[pointLen * 2 + 1];
    rBar[0] = 0x04;
    Buffer.BlockCopy(ephemPublicParams.Q.X, 0, rBar, 1, pointLen);
    Buffer.BlockCopy(ephemPublicParams.Q.Y, 0, rBar, 1 + pointLen, pointLen);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 不能直接这样做,继续。

  4. 不能直接这样做,继续。
  5. 由于我们在这里处于控制之中,因此我们将把 3、4、5 和 6 做为一件事。
  6. KDF时间。

    // This is why we picked AES 256, HMAC-SHA-2-256(-256) and SHA-2-256,
    // the KDF is dead simple.
    byte[] ek = ephem.DeriveKeyFromHash(
        recipientPublic,
        HashAlgorithmName.SHA256,
        null,
        new byte[] { 0, 0, 0, 1 });
    
    byte[] mk = ephem.DeriveKeyFromHash(
        recipientPublic,
        HashAlgorithmName.SHA256,
        null,
        new byte[] { 0, 0, 0, 2 });
    
    Run Code Online (Sandbox Code Playgroud)
  7. 加密东西。

    byte[] em;
    
    // ECIES uses AES with the all zero IV. Since the key is never reused,
    // there's not risk in that.
    using (Aes aes = Aes.Create())
    using (ICryptoTransform encryptor = aes.CreateEncryptor(ek, new byte[16]))
    {
        if (!encryptor.CanTransformMultipleBlocks)
        {
            throw new InvalidOperationException();
        }
    
        em = encryptor.TransformFinalBlock(message, 0, message.Length);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  8. MAC它

    byte[] d;
    
    using (HMAC hmac = new HMACSHA256(mk))
    {
        d = hmac.ComputeHash(em);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  9. 结束

    // Either
    return Tuple.Create(rBar, em, d);
    // Or
    return rBar.Concat(em).Concat(d).ToArray();
    
    Run Code Online (Sandbox Code Playgroud)

解密

留给读者作为练习。