C#转Python的RSA PKCS1加密实现求助:服务器返回拒绝
我正在尝试把一段C#代码转成Python,场景是服务器会下发公钥(模数modulus、指数exponent),我需要用PKCS1填充方式加密数据后发给服务器,但Python这边的加密结果总是不被服务器认可,一直返回拒绝。
原C#实现代码
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)); } } } }
我试过的Python代码
使用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)
使用pycryptodome库:
pubkey = construct((int.from_bytes(modulus, 'big'), int.from_bytes(exponent, 'big'))) cipher = PKCS1_v1_5.new(pubkey) encrypted_data = cipher.encrypt(data)
有没有大佬知道是不是Python和C#的RSA实现有什么特殊的不兼容点?
可能的问题排查及解决方法
我来帮你梳理几个最容易踩的坑,大概率是这些地方出了问题:
字符串序列化逻辑不一致(核心问题!)
你在C#里用BinaryWriter.Write(string)写入字符串,这个方法的逻辑是:先写一个4字节小端整数表示字符串UTF-8编码后的字节长度,再写入UTF-8的字节数组。但你Python里直接把两个字符串拼接后编码,完全没模拟这个前缀长度的逻辑,导致加密的原始数据和C#版本完全不一样,服务器解密后自然识别不了。正确的Python写法应该模拟C#的序列化逻辑:
import struct from io import BytesIO def csharp_binary_write_string(stream, s): # 先转UTF-8 utf8_bytes = s.encode('utf-8') # 写入4字节小端整数表示字节长度 stream.write(struct.pack('<i', len(utf8_bytes))) # 写入字符串字节 stream.write(utf8_bytes) # 构造要加密的原始数据 data_stream = BytesIO() csharp_binary_write_string(data_stream, string1) csharp_binary_write_string(data_stream, string2) raw_data = data_stream.getvalue()之后用这个
raw_data去做加密,而不是直接拼接字符串编码。公钥导入的细节问题
用pycryptodome的时候,你代码里的construct应该是RSA.construct,要确保导入正确的模块:from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 # 构造公钥 pubkey = RSA.construct((int.from_bytes(modulus, byteorder='big'), int.from_bytes(exponent, byteorder='big'))) cipher = PKCS1_v1_5.new(pubkey) # 用上面构造的raw_data加密 encrypted_data = cipher.encrypt(raw_data)另外可以验证下读取的指数是否正确:通常RSA公钥的指数是65537(也就是0x010001),你可以打印
int.from_bytes(exponent, 'big')看看是不是这个值,确认公钥有没有完整读取。TCP流读取的完整性问题
C#里的ReadAsync可能不会一次性读满指定的字节数(比如网络延迟导致分包),你在Python里读取modulus和exponent的时候,要确保读满指定长度(256字节modulus、3字节exponent),不能只调用一次read()就完事。比如可以写个辅助函数:def read_exact(stream, length): buffer = b'' while len(buffer) < length: chunk = stream.read(length - len(buffer)) if not chunk: raise Exception("连接断开,未读取到足够数据") buffer += chunk return buffer # 读取公钥 modulus = read_exact(stream, 256) exponent = read_exact(stream, 3)
备注:内容来源于stack exchange,提问作者Tux

