ECDSA 密钥对执行 ECDH 共享秘密加密

问题描述 投票:0回答:2

我需要能够获取 ECDSA 密钥对(私钥和公钥),然后执行 ECDH 以获得两方之间的共享秘密。

在本例中,我们谈论的是爱丽丝和鲍勃。我在任何地方都找不到任何相关的 C# 示例,人们在谈论它时都说这是可能的,但没有提供真正的答案。

因此给出下面的两个密钥对:

string alicePrivateKeyHex = "6105a3237a98d2843a35ac35fb63ba2cfbde4deabc97faa9664f42762103e0de";
string alicePublicKeyHex = "04cadf4b345e6f62a858fb2c25509bace24d81fd600dfb3aa40dcf021902bfc012ba1a8c2364b6323b71ab659891ef856cba546b58c881311fafc75103111a5bdb";

string bobPrivateKeyHex = "7034a8fe220fa73704be34e0fdf82d5671598a19c49f71cec3bce5cee5b30e59";
string bobPublicKeyHex = "04e7526b124f22c8549a2b91783b34a8df067a74df9b601447bc7561a4e7eaea7a652582d1510b59365a7684195f99e4464a6b612441e5a8c2b63664d957384ba9";

我需要能够产生一个共享的秘密。我遇到的问题是,这些从 ECDSA 生成的密钥不遵循 ANS.1 标准,因此大多数 Microsoft 和 Bouncy Castle 库无法使用这些密钥,尽管将它们转换为 Cgn、PEM、Der 等。

我也无法做其他事情,例如 Diffie-Hellman 密钥交换(DHKE),因为我无法安全地交换密钥,因为一切都将通过公共渠道进行,并且在不公开的情况下无法在这些渠道之外进行通信,所以我唯一的选择是通过 ECDH 共享秘密。

我还知道,使用 1 个密钥来实现永久性存在明显的担忧,就好像该密钥一旦泄露,那么有人就可以解密所有消息。这不是本文的目标。我只是想得到一个可行的例子。将来,每条消息都将来自一个新密钥,以更好地保护。

经过多次失败的尝试,甚至尝试让 chatGPT 来帮助我终于创建了自己的解决方案,并决定在这里发布给那些将来需要这个解决方案的人。

我把答案放在下面。

c# encryption blockchain ecdsa ecdh
2个回答
1
投票

下面的代码是如何获取 ECDSA(曲线 - secp256k1)派生密钥对并生成遵循 ECDH 标准的共享密钥。

请注意,这适用于比特币地址、以太坊地址、ReserveBlock 网络以及可能使用 ECDSA 创建数字签名密钥对的任何其他内容。

void Main()
{
    string alicePrivateKeyHex = "6105a3237a98d2843a35ac35fb63ba2cfbde4deabc97faa9664f42762103e0de";
    string alicePublicKeyHex = "04cadf4b345e6f62a858fb2c25509bace24d81fd600dfb3aa40dcf021902bfc012ba1a8c2364b6323b71ab659891ef856cba546b58c881311fafc75103111a5bdb";

    string bobPrivateKeyHex = "7034a8fe220fa73704be34e0fdf82d5671598a19c49f71cec3bce5cee5b30e59";
    string bobPublicKeyHex = "04e7526b124f22c8549a2b91783b34a8df067a74df9b601447bc7561a4e7eaea7a652582d1510b59365a7684195f99e4464a6b612441e5a8c2b63664d957384ba9";

    BigInteger a1 = BigInteger.Parse(alicePrivateKeyHex, NumberStyles.AllowHexSpecifier);//converts hex private key into big int.
    BigInteger a2 = BigInteger.Parse(alicePublicKeyHex, NumberStyles.AllowHexSpecifier);//converts hex private key into big int.
    
    BigInteger b1 = BigInteger.Parse(bobPrivateKeyHex, NumberStyles.AllowHexSpecifier);//converts hex private key into big int.
    BigInteger b2 = BigInteger.Parse(bobPublicKeyHex, NumberStyles.AllowHexSpecifier);//converts hex private key into big int.

    BigInteger P, G, a, b, x, y, ka, kb;

    // Both the persons will be agreed upon the
    // public keys G and P

    P = a2; // A prime number P is taken
    Console.WriteLine("The value of P:" + P);

    G = b2; // A primitive root for P, G is taken
    Console.WriteLine("The value of G:" + G);

    // Alice will choose the private key a
    a = a1; // a is the chosen private key
    Console.WriteLine("\nThe private key a for Alice:"
                      + a);
    x = power(G, a, P); // gets the generated key

    // Bob will choose the private key b
    b = b1; // b is the chosen private key
    Console.WriteLine("The private key b for Bob:" + b);
    y = power(G, b, P); // gets the generated key

    // Generating the secret key after the exchange
    // of keys
    ka = power(y, a, P); // Secret key for Alice
    kb = power(x, b, P); // Secret key for Bob

    Console.WriteLine("\nSecret key for the Alice is:"
                      + ka.ToString("X"));
    Console.WriteLine("Secret key for the Bob is:"
                      + kb.ToString("X"));
                      
    if(ka == kb)
        Console.WriteLine("Shared Secrets Match!");

    string sharedSecretHex = ka.ToString("X").Substring(0,64).Dump(); // Replace with your shared secret in hexadecimal format
    byte[] sharedSecret = HexStringToByteArray(sharedSecretHex);

    string plainText = @"Hello, world!";
    byte[] encryptedData = Encrypt(plainText, sharedSecret);
    string decryptedText = Decrypt(encryptedData, sharedSecret);

    Console.WriteLine("Plain Text: " + plainText);
    Console.WriteLine("Encrypted Data (Hex): " + ByteArrayToHexString(encryptedData));
    Console.WriteLine("Decrypted Text: " + decryptedText);
}
static byte[] Encrypt(string plainText, byte[] key)
{
    using (Aes aes = Aes.Create())
    {
        aes.KeySize = 256;
        aes.Key = key;
        aes.GenerateIV();

        ICryptoTransform encryptor = aes.CreateEncryptor();

        byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
        byte[] encryptedBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);

        byte[] encryptedData = new byte[aes.IV.Length + encryptedBytes.Length];
        Array.Copy(aes.IV, 0, encryptedData, 0, aes.IV.Length);
        Array.Copy(encryptedBytes, 0, encryptedData, aes.IV.Length, encryptedBytes.Length);

        return encryptedData;
    }
}

static string Decrypt(byte[] encryptedData, byte[] key)
{
    using (Aes aes = Aes.Create())
    {
        aes.KeySize = 256;
        aes.Key = key;

        byte[] iv = new byte[aes.BlockSize / 8];
        byte[] encryptedBytes = new byte[encryptedData.Length - iv.Length];

        Array.Copy(encryptedData, 0, iv, 0, iv.Length);
        Array.Copy(encryptedData, iv.Length, encryptedBytes, 0, encryptedBytes.Length);

        aes.IV = iv;

        ICryptoTransform decryptor = aes.CreateDecryptor();

        byte[] decryptedBytes = decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);

        string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
        return decryptedText;
    }
}
static byte[] HexStringToByteArray(string hexString)
{
    int byteCount = hexString.Length / 2;
    byte[] bytes = new byte[byteCount];
    for (int i = 0; i < byteCount; i++)
    {
        bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
    }
    return bytes;
}

static string ByteArrayToHexString(byte[] bytes)
{
    StringBuilder sb = new StringBuilder(bytes.Length * 2);
    foreach (byte b in bytes)
    {
        sb.AppendFormat("{0:x2}", b);
    }
    return sb.ToString();
}

private static BigInteger power(BigInteger a, BigInteger b, BigInteger P)
{
    return BigInteger.ModPow(a, b, P);
}

再次强调,这与安全性无关,显然您应该以符合您要求的方式使用它,例如只使用一次密钥,因此如果有人确实获得了密钥,他们只能解密 1 条消息,不是整个线程,显然不存储诸如共享秘密之类的东西,但总而言之,这就是我所寻找的能够修改和适应我的代码的东西,所以我简化了它,以便其他使用 ECDSA 的人可以适应使用 ECDH 生成共享秘密并加密消息。

另外,作为旁注,这仅使用标准的 Microsoft 库,因此不需要任何额外的。

我把它变成了一个项目/nuget。请随意使用并留下任何评论: https://github.com/ReserveBlockIO/ecdsatoecdh

https://www.nuget.org/packages/ECDSAToECDH/


0
投票

我已尝试使用 ECDSA 示例控制台应用程序来生成 客户端和服务器的密钥对。在这里,我生成了两个用于客户端到服务器数据加密和解密的共享密钥。使用客户端私钥和服务器公钥我生成一个共享秘密并使用服务器私钥和客户端公钥我生成第二个共享秘密。 使用密钥1时,我使用AESGCM加密加密数据,并使用密钥2AESGCM解密解密

数据
*Program.cs*
    
    using GenerateSharedSecret;
    
    namespace GenerateSharedSecretProj
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                try
                {
                    GenerateKeys generateKeys = new GenerateKeys();
                    //server key pair...
                    Console.WriteLine("***Server Key pair***");
                    Console.WriteLine("");
                    Console.WriteLine("Server Public Key : ");
                    var serverPKeys = generateKeys.GeneratePublicPrivateKeys();
                    Console.WriteLine(serverPKeys.PublicKey);
                    Console.WriteLine("");
                    Console.WriteLine("Server Private Key : ");
                    Console.WriteLine(serverPKeys.PrivateKey);
                    Console.WriteLine("");
                    Console.WriteLine("***Client Key pair***");
                    Console.WriteLine("");
                    Console.WriteLine("Client Public Key : ");
                    var clientPKey = generateKeys.GeneratePublicPrivateKeys();
                    Console.WriteLine(clientPKey.PublicKey);
                    Console.WriteLine("");
                    Console.WriteLine("Client Private Key : ");
                    Console.WriteLine(clientPKey.PrivateKey);
                    Console.WriteLine("");
                    Console.WriteLine("***Shared Secret***");
                    Console.WriteLine("");
                    Console.WriteLine("Shared Secret : server pubkey + client secretkey ");
                    var sharedSecret1 = generateKeys.GenerateSharedSecret(clientPKey.PrivateKey, serverPKeys.PublicKey);
                    Console.WriteLine(sharedSecret1);
                    Console.WriteLine("");
                    Console.WriteLine("Shared Secret : client pubkey + server secretkey ");
                    var sharedSecret2 = generateKeys.GenerateSharedSecret(serverPKeys.PrivateKey, clientPKey.PublicKey);
                    Console.WriteLine(sharedSecret2);
                    Console.WriteLine("");
                    Console.WriteLine("***Encrypt Data using shared secret1***");
                    Console.WriteLine("");
                    Console.WriteLine("Enter random data :");
                    string randomData = Console.ReadLine();
                    var encryptedData = generateKeys.EncryptWithAESGCM(randomData, sharedSecret1);
                    Console.WriteLine("Encrypted data :");
                    Console.WriteLine(encryptedData);
                    Console.WriteLine("");
                    Console.WriteLine("***Decrypt Data using shared secret2***");
                    var decryptedData = generateKeys.DecryptWithAESGCM(encryptedData, sharedSecret2);
                    Console.WriteLine(decryptedData);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }

GenerateKeys.cs

using System.Security.Cryptography;

namespace GenerateSharedSecret
{
    public class GenerateKeys
    {
        public GenerateKeys()
        {

        }
        public KeyPair GeneratePublicPrivateKeys()
        {
            ECDsa ecdsa = ECDsa.Create();
            byte[] publicKey = ecdsa.ExportSubjectPublicKeyInfo();
            byte[] privateKey = ecdsa.ExportPkcs8PrivateKey();
            var keyPairObj = new KeyPair()
            {   
                PrivateKey = Convert.ToBase64String(privateKey),
                PublicKey = Convert.ToBase64String(publicKey)
            };

            return keyPairObj;
        }
        public string GenerateSharedSecret(string privateKey, string publicKey)
        {
            try
            {
                ECDiffieHellmanCng ecdh = new ECDiffieHellmanCng();
                ecdh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
                ecdh.HashAlgorithm = CngAlgorithm.Sha256;
                // Import private key
                ecdh.ImportPkcs8PrivateKey(ConvertBase64ToByteArray(privateKey), out _);
                // Derive shared secret as a byte array
                ECDiffieHellmanCng publicKeyProvider = new ECDiffieHellmanCng();
                publicKeyProvider.ImportSubjectPublicKeyInfo(ConvertBase64ToByteArray(publicKey), out _);
                ECDiffieHellmanPublicKey pubKey = publicKeyProvider.PublicKey;
                byte[] sharedSecretBytes = ecdh.DeriveKeyMaterial(pubKey);
                return Convert.ToBase64String(sharedSecretBytes);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error generating shared secret: " + ex.Message);
                return null;
            }
        }

        public string EncryptWithAESGCM(string plainText, string key)
        {
            Aes aesAlg = Aes.Create();
            aesAlg.Mode = CipherMode.CBC; // Use CBC mode for AES
            aesAlg.Padding = PaddingMode.PKCS7; // Use PKCS7 padding

            ICryptoTransform encryptor = aesAlg.CreateEncryptor(ConvertBase64ToByteArray(key), aesAlg.IV);

            using (MemoryStream memoryStream = new MemoryStream())
            {
                memoryStream.Write(aesAlg.IV, 0, aesAlg.IV.Length); // Write IV to the beginning of the stream
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
                {
                    streamWriter.Write(plainText);
                }
                return Convert.ToBase64String(memoryStream.ToArray());
            }
        }
        public string DecryptWithAESGCM(string cipherText, string key)
        {
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
            byte[] keyBytes = Convert.FromBase64String(key);

            Aes aesAlg = Aes.Create();
            aesAlg.Key = keyBytes;
            aesAlg.Mode = CipherMode.CBC; // Use CBC mode for AES
            aesAlg.Padding = PaddingMode.PKCS7; // Use PKCS7 padding

            byte[] iv = new byte[aesAlg.BlockSize / 8]; // IV size based on block size
            Array.Copy(cipherTextBytes, iv, iv.Length); // Extract IV from the beginning of the ciphertext

            aesAlg.IV = iv;

            using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes, iv.Length, cipherTextBytes.Length - iv.Length))
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aesAlg.CreateDecryptor(), CryptoStreamMode.Read))
            using (StreamReader streamReader = new StreamReader(cryptoStream))
            {
                return streamReader.ReadToEnd();
            }
        }

        private static byte[] ConvertBase64ToByteArray(string arrayString)
        {
            byte[] byteArray = Convert.FromBase64String(arrayString);
            return byteArray;
        }
    }

    public class KeyPair
    {
        public string PublicKey { get; set; }
        public string PrivateKey { get; set; }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.