Search code examples
pythonc#rsa

C# to Python RSA implement


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?


Solution

  • 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