Sending encrypted data via TCP (“Bad Data” exception) - c#

How can i send illegal charecters from tpc client to tcp server.
This is an example of what the encrypted gibberish looks like:
https://i.stack.imgur.com/wfZdm.png
How can i send this pice of gibberish to either my client or server?
This is my encryption & decryption code
public static string Decrypt(string data)
{
byte[] dataToDecrypt = StringToByteArray(data);
byte[] decryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(privateKey);
decryptedData = rsa.Decrypt(dataToDecrypt, false);
}
UnicodeEncoding byteConverter = new UnicodeEncoding();
return ByteArrayToString(decryptedData);
}
public static string Encrypt(string data, string publicKey)
{
UnicodeEncoding byteConverter = new UnicodeEncoding();
byte[] dataToEncrypt = StringToByteArray(data);
byte[] encryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(publicKey);
encryptedData = rsa.Encrypt(dataToEncrypt, false);
}
return ByteArrayToString(encryptedData);
}
public static byte[] StringToByteArray(string data)
{
return Encoding.ASCII.GetBytes(data);
}
public static string ByteArrayToString(byte[] bytes)
{
return Encoding.ASCII.GetString(bytes);
}
I have made it so the client and the server share eachothers public keys but i am getting Exception "Bad data". One more thing if i send encrypted data from a client to the server which data is 128 bytes the server receives only 78 bytes for example

There's a few things wrong with your code:
You shouldn't be using String at all.
String is meant for text, not arbitrary binary data (I assume you got this impression from C or PHP where their string types are really just synonyms for - or thin-wrappers over - a byte-array).
Keep the Byte[] buffer you get from rs.Encrypt and pass that directly to your Socket, TcpClient or NetworkStream that you're using. You'll need to define a binary protocol with length-prefix though.
Encoding.ASCII.GetBytes will convert the UTF-16LE-encoded characters in the String data instance to 7-bit ASCII, it does this by replacing characters with values above 0x7F with '?' - this is not what you want! (and this is what's causing the garbage output on your screen: those "illegal characters" are byte-values above 0x7F that are outside ASCII's 7-bit range. From the documentation:
It uses replacement fallback to replace each string that it cannot encode and each byte that it cannot decode with a question mark ("?") character.
If you really do want to transmit data over the network using human-readable text then use Base64 encoding: Convert.ToBase64String( Byte[] buffer ) and convert it back using Convert.FromBase64String( String s ) at the receiving end - but you'll still need to length-prefix or delimit your data.

Related

RSA Decryption C# The data to be decrypted exceeds the maximum for this modulus of 256 bytes

I was trying to decrypt the encrypted data which was in string format, because of this i have to execute the Encoding function 2 times which leads me to this error The data to be decrypted exceeds the maximum for this modulus of 256 bytes. However if i pass in the encrypted data in bytes format, the decryption works.
Here is the issue, i want user to copy and paste the encrypted data into a Textbox(string format) and decrypt using the information from the TextBox.
Please advice me on how to fix this problem.
Here is the example of my concept, the first Textbox is where user input the encrypted data
Here is my code
public static byte[] RSADecrypt2(byte[] ciphertext, string srcKey)
{
byte[] decryptedData;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(srcKey);
decryptedData = rsa.Decrypt(ciphertext, true);
rsa.Dispose();
return decryptedData;
}
private void RSADecrypt_Click(object sender, EventArgs e)
{
byte[] encryption = Encoding.Unicode.GetBytes(passBox.Text);
string privateKey = rsaBoxPrivate.Text;
byte[] decryption = RSADecrypt2(encryption, privateKey);
decryptedBox.Text = Encoding.Unicode.GetString(decryption);
}

Client-server communication and encryption/decryption

I am implementing a website in C# and I have a client server communication that I need to carry out. Client would send
(POST) encrypted data and server would read the data and response with encrypted response. The encryption that we are using is AES-128 ECB cipher.
The device is sending encrypted data in following format (this is a piece of data):
3721 b1cc 1759 3067 f993 7c3d bda9 4f04 547c ea1b 3974 2bd1 f213 74a5
2036 7927 e679 bab5 7bbc 3fbf 6a30 85e9 38a8 b877 594a d329 74bb 5d24
2088 738a 9978 cecc
I am not able to receive it in the .aspx page in the page_load method.
Can any one tell me what kind of data this is ?
Also, how do I convert a plain text to this kind of data ?
I already have a decryption and encryption function.
When I use the above data in my decryption function after reading it from a file into a byte array, I am able to get the plan text. I have to use Encoding.UTF8.ToString() function to get the plain text from the byte array output by the decryption function to get the plain text.
But when I convert the response data to byte array and send it byte by byte to the Response.write(), I still get the original encrypted string, and not the raw binary data that I have shown above.
Following are the functions for encryption and decryption :
public string decryptMessage(byte[] data)
{
byte[] decryptedBytes = Decrypt(data, key);
return Encoding.UTF8.GetString(decryptedBytes);
}
public byte[] encryptMessage(string plainText)
{
byte[] encryptedBytes = Encrypt(Encoding.UTF8.GetBytes(plainText), key);
return encryptedBytes;
}
public static byte[] Encrypt(byte[] data, byte[] key)
{
using (AesCryptoServiceProvider csp = new AesCryptoServiceProvider())
{
csp.KeySize = 128;
csp.BlockSize = 128;
csp.Key = key;
csp.Padding = PaddingMode.None;
csp.Mode = CipherMode.ECB;
ICryptoTransform encrypter = csp.CreateEncryptor();
return encrypter.TransformFinalBlock(data, 0, data.Length);
}
}
private static byte[] Decrypt(byte[] data, byte[] key)
{
using (AesCryptoServiceProvider csp = new AesCryptoServiceProvider())
{
csp.KeySize = 128;
csp.BlockSize = 128;
csp.Key = key;
csp.Padding = PaddingMode.None;
csp.Mode = CipherMode.ECB;
ICryptoTransform decrypter = csp.CreateDecryptor();
return decrypter.TransformFinalBlock(data, 0, data.Length);
}
}
For example, the plan text would be "Hello, how are you?" and the encrypted text would be "Cxzx3miStNMIjP9zG1xlCme". I need to convert this encrypted text to the raw binary data like the one sample shown above and send it back with Response.Write().
I have tried using Response.BinaryWrite() but it doesn't send back the raw data that I need when i invoke test the page in the browser. It just gives me "Cxzx3miStNMIjP9zG1xlCme" or some other weird characters.

How to decrypt RSA encrypted string in c# while it was encrypted in GO language. Error occurred while decoding OAEP padding

I have an application running on c# and another server application in go. I need to implement secure communication using rsa.
What i'am doing is I've initialized the RSA provider in my C# application and generated public key to extract Modulus and Exponent. Then concatenated the modulus (Hexadecimal) and exponent (int) and converted this string to Base64 string, send it to a GO endpoint.
Here is C# code snippet
public string ConvertToPublicKey()
{
CspParameters rsaParameters = GetCspParameters();
RSACryptoServiceProvider provider = newRSACryptoServiceProvider(rsaParameters);
string paramsXml = RsaProvider.ToXmlString(false);
XDocument xDocument = XDocument.Parse(paramsXml);
string modulus = xDocument.Descendants().FirstOrDefault(x => x.Name == "Modulus")?.Value ?? string.Empty;
string exponent = xDocument.Descendants().FirstOrDefault(x => x.Name == "Exponent")?.Value ?? string.Empty;
byte[] base64BytesOfModulus = Convert.FromBase64String(modulus);
string hexaDecimalofModulus = BitConverter.ToString(base64BytesOfModulus).Replace("-", string.Empty);
byte[] base64BytesOfExponent = Convert.FromBase64String(exponent);
string hexadecimalOfExponent = BitConverter.ToString(base64BytesOfExponent).Replace("-", string.Empty);
int intOfExponent = Convert.ToInt32(hexadecimalOfExponent, 16);
byte[] publicKey = Encoding.UTF8.GetBytes($"{hexaDecimalofModulus};{intOfExponent}");
return Convert.ToBase64String(publicKey);
}
private static CspParameters GetCspParameters()
{
const string containerName = "KeyContainer";
return new CspParameters
{
KeyContainerName = containerName,
Flags = CspProviderFlags.UseMachineKeyStore
};
}
At Go endpoint I received public key and modulus correctly. Then I encrypted the message using public key and sent it back to c# application in response after converting encrypted message byte[] to base64.
Here is GO code snippet
func GetLicenseInfo(responseWriter http.ResponseWriter,request*http.Request)
{
encryptionKey := request.Header.Get("Authorization")
var decodedStringBytes, errors = b64.StdEncoding.DecodeString(encryptionKey)
if errors == nil {
var decodedString = string(decodedStringBytes)
result := strings.Split(decodedString, ";")
modulus := new(big.Int)
modulus.SetString(result[0], 16)
exponent, exponentErrors := strconv.Atoi(result[1])
if exponentErrors == nil {
var someInfo = utils.GetInfo()
var InfoInJson = ToJson(someInfo)
publicKey := &rsa.PublicKey{N: modulus, E: exponent}
var encryptedMessage, err = rsa.EncryptOAEP(sha256.New(),rand.Reader, publicKey,[]byte(InfoInJson), []byte(""))
var response = b64.StdEncoding.EncodeToString(encryptedMessage)
if err == nil {
json.NewEncoder(responseWriter).Encode(response)
}
}
}
}
func ToJson(model InfoModel) string {
InfoInJson, errors := json.Marshal(model)
if errors != nil {
panic("An error occurred while serializing the response")
}
return string(InfoInJson)
}
When i received the response back in Base64 string i converted it to Byte[] and tried decrypting that with same instance of RSACryptoServiceProvider then it throws the following exception
Error occurred while decoding OAEP padding.
any help?
UPDATE
for example here is the base64 string that i received in GO
QUQ2NDlFRTlCQTA3Q0IxNEI1MTNDMzczQzBBMjNBOEQyMDI5MkVGQTBFMjgyNUIyMEEyMzM1MEE3OTUyNjgyQ0Y3MEFBQjJBMTZGMzQyNTM4MkU2RDZBRjU5M0IxRTI2MTE0OEIyQkFFRTY3MUVDMTQ1NDk1NjBDRkNEQUNCQzI3RUUxNDRFODZDQUI4RDBDOUY2OENBNTUwNUMxQjZGQkVBQjQ0MTlBMjg3RDhBRjgxRDUyREY3MEM0RDZDQTA5MkREMzk5Q0NEODU5Q0FGQzAzQ0JEQ0JBQzgwOTg3NDY0NThBMkY4NEREOTc1QjU5QTJBMUNBNzQxQTBDNkQ2RDs2NTUzNw==
and here is what my GO app sent back
QuWpWdEPSJR+l9UJTkh+heJJ/NpPwhz/hVVu1VdKYdz37YGWWdKTj7Fc5lZ3A8p1WjtC4F+yieZCz0tEatCqTpRmm9g6Oioyjbtr9qGTxO/PE+GA33YyBe6nmMRe674SPePx/fg6l3nnfSZ4/+iLCV4bNgyNqFHCaXc7H4Snms8=
UPDATE 2
I've updated the code snippet and included the data types and here is the part that dscrypts the content received from GO end point
public byte[] Decrypt(byte[] encryptedData, RSAParameters rsaParameters)
{
RsaProvider.ImportParameters(rsaParameters);
return RsaProvider.Decrypt(encryptedData, true);
}
i receive a base64 string then i convert to byte[] using this
byte[] b = Convert.FromBase64String(responseString); byte[] decryptedBytes=crypto.Decrypt(b, crypto.RsaProvider.ExportParameters(false));
crypto is the instance of the class that contains decryption logic,instacne of RSACryptoServiceProvider and that method(ConvertToPublicKey) given above returning the public key
In go you have
var encryptedMessage, err = rsa.EncryptOAEP(sha256.New(), ...
Which I'll go ahead and assume is OAEP with SHA-2-256.
In C# you have
return RsaProvider.Decrypt(encryptedData, true);
Which is OAEP with SHA-1.
You need to abandon RSACryptoServiceProvider. If you switch to RSACng you can then decrypt it with
using (RSA rsa = new RSACng())
{
rsa.ImportParameters(rsaParameters);
return rsa.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA256);
}
And while I'm here:
In ConvertToPublicKey you export the XML, parse the XML, and turn it into byte arrays. Why not just call ExportParameters(false) and extract the Modulus and Exponent byte arrays directly?
After addressing comments by #bartonjs I've also changed response response from go to
var encryptedMessage, err = rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, []byte(licenseInformationJson), []byte(""))
if err == nil {
responseWriter.Write([]byte(encryptedMessage))
}
Notice []byte(encryptedMessage) byte stream sent from go to c# is slightly changed because encryptedMessage is []unint8. so you need to typecast encryptedMessage to []byte so that values are correctly mapped in c#.

The server can not receive encrypted text by RSA in client/server application

I am using RSA to encrypted text and then send the encrypted text to a server. My problem is the server is unable to decrypt the text because the server can not read encrypted text correctly.
My code in the client is :
byte[] plaintext;
byte[] encryptedtext=new byte[128];
plaintext = ByteConverter.GetBytes("hello");
encryptedtext = Encryption(plaintext, RSAA.ExportParameters(false), false);
textBox10.Text = ByteConverter.GetString(encryptedtext);
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(textBox10.Text);
networkStream.Write(bytesToSend, 0, bytesToSend.Length);
my code in server :
byte[] buffer = new byte[handlerSocket.ReceiveBufferSize];
dataReceived = Encoding.ASCII.GetString(buffer, 0, buffer.Length);
byte[] encryptedtext=new byte[128] ;
// dataReceived = ByteConverter.GetBytes(dataReceived);
encryptedtext = ByteConverter.GetBytes(dataReceived);
byte[] decryptedtex = Decryption(encryptedtext, RSA.ExportParameters(true), false);
if(decryptedtex!=null)
textBox4.Text = ByteConverter.GetString(decryptedtex);
output :
In client, the encrypted text :
卅凉䂧즠淦燺◓㎜ზᄋ큺�㬧ᶥ숿둴ꖉ␽넵Տ懾쵌摘䙑원퀮歒�숈鬖姈㫑餓貜Dž手葏㻐㺿⣨ꇑ橿朁繱�䘧殍㲙㦱䟽躮Ồᒛ鈉嫡짟
but when the encrypted text is received by the server, it looks like:
?????????????????????????????????????????????????????????
and the server can not decrypt the text.
NOTE : the server has the correct information to decrypt the text (public key, private key ....)
So how I can solve this problem ?
You can't just convert binary data to an ASCII string and hope that all the character encodings match up. It just doesn't work like that.
Send the encrypted data as a byte[]. E.g.:
networkStream.Write(encryptedtext, 0, encryptedtext.Length);
You'll need to make the same change on the server side.
If you really want to send the data as a string (which I don't understand why you would want to), you can convert to base64 encoding instead of ASCII, which will be preserved properly:
string b64Str = Convert.ToBase64String(encryptedtext);
Also, using encryptedtext as your variable name for a byte[] is super misleading. You should change that. You should also not be using RSA to send payloads and instead just send a symmetric key. But if we were going to talk about that, you should really just be using TLS over an SslStream.

RSA Encryption and Decryption in C#.NET

I have below code to encrypt and decrypt the message in c#. when i am trying to run it is giving an exception ie "The data to be decrypted exceeds the maximum for this modulus of 256 bytes"
public static void Main(string[] args)
{
X509Certificate2 cert = new X509Certificate2(#"C:\Data\ABC-rsa-public-key-certificate.cer");
string encryptedText = EncrypIt("Hello", cert);
string decryptedText = DecrptIt(encryptedText, cert);
System.Console.WriteLine(decryptedText);
}
public static string EncrypIt(string inputString, X509Certificate2 cert)
{
RSACryptoServiceProvider publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] plainBytes = Encoding.UTF8.GetBytes(inputString);
byte[] encryptedBytes = publicKey.Encrypt(plainBytes, false);
string encryptedText = Encoding.UTF8.GetString(encryptedBytes);
return encryptedText;
}
public static string DecrptIt(string encryptedText, X509Certificate2 cert)
{
RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] encryptedBytes = Encoding.UTF8.GetBytes(encryptedText);
byte[] decryptedBytes = privateKey.Decrypt(encryptedBytes, false);
string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
return decryptedText;
}
Several problems:
RSA by default only encrypts one block. It's not suitable for long messages. You shouldn't encrypt the message itself with RSA. Generate a random AES key and encrypt the key with RSA and the actual message with AES.
You must use a binary safe encoding such as Hex or Base64 for the ciphertext. Using UTF-8 corrupts the data since it doesn't allow arbitrary byte sequences.
UTF-8 is designed to encode text, so it's fine for your plaintext.
Use OAEP, the old 1.5 padding mode is not secure. i.e. pass true as second parameter to Encrypt/Decrypt. (Technically it's possible to use it securely, but it's tricky and I wouldn't recommend it)
As a further note, once you use AES, there are some more pitfalls: 1) Use a MAC in an encrypt-then-mac scheme, else active attacks including padding-oracles will break your code 2) Use a random IV that's different for each message
RSA should not be used to encrypt this kind of data. You should be encrypting your data with a symmetric key like AES, then encrypting the symmetric key with RSA.

Categories