0

I have a problem when I try to decrypt a string generated using RSA in C#. It Encrypts the strings well, but it throws an error when I try to decrypt the string using the private key:

Error occurred while decoding OAEP padding.

I tried changing the fOAEP parameter between true and false, changing the RSACryptoServiceProviders with 2048 as parameter and still doesn't work.

Project is a WPF App which generates 2 files with the keys, keys are loaded by the core.cs file. Then keys are being loaded.

The example I taken only uses the public key to encrypt the string and only the private key to decrypt the string.

Core.cs File

        // 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);
        }
    }
Community
  • 1
  • 1
Zentyk
  • 67
  • 8
  • "Throws an error" is a bit vague. What exception do you get exactly and where in your code? – Klaus Gütter May 12 '20 at 04:39
  • @KlausGütter oh sorry! updated question. – Zentyk May 12 '20 at 04:44
  • Did you search for the error message? Maybe this is of interest for you: https://stackoverflow.com/questions/954416/error-occurred-while-decoding-oaep-padding – Klaus Gütter May 12 '20 at 04:45
  • Does this answer your question? [Error occurred while decoding OAEP padding](https://stackoverflow.com/questions/954416/error-occurred-while-decoding-oaep-padding) – Klaus Gütter May 12 '20 at 04:47

2 Answers2

1

RSA keys are always generated in pairs, a public and a private. These keys are created in the constructor of RSACryptoServiceProvider. Your code calls this constructor in both GeneratePublicKey and GeneratePrivateKey, therefore there will be two unrelated key-pairs generated.

You should be able to check this. When exporting parameters the public component is always included. If they are not identical that that would serve as confirmation.

The fix would be to generate the public and private key at the same time.

JonasH
  • 28,608
  • 2
  • 10
  • 23
1

First thing, WPF and Decryption are two different things. So, we have to concentrate only on the RSA encrypt/decrypt part.

In your code, you are trying to generate public/private keys using different methods. One single rsaprovider should export both parameters. I have provided a sample code for using RSA encryption/decryption. Please check below.

      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;
            }
        }

    }

It has two simple methods and returns output using valuetuple(So add Valutuple nuget package). The file saving and processing are done outside of the RSA logic. Shroud- Encryption, Divulge-Decryption. (Please excuse the naming conventions used, its as per our company standards).

We usually ship our publickey along with our WPF application. For encryption (Shroud), we pass in the private key.. For decryption (Divulge) , we pass in the publich key..

Lingam
  • 609
  • 5
  • 16