C# RSACryptoServiceProvider ToXmlString()/FromXmlString()

user441521 picture user441521 · Jul 18, 2011 · Viewed 15.9k times · Source

EDIT2: Seems Convert.FromBase64String is my savior. Does anyone know if a comma would every be in such a string? I'm doing basic csv parsing. If it is I can always use quotes (but can quotes ever be in a string like this too I guess) but this is just a small project for myself so just easier to parse by splitting by ','.

EDIT: OK so it would seem my issue is when I convert the encrypted bytes to a string, then convert that string back to bytes to be decrypted. How could I do this because I want to actually store the encrypted string somewhere and be able to decrypt that string.

I'm trying to save the public key (and private just for testing) to an xml file so that I can read the same one in later, but getting "Bad data" error when trying to decrypt. Can anyone see what I'm doing wrong?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace EncryptionTest
{
class Program
{
    static void Main(string[] args)
    {
        UnicodeEncoding ByteConverter = new UnicodeEncoding();

        byte[] dataToEncrypt = ByteConverter.GetBytes("Test data");

        WriteRSAInfoToFile();

        string enc = Encrypt(dataToEncrypt);

        enc = Decrypt(enc);
    }

    static void WriteRSAInfoToFile()
    {
        RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
        TextWriter writer = new StreamWriter("C:\\publicKey.xml");
        string publicKey = RSA.ToXmlString(false);
        writer.Write(publicKey);
        writer.Close();

        writer = new StreamWriter("C:\\privateKey.xml");
        string privateKey = RSA.ToXmlString(true);
        writer.Write(privateKey);
        writer.Close();
    }

    static string Encrypt(byte[] data)
    {
        UnicodeEncoding ByteConverter = new UnicodeEncoding();
        RSACryptoServiceProvider encrypt = new RSACryptoServiceProvider();
        TextReader reader = new StreamReader("C:\\publicKey.xml");
        string publicKey = reader.ReadToEnd();
        reader.Close();

        encrypt.FromXmlString(publicKey);

        byte[] encryptedData = encrypt.Encrypt(data, false);

        return ByteConverter.GetString(encryptedData);
    }

    static string Decrypt(string data)
    {
        UnicodeEncoding ByteConverter = new UnicodeEncoding();
        RSACryptoServiceProvider decrypt = new RSACryptoServiceProvider();
        TextReader reader = new StreamReader("C:\\privateKey.xml");
        string privateKey = reader.ReadToEnd();
        reader.Close();

        decrypt.FromXmlString(privateKey);

        byte[] d = ByteConverter.GetBytes(data);
        byte[] decryptedData = decrypt.Decrypt(d, false);       // ERROR: bad data

        return ByteConverter.GetString(decryptedData);
    }
}

}

Answer

Henk Holterman picture Henk Holterman · Jul 18, 2011

This has nothing to do with your public/private key handling (I think that part looks OK, the file I/O can be done easier though).

But your way of transporting the encrypted data as a string is not suitable for "round-trip" . Use a Base64 encoding instead.

You should be able to see this in the debugger: make a note of the length and the first bytes of encryptedData and this should match d in the Decrypt method.