Just trying to rewrite this c# code to python. Server send public key(modulus, exponent), need to encrypt it with pkcs1 padding.
using (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));
}
}
}
}
Everything works fine, except encrypted result by python.
I've tried with rsa
and pycryptodome
, no luck at all, server returns reject.
Tried something like this (rsa
)
server_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)
or this (pycryptodome
)
pubkey = construct((int.from_bytes(modulus, 'big'), int.from_bytes(exponent, 'big')))
cipher = PKCS1_v1_5.new(pubkey)
encrypted_data = cipher.encrypt(data)
Is there some special python RSA implementation, that just not working with C#, or vice versa?
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:
import 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:
import leb128
data = bytearray()
data += leb128.u.encode(len(string1.encode()))
data += string1.encode()
data += leb128.u.encode(len(string2.encode()))
data += string2.encode()
data
I used the leb128 package that can be installed with pip install leb128
. But you can create a function to do that encoding
def encode_to_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