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

时间:2017-09-01 20:17:23

标签: .net encryption cryptography elliptic-curve

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

  1. 使用椭圆曲线(标识为NIST P-256) 生成短暂(临时)公钥和私钥对
  2. 掌握接收方的公钥(即另一方)的一些方式
  3. 使用方案(比如Elliptic Curve Diffie-Hellman,也称为 ECDH)使用其他方的公钥来获得共享秘密 来自上面第1步的短暂私钥
  4. 使用共享密钥 使用密钥推导函数(KDF)来导出对称密钥(比如说 NIST单步KDF,如下所述 http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf
  5. 最后使用此对称密钥使用AES加密消息。
  6. 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时,它们是相同的)

    问题4:要正确实现NIST单步KDF(调用kdf(Z,OtherInput),其中Z是秘密协议,OtherInput由keydatalen和OtherInfo组成),应该使用具有secretPrepend的DeriveKeyFromHash方法secretAppend bytes signature但只填充secreteAppend bytes?

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

    • 256个无符号字节值(对于keydatalen)
    • len(" AlgorithmIDStuff")16,无符号值字节
    • " AlgorithmIDStuff"在无符号ASCII值bytes =>十进制字节值:65 108 103 111 114 105 116 104 109 73 68 83 116 117 102 102
    • " PartyUInfoStuff"在无符号ASCII值bytes =>十进制字节值:80 97 114 116 121 85 73 110 102 111 83 116 117 102 102
    • " PartyVInfoStuff"在无符号ASCII值bytes =>十进制字节值:80 97 114 116 121 86 73 110 102 111 83 116 117 102 102

1 个答案:

答案 0 :(得分:1)

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

  

Q1:上述程序是否有任何问题?

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

如果你正在使用SP-800-56A,你也会使用稍微不同的KDF,但这是一个很好的KDF。

  

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

流程很好,但有些特定代码并不正确。

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

" GenericPublicBlob"不是任何通用的公共blob"而是来自CNG的一种blob。 GetPublicKey()方法返回公钥字节,这是一个特定于算法的数据blob。对于ECC密钥,它是编码的公共密钥点Q。

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

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)

的方式制作短暂的密钥
  

问题3:使用DeriveKeyMaterial还是DeriveKeyFromHash有区别吗? (注意:当我比较symmetricKey和symmetricKey2时,它们是相同的)

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

在.NET 4.6.2或其附近添加了

DeriveKeyFromHash,以便更清楚地了解正在执行的操作以及操作中涉及的属性(现在的参数)。

我认为没有人应该再次使用DeriveKeyMaterial(相反,使用新方法),但那只是我。

  

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

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

  

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

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