C#/WPF RSA 不解密文本

C# / WPF RSA Doesn't Decrypt Text

当我尝试解密在 C# 中使用 RSA 生成的字符串时遇到问题。它很好地加密了字符串,但是当我尝试使用私钥解密字符串时它会抛出错误:

Error occurred while decoding OAEP padding.

我尝试在 true 和 false 之间更改 fOAEP 参数,将 RSACryptoServiceProviders 更改为 2048 作为参数,但仍然不起作用。

项目是一个 WPF 应用程序,它使用密钥生成 2 个文件,密钥由 core.cs 文件加载。 然后正在加载密钥。

我举的例子只用public密钥加密字符串,只用私钥解密字符串。

Core.cs 文件

        // Keys are generated
        public void GeneratePublicKey(string publicKeyFile) {
            using (var rsa = new RSACryptoServiceProvider(2048)) {
                rsa.PersistKeyInCsp = false;

                if (File.Exists(publicKeyFile))
                    File.Delete(publicKeyFile);

                //and the public key ...
                var pubKey = rsa.ExportParameters(false);

                //converting the public key into a string representation
                string pubKeyString; {
                    //we need some buffer
                    var sw = new System.IO.StringWriter();
                    //we need a serializer
                    var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
                    //serialize the key into the stream
                    xs.Serialize(sw, pubKey);
                    //get the string from the stream
                    pubKeyString = sw.ToString();
                }

                
                File.WriteAllText(publicKeyFile, pubKeyString);
            }
        }

        public void GeneratePrivateKey(string privateKeyFile) {
            using (var rsa = new RSACryptoServiceProvider(2048)) {
                rsa.PersistKeyInCsp = false;

                if (File.Exists(privateKeyFile))
                    File.Delete(privateKeyFile);

                //how to get the private key
                var privKey = rsa.ExportParameters(true);

                //converting the public key into a string representation
                string privKeyString;
                {
                    //we need some buffer
                    var sw = new System.IO.StringWriter();
                    //we need a serializer
                    var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
                    //serialize the key into the stream
                    xs.Serialize(sw, privKey);
                    //get the string from the stream
                    privKeyString = sw.ToString();
                }
                File.WriteAllText(privateKeyFile, privKeyString);
            }
        }

        //Si las llaves ya existen entonces NO Generar, solo leer la llave indicada (leer el texto de la ruta)
        public RSAParameters ReadPublicKey(string publicKeyFile) {
            //Leer
            string pubKeyString = File.ReadAllText(publicKeyFile);
            //Reconvertir
            var sr = new System.IO.StringReader(pubKeyString);
            //we need a deserializer
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
            //get the object back from the stream
            return (RSAParameters)xs.Deserialize(sr);
        }

        public RSAParameters ReadPrivateKey(string privateKeyFile) {
            //Leer
            string privKeyString = File.ReadAllText(privateKeyFile);
            //Reconvertir
            var sr = new System.IO.StringReader(privKeyString);
            //we need a deserializer
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
            //get the object back from the stream
            return (RSAParameters)xs.Deserialize(sr);
        }
        
        //Con la llave publica se encripta el texto
        public string Encrypt(string publicKeyFile, string textToEncrypt) {
            var csp = new RSACryptoServiceProvider();
            csp.ImportParameters(ReadPublicKey(publicKeyFile));

            //for encryption, always handle bytes...
            var bytesPlainTextData = System.Text.Encoding.Unicode.GetBytes(textToEncrypt);

            //apply pkcs#1.5 padding and encrypt our data 
            var bytesCypherText = csp.Encrypt(bytesPlainTextData, true);

            //we might want a string representation of our cypher text... base64 will do
            Debug.WriteLine("Texto Encriptado: "+Convert.ToBase64String(bytesCypherText));

            return Convert.ToBase64String(bytesCypherText);
        }

        /// <summary>
        /// Con la llave Privada se Desencripta
        /// </summary>
        /// <param name="privateKeyFile"></param>
        /// <param name="textToDecrypt"></param>
        /// <returns></returns>
        public string Decrypt(string privateKeyFile, string textToDecrypt) {
            //first, get our bytes back from the base64 string ...
            var bytesCypherText = Convert.FromBase64String(textToDecrypt);

            //we want to decrypt, therefore we need a csp and load our private key
            var csp = new RSACryptoServiceProvider();
            csp.ImportParameters(ReadPrivateKey(privateKeyFile));

            //decrypt and strip pkcs#1.5 padding
            var bytesPlainTextData = csp.Decrypt(bytesCypherText, true);

            Debug.WriteLine("Desencriptado: "+ 
            System.Text.Encoding.Unicode.GetString(bytesPlainTextData));
            //get our original plainText back...
            return System.Text.Encoding.Unicode.GetString(bytesPlainTextData);
        }

MainWindow.cs

 public partial class MainWindow : Window {
        readonly RsaEnc _rs = new RsaEnc();
        private string _publicKeyFile = "./public.cert";
        private string _privateKeyFile = "./private.key";
        
        public MainWindow() {
            InitializeComponent();
        }

        private void GenerateBtn_Click(object sender, RoutedEventArgs e) {
            _rs.GeneratePublicKey(_publicKeyFile);
            _rs.GeneratePrivateKey(_privateKeyFile);
        }

        private void OpenPublic_Click(object sender, RoutedEventArgs e) {
            OpenFileDialog fileDialog = new OpenFileDialog {
                Multiselect = false, Filter = "Public |*.cert", DefaultExt = ".cert"
            };
            bool? dialogOk = fileDialog.ShowDialog();

            if (dialogOk == true) {
                string sFilenames = "";
                foreach (string sFileName in fileDialog.FileNames) {
                    sFilenames += ";" + sFileName;
                }

                sFilenames = sFilenames.Substring(1);
                _publicKeyFile = sFilenames;//Esto solo da la RUTA
                Debug.WriteLine("public Cert: " + _publicKeyFile);
            }
        }

        private void OpenPrivate_Click(object sender, RoutedEventArgs e) {
            OpenFileDialog fileDialog = new OpenFileDialog
            {
                Multiselect = false, Filter = "Certificates |*.key", DefaultExt = ".key"
            };
            bool? dialogOk = fileDialog.ShowDialog();

            if (dialogOk == true) {
                string sFilenames = "";
                foreach (string sFileName in fileDialog.FileNames) {
                    sFilenames += ";" + sFileName;
                }

                sFilenames = sFilenames.Substring(1);
                _privateKeyFile = sFilenames; //Esto solo da la RUTA
                Debug.WriteLine("private Key: " + _privateKeyFile);
            }
        }

        private void EncryptBtn_Click(object sender, RoutedEventArgs e) {
            _rs.Encrypt(_publicKeyFile, BoxToEncrypt.Text);
        }

        private void DecryptBtn_Click(object sender, RoutedEventArgs e) {
            _rs.Decrypt(_privateKeyFile, BoxToDecrypt.Text);
        }
    }

RSA 密钥总是成对生成的,一个 public 和一个私有密钥。这些键是在 RSACryptoServiceProvider 的构造函数中创建的。您的代码在 GeneratePublicKeyGeneratePrivateKey 中调用此构造函数,因此将生成两个不相关的密钥对。

你应该可以检查这个。导出参数时,始终包含 public 组件。如果它们不相同,那将作为确认。

修复方法是同时生成 public 和私钥。

首先,WPF 和解密是两个不同的东西。因此,我们必须只关注 RSA encrypt/decrypt 部分。

在您的代码中,您尝试使用不同的方法生成 public/private 密钥。一个单独的 rsaprovider 应该导出两个参数。我提供了使用 RSA encryption/decryption 的示例代码。请检查以下内容。

      internal sealed class RSA
    {
        public static (string public_key, string private_key) getKeyPair()
        {
            try
            {
                var rsa_provider = new RSACryptoServiceProvider(1024);
                return (rsa_provider.ToXmlString(false), rsa_provider.ToXmlString(true));
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static byte[] shroud_divulge(byte[] input_byte, string _key,bool is_shroud)
        {
            try
            {
                var rsa_provider = new RSACryptoServiceProvider();
                rsa_provider.FromXmlString(_key);
                var padding = RSAEncryptionPadding.OaepSHA256;

                switch(is_shroud)
                {
                    case true:
                        return rsa_provider.Encrypt(input_byte, padding);

                    case false:
                        return rsa_provider.Decrypt(input_byte, padding);
                }
                return null;
            }
            catch (Exception)
            {
                throw;
            }
        }

    }

它有两个简单的方法和 returns 使用 valuetuple 输出(所以添加 Valutuple nuget 包)。 文件保存和处理是在 RSA 逻辑之外完成的。裹尸布 - 加密,泄露 - 解密。 (请原谅使用的命名约定,这是按照我们公司的标准)。

我们通常随 WPF 应用程序一起提供公钥。 为了加密(Shroud),我们传入私钥.. 对于解密(Divulge),我们传入公钥..