跨平台 C# Crypto:无法生成正确的 key/Choose 正确的算法

Cross-Platform C# Crypto: Cannot generate right key/Choose right algorithm

我正在创建一个加密 class 以在我的聊天客户端(Windows、.NET Framework)和服务器(Linux、.NET Core)中使用。

我想我会使用 BouncyCastle,因为它“有据可查”,而且我需要默认库不支持的跨平台 (CNG classes)。 所以我得到了密钥生成工作(还没有测试它的跨平台),但由于无效的密钥大小,加密和解密不起作用。

我找不到这方面的任何文档,已经被困在这里一段时间了。 请指出我是否做错了什么

(我对加密很陌生,C#...一般来说编程很好,已经学习了 1 年)

我真的希望我的代码不会一团糟:

using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using System;
using System.IO;
using System.Text;

namespace ChatClient
{
    public class Crypto
    {
        private bool _ready = false;
        private X9ECParameters m_x9EC;

        private ECPublicKeyParameters m_myPubKey;
        private AsymmetricKeyParameter m_myPrivKey;

        private byte[] m_sharedSecret = null;

        public Crypto()
        {
            // Get curve
            m_x9EC = NistNamedCurves.GetByName("P-521");
            ECDomainParameters ecDomain = new ECDomainParameters(m_x9EC.Curve, m_x9EC.G, m_x9EC.N, m_x9EC.H, m_x9EC.GetSeed());

            // Create generator
            ECKeyPairGenerator g = (ECKeyPairGenerator)GeneratorUtilities.GetKeyPairGenerator("ECDH");
            g.Init(new ECKeyGenerationParameters(ecDomain, new SecureRandom()));

            // Generate keypair
            AsymmetricCipherKeyPair keyPair = g.GenerateKeyPair();

            // Set keys
            m_myPubKey = (ECPublicKeyParameters)keyPair.Public;
            m_myPrivKey = keyPair.Private;
        }

        public void GenPrivateKey(byte[] key)
        {
            // Why whould anyone even...
            if (key == null)
                throw new ArgumentNullException();

            // Split up the Base64-Encoded, comma-seperated crypto-coords of the server/client
            string str = Encoding.UTF8.GetString(key);
            string[] elements = str.Split(',');
            if (elements.Length != 2)
                throw new ArgumentException();

            // Generate a key out of the coordinates
            ECPoint point = m_x9EC.Curve.CreatePoint(
                new BigInteger(Convert.FromBase64String(elements[0])),
                new BigInteger(Convert.FromBase64String(elements[1]))
                );

            // Get public key
            ECPublicKeyParameters remotePubKey = new ECPublicKeyParameters("ECDH", point, SecObjectIdentifiers.SecP521r1);

            // Generate shared secret key
            IBasicAgreement aKeyAgree = AgreementUtilities.GetBasicAgreement("ECDH");
            aKeyAgree.Init(m_myPrivKey);
            m_sharedSecret = aKeyAgree.CalculateAgreement(remotePubKey).ToByteArray();

            // Debugging...
            Console.WriteLine("Key: {0}", Convert.ToBase64String(m_sharedSecret));

            // Authentication is done, class can now be used for encryption/decryption
            _ready = true;
        }

        public byte[] GetPublicKey()
        {
            // Assemble the Base64-Encoded, comma-seperated crypto-coords to send to the other client/server
            string str = string.Format(
                "{0},{1}",
                Convert.ToBase64String(m_myPubKey.Q.AffineXCoord.ToBigInteger().ToByteArray()),
                Convert.ToBase64String(m_myPubKey.Q.AffineYCoord.ToBigInteger().ToByteArray())
                );
            
            // Return it
            return Encoding.UTF8.GetBytes(str);
        }

        public byte[] Encrypt(byte[] unencryptedData)
        {
            // Keys need to be generated before we can start encrypting/decrypring, And please dont pass null into here...
            if (!_ready || unencryptedData == null)
                return null;

            using (MemoryStream ms = new MemoryStream())
            {
                using (System.Security.Cryptography.AesManaged cryptor = new System.Security.Cryptography.AesManaged())
                {
                    // Set parameters
                    cryptor.Mode = System.Security.Cryptography.CipherMode.CBC;
                    cryptor.Padding = System.Security.Cryptography.PaddingMode.PKCS7;
                    cryptor.KeySize = 128;
                    cryptor.BlockSize = 128;

                    // Get iv
                    byte[] iv = cryptor.IV;

                    // Encrypt the data
                    using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(ms, cryptor.CreateEncryptor(m_sharedSecret, iv), System.Security.Cryptography.CryptoStreamMode.Write))
                        cs.Write(unencryptedData, 0, unencryptedData.Length);
                    
                    // Get stuff that was encrpyted
                    byte[] encryptedContent = ms.ToArray();

                    // Create a new array for the data + iv
                    byte[] result = new byte[iv.Length + encryptedContent.Length];

                    //copy both arrays into one
                    System.Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
                    System.Buffer.BlockCopy(encryptedContent, 0, result, iv.Length, encryptedContent.Length);

                    // Aaaand return it
                    return result;
                }
            }
            
            return null;
        }

        public byte[] Decrypt(byte[] encryptedData)
        {
            // Keys need to be generated before we can start encrypting/decrypring, And please dont pass null into here...
            if (!_ready || encryptedData == null)
                return null;

            // New arrays for iv and data
            byte[] iv = new byte[16];
            byte[] dat = new byte[encryptedData.Length - iv.Length];

            // Get iv and data
            System.Buffer.BlockCopy(encryptedData, 0, iv, 0, iv.Length);
            System.Buffer.BlockCopy(encryptedData, iv.Length, dat, 0, dat.Length);

            using (MemoryStream ms = new MemoryStream())
            {
                using (System.Security.Cryptography.AesManaged cryptor = new System.Security.Cryptography.AesManaged())
                {
                    // Set parameters
                    cryptor.Mode = System.Security.Cryptography.CipherMode.CBC;
                    cryptor.Padding = System.Security.Cryptography.PaddingMode.PKCS7;
                    cryptor.KeySize = 128;
                    cryptor.BlockSize = 128;

                    // Decrypt the data
                    using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(ms, cryptor.CreateDecryptor(m_sharedSecret, iv), System.Security.Cryptography.CryptoStreamMode.Write))
                        cs.Write(encryptedData, 0, encryptedData.Length);
                    
                    // Aaaand return it
                    return ms.ToArray();
                }
            }
            
            return null;
        }
    }
}

这部分在哪里:

// Encrypt the data
using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(ms, cryptor.CreateEncryptor(m_sharedSecret, iv), System.Security.Cryptography.CryptoStreamMode.Write))

引发此错误:

Exception thrown: 'System.ArgumentException' in System.Core.dll
An unhandled exception of type 'System.ArgumentException' occurred in System.Core.dll
The specified key is not a valid size for this algorithm.

关闭这个问题,Topaco 在评论中回答了这个问题

In the code the shared secret is used directly as symmetric key, which causes the error due to the different sizes. Instead, the symmetric key (with the appropriate size) is usually derived from the shared secret, here and here.