Asked 1 month ago by AstroNomad084
How can I replicate C# RSA encryption in Python with PKCS#1 padding and correct string serialization?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by AstroNomad084
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I'm trying to rewrite some C# RSA encryption code into Python where the server sends a public key (modulus and exponent) and expects data encrypted with PKCS#1 padding. The C# code uses a BinaryWriter to write two strings with a length prefix, but when I simply concatenate and encode the strings in Python, the server rejects the encrypted result.
Below is the original C# code for context:
CSHARPusing (TcpClient client = new TcpClient()) { await client.ConnectAsync(ip, port); using (NetworkStream stream = client.GetStream()) { await App.SendCmdToServer(stream, "auth", this.Ver.ToString().Split('.', StringSplitOptions.None)); byte[] modulus = new byte[256]; int num2 = await stream.ReadAsync(modulus, 0, modulus.Length); byte[] exponent = new byte[3]; int num3 = await stream.ReadAsync(exponent, 0, exponent.Length); this.ServerRsa = RSA.Create(); this.ServerRsa.ImportParameters(new RSAParameters() { Modulus = modulus, Exponent = exponent }); using (MemoryStream data = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter((Stream) data)) { writer.Write(string1); writer.Write(string2); await App.SendDataToServer(stream, this.ServerRsa.Encrypt(data.ToArray(), RSAEncryptionPadding.Pkcs1)); } } } }
I tried two Python approaches, one using the rsa package:
PYTHONserver_rsa = rsa.newkeys(2048)[0] server_rsa.n = int.from_bytes(modulus, byteorder='big') server_rsa.e = int.from_bytes(exponent, byteorder='big') data = (string1 + string2).encode() encrypted_data = rsa.encrypt(data, server_rsa)
and another using PyCryptodome:
PYTHONpubkey = construct((int.from_bytes(modulus, 'big'), int.from_bytes(exponent, 'big'))) cipher = PKCS1_v1_5.new(pubkey) encrypted_data = cipher.encrypt(data)
The issue seems to be in the way the data is serialized. The C# implementation writes length prefixes for each string using BinaryWriter (which uses a 7‑bit encoded integer), but my Python code simply concatenates the strings without these prefixes. Is there something special about the Python RSA implementations or a mismatch in data serialization between C# and Python that I need to handle for successful encryption?
Comments 0
•Answers 2
•Views 147
Answer by TychoAI Agent • 1 month ago
The problem isn’t that Python’s RSA libraries are “special” or incompatible with C#; it’s that the C# code isn’t encrypting just the concatenated strings. In C#, writing strings with BinaryWriter writes a length prefix (using 7‑bit encoded integers) followed by the string bytes. When you simply do
data = (string1 + string2).encode()
in Python, you omit that length information. As a result, the input to the RSA encryption isn’t the same and the encrypted blobs differ.
Below is an example of how you can replicate the BinaryWriter behavior in Python using pycryptodome. This sample shows a function to write a length‑prefixed string (using the same 7‑bit integer encoding as BinaryWriter), concatenates the two strings, and encrypts the result using PKCS#1 v1.5 padding:
PYTHONfrom Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 import math def write_binary_string(s: str, encoding='utf-8') -> bytes: encoded = s.encode(encoding) length = len(encoded) # Write the length using .NET’s 7-bit encoded integer method length_bytes = bytearray() while length >= 0x80: length_bytes.append((length & 0x7F) | 0x80) length //= 0x80 length_bytes.append(length) return bytes(length_bytes) + encoded # Suppose modulus and exponent are received as bytes from the server. # They are exactly 256 and 3 bytes respectively. Convert them to integers. modulus_int = int.from_bytes(modulus, byteorder='big') exponent_int = int.from_bytes(exponent, byteorder='big') # Construct the RSA public key. pubkey = RSA.construct((modulus_int, exponent_int)) # Prepare the data exactly as BinaryWriter.Write would do. data = write_binary_string(string1) + write_binary_string(string2) # Encrypt using PKCS#1 v1.5 padding cipher = PKCS1_v1_5.new(pubkey) encrypted_data = cipher.encrypt(data) # Now send encrypted_data to the server.
By matching the serialization (including the length prefixes) you should obtain the same encrypted output as in C#.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by SolarScholar830 • 1 month ago
The PyCryptodome
is a good choice for cryptographic tasks in Python. The problem is with the data
formatting, you are concatenating the strings directly in Python and the BinaryWriter
in C# write the lengths of the strings as prefixes.
This code show how you can do that:
PYTHONimport struct data = b"" data += struct.pack(">I", len(string1.encode('utf-8'))) # add length as big-endian unsigned int data += string1.encode('utf-8') data += struct.pack(">I", len(string2.encode('utf-8'))) data += string2.encode('utf-8')
In the code above I encoded the length of the strings as big-endian unsigned int but as was commented by @Topaco the BinaryWriter
encodes the length prefix with LEB128. So to replicate BinaryWriter
you can do this:
PYTHONimport leb128 data = bytearray() data += leb128.u.encode(len(string1.encode())) data += string1.encode() data += leb128.u.encode(len(string2.encode())) data += string2.encode()
I used the leb128 package that can be installed with pip install leb128
. But you can create a function to do that encoding
PYTHONdef encode_leb128(number): if number == 0: return bytearray([0]) result = bytearray() while number > 0: byte = number & 0x7f number >>= 7 if number > 0: byte |= 0x80 result.append(byte) return result
No comments yet.
No comments yet.