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

标签 c# .net encryption cryptography elliptic-curve

我正在使用:

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

这是我做的:

  1. 使用 OpenSSL 和在 https://gist.github.com/sidshetye/4759690 的脚本中概述的步骤获得自签名 ECC 证书修改后:

    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证书没有这样的方法。

接下来,我无意中发现了这篇文章 ( Load a Certificate Using X509Certificate2 with ECC Public Key ) 并尝试了以下内容(尽管为什么 ECC 证书公钥被强制转换为 RSA 类型看起来很奇怪):

RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key

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

接下来我无意中发现了这篇文章 ( Importing ECC-based certificate from the Windows Certificate Store into CngKey ),它基本上试图创建 CNGKey 类型并用它实例化 ECDsaCng。然而,即使我可以使用 ECDiffieHellmanCng 做到这一点,它也没有加密方法。

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

最佳答案

###背景

非对称算法具有三种不同的目的(据我所知)

  1. 加密
  • RSA 是唯一可以直接执行此操作的“标准”算法。
  1. 签名
  • 自适应搜索
  • 动态搜索广告
  • ECDSA
  • ElGamal 签名
  1. key 协议(protocol)
  • 迪菲-赫尔曼 (DH)
  • ECDH
  • ElGamal 加密(非对称启动阶段)
  • MQV
  • ECMQV

由于RSA加密是有空间限制的,90年代的计算机很难用,所以RSA加密主要用在“Key Transfer”,也就是说“加密信息”只是DES/的对称加密 key 3DES(AES 尚未发明)- https://www.rfc-editor.org/rfc/rfc2313#section-8 .

key 协议(protocol)(或传输)方案始终必须与协议(protocol)/方案结合才能进行加密操作。这些方案包括

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

所以您可能想要的是 ECIES。

ECIES.Net

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

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

using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
    return ECDiffieHellman.Create(ecdsa.ExportParameters(false));
}

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

让我们继续分割接收者公共(public)对象:

ECDiffieHellmanPublicKey recipientPublic = GetECDHFromCertificate(cert).PublicKey;
ECCurve curve = recipientPublic.ExportParameters().Curve;

现在我们转向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 原语。
  5. 您已经选择了 secp256r1。
  6. 硬编码。完成。
  7. 点压缩很烦人,选择不使用它。
  8. 我省略了 SharedInfo。这可能使我成为一个坏人。
  9. 不使用 XOR,N/A。

###加密

  1. 在右曲线上制作一个临时 key 。

     ECDiffieHellman ephem = ECDiffieHellman.Create(curve);
    
  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);
    
  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 });
    
  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);
     }
    
  8. MAC它

     byte[] d;
    
     using (HMAC hmac = new HMACSHA256(mk))
     {
         d = hmac.ComputeHash(em);
     }
    
  9. 完成

     // Either
     return Tuple.Create(rBar, em, d);
     // Or
     return rBar.Concat(em).Concat(d).ToArray();
    

###解密 作为练习留给读者。

关于c# - 如何在 Windows 上的 .net 框架中使用来自 ECC X509 证书的公钥加密数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47116611/

相关文章:

c# - 数字和斜杠的正则表达式

c# - 快速整数 ABS 函数

c# - WCF 服务引用使用 XmlSerializer 创建临时动态程序集

php - 静态盐与随机盐 - 安全 PHP

c# - 如何在 C# 桌面应用程序中的 DataGridView 中添加空白行

c# - <include> 注释标签是否会抑制智能感知?

c# - 非托管 C++ 加密字符串转换为 C# byte[]

encryption - lua aes加密

c# - Owin.Websocket 超过消息大小导致 CPU 使用率高

.net - C# 数组 : finding custom minimal element