如何使用 .net framework 4.7 正确准备 ECC 加密?

How to properly prepare for ECC encryption using .net framework 4.7?

在ECC加密过程中,我(作为发件人)假设我会做以下事情:

  1. 使用椭圆曲线(标识为 NIST P-256)来 生成临时(临时)public 和私钥对
  2. 如何获得public接收方(即对方)的密钥
  3. 使用方案(例如椭圆曲线 Diffie-Hellman,也称为 ECDH) 使用另一方的 public 密钥导出共享秘密,并且 上面第 1 步的临时私钥
  4. 使用共享密钥 使用密钥派生函数 (KDF) 派生对称密钥(比如 NIST 单步 KDF 记录在 http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf)
  5. 最后使用此对称密钥通过 AES 加密消息。

Q1:以上流程有没有问题?

在 .net Framework 4.7 中,这是我的第一次尝试:

        var curve = ECCurve.NamedCurves.nistP256;
        var ecdhSender = ECDiffieHellman.Create(curve);

        X509Certificate2 otherPartyPublicCert = null;   //TODO: Get some how from other party and populate this variable
        byte[] otherPartyPublicKey = otherPartyPublicCert.GetPublicKey();
        ECDiffieHellmanPublicKey otherPartyECDHPublicKey = ECDiffieHellmanCngPublicKey.FromByteArray(otherPartyPublicKey, CngKeyBlobFormat.GenericPublicBlob);

        //The DeriveKeyMaterial seem to generate secret agreement, generate key and throw away the secrete agreement
        var symmetricKey = ecdhSender.DeriveKeyMaterial(otherPartyECDHPublicKey);
        // Or
        //The DeriveKeyFromHash seem to generate same key as above because SHA256 was probably implicit in above call
        var symmetricKey2 = ecdhSender.DeriveKeyFromHash(otherPartyECDHPublicKey, HashAlgorithmName.SHA256);

        //TODO: Perform encryption with above key (either symmetricKey or symmetricKey2, depending on which call we choose) using AES etc.

Q2:上面的代码流程是否正确(包括我在代码中的注释)?

问题 3:使用 DeriveKeyMaterial 或 DeriveKeyFromHash 有区别吗? (注意:我比较symmetricKey和symmetricKey2的时候是一样的)

Q4:要正确实施 NIST 单步 KDF(调用 kdf(Z, OtherInput),其中 Z 是秘密协议,OtherInput 由 keydatalen 和 OtherInfo 组成),应该使用 DeriveKeyFromHash 方法具有 secretPrepend 和 secretAppend 字节签名但仅填充 secreteAppend 字节?

Q5:如果对 Q4 的回答是肯定的,并且如果 OtherInfo 包括说信息 "AlgorithmIDStuff"、"PartyUInfoStuff"、"PartyVInfoStuff"(如果我选择实施连接格式),我是否应该在 secretAppend 参数中使用以下字节序列(即位串)(DeriveKeyFromHash 方法使用的 Z 字节值)之后的值:

如您所知,由于您对它们进行了编号,因此这里有多个问题。理想情况下每个问题有一个问题,但我同意它们是相关的,所以我会尝试一次性回答它们。

Q1: Are there any issues with above procedure?

您所描述的实际上是 ECIES(椭圆曲线 Integrated Encryption Scheme) (http://www.secg.org/sec1-v2.pdf),尽管您缺少计算 MAC 键。如果您不能使用经过身份验证的加密模式,您可能只想遵循 ECIES。

如果您使用的是 SP-800-56A,您还使用了稍微不同的 KDF,但这是一个很好的 KDF。

Q2: Does above code flow seem correct (including my comments in the code)?

流程没问题,但有些特定代码不正确。

byte[] otherPartyPublicKey = otherPartyPublicCert.GetPublicKey();
ECDiffieHellmanPublicKey otherPartyECDHPublicKey =
    ECDiffieHellmanCngPublicKey.FromByteArray(
        otherPartyPublicKey,
        CngKeyBlobFormat.GenericPublicBlob);

"GenericPublicBlob" 不是 "any generic public blob",而是一种来自 CNG 的 blob。 GetPublicKey() 方法 returns public 关键字节,这是特定算法的数据块。对于 ECC 密钥,它是编码的 public 关键点 Q.

除非密钥的发送者讨厌你,否则它将是一个长度为奇数且以0x04开头的数组。如果是这样,您可以通过 .NET 4.7 API:

导入 public 密钥
private static ECDiffieHellmanPublicKey GetECDHPublicKey(X509Certificate2 cert)
{
    byte[] pubKey = cert.GetPublicKey();

    if (pubKey.Length % 2 == 1 && pubKey[0] == 0x04)
    {
        byte[] qx = new byte[pubKey.Length / 2];
        byte[] qy = new byte[qx.Length];
        Buffer.BlockCopy(pubKey, 1, qx, 0, qx.Length);
        Buffer.BlockCopy(pubKey, 1 + qx.Length, qy, 0, qy.Length);

        ECParameters ecParameters = new ECParameters
        {
            Curve = ECCurve.NamedCurves.nistP256,
            Q =
                    {
                        X = qx,
                        Y = qy,
                    }
        };

        using (var otherEcdh = ECDiffieHellman.Create(ecParameters))
        {
            return otherEcdh.PublicKey;
        }
    }

    throw new NotSupportedException();
}            

如果您事先不知道预期曲线是什么,您可以稍微改变一下:

using (ECDsa ecdsa = cert.GetECDsaPublicKey())
using (ECDiffieHellman ecdh = ECDiffieHellman.Create(ecdsa.ExportParameters(false))
{
    // This one takes care of the curve.
    return ecdh.PublicKey;
}

当然,您可以通过 ECDiffieHellman.Create(otherPublic.ExportParameters().Curve)

之类的方式制作您的临时密钥

Q3: Is there a difference in using either DeriveKeyMaterial or DeriveKeyFromHash? (Note: When I compared the symmetricKey and symmetricKey2, they are identical)

DeriveKeyMaterial 来自.NET 3.5 并使用一堆属性来控制它。默认情况下,它使用 SHA-256 进行哈希推导。

DeriveKeyFromHash 是在 .NET 4.6.2 或附近添加的,以便更清楚地说明正在做什么以及操作中涉及哪些属性(现在是参数)。

我认为没有人应该再次使用 DeriveKeyMaterial(而是使用新方法),但我就是这样。

Q4: To properly implement NIST Single-step KDF (which calls for kdf(Z, OtherInput) where Z is the secret agreement, OtherInput consists of keydatalen and OtherInfo), should be using DeriveKeyFromHash method that has secretPrepend and secretAppend bytes signature but only populate secreteAppend bytes?

没有。 OtherInfo 进入 secretAppend,但大端 32 位计数器进入 secretPrepend(参见 5.8.1.1,过程,步骤 3、4、5.1、5.2)。

Q5: If answer is yes to Q4 and if OtherInfo includes say info "AlgorithmIDStuff", "PartyUInfoStuff", "PartyVInfoStuff" (and if I choose to implement The Concatenation Format), should I use following values following byte sequence (i.e. bit string) (Z byte values used by DeriveKeyFromHash method) in secretAppend argument:

没有。 "AlgorithmIDStuff" 不符合 AlgorithmID 值的定义(第 5.8.1.2 节);和 PartyU/PartyV 信息类似。