Python 与 C# 的 RSA 加解密数据交互

栏目: Python · 发布时间: 8年前

内容简介:Python 与 C# 的 RSA 加解密数据交互

最近在做一个东西,服务端是用 Python 写的 API,客户端是其他语言写的,比如 C#。客户端与服务端之间有个数据通信的需求是通过 RSA 做加解密。例如客户端 C# 使用公钥加密数据,服务端 Python 使用私钥解密数据。因此需要让不同语言的 RSA 加密后的数据兼容,能够互相解密。

在 C# 中 RSA 的私钥和公钥文件是使用 XML 格式存储的,而 Python 的 pycrypto 是使用 PEM 格式,第一个要解决的问题是密钥文件的一致性问题。使用 openssl 生成的 RSA 密钥文件,C# 无法读取。研究了很久 C# 读取 PEM 的方法,但是没有找到简单高效的方法,最后的妥协方法是使用 C# 生成 RSA 的密钥,然后再转成 PEM 格式。

生成 xml 格式的 RSA 密钥

使用如下代码,用 C# 生成 RSA 的密钥

public void GenerateRSAKey()
{
    using (var rsa = new RSACryptoServiceProvider(2048))
    {
        // 输出私钥
        Console.WriteLine(rsa.ToXmlString(true));
        // 输出公钥
        Console.WriteLine(rsa.ToXmlString(false));
    }
}

将 RSA 由 xml 转成 pem 格式

可以使用 这个在线工具 ,但是在线 工具 毕竟有数据泄露的风险,最后参考了 这篇文章 ,代码在这里 XMLSec2PEM.java

编译代码

javac XMLSec2PEM.java

转换 XML 到 PEM

java XMLSec2PEM your_key.xml

将上述输出的内容保存到 rsa_key.pem。在 Python 中调用会提示 不支持的 RSA Key 格式 ,需要进一步做转换

openssl pkcs8 -topk8 -in rsa_key.pem -nocrypt -out rsa.key

最后得到可用的 rsa.key,使用 openssl 提取出公钥

openssl rsa -in rsa.key -pubout -out rsa_pub.key

不同语言兼容的 RSA 加解密

要做到 C# 和 Python 相互兼容的 RSA 加解密还是有很多细节问题的,例如 C# 默认会做随机的填充,因此每次加密得到的数据都不同,以及 Unicode 和 UTF8的编码问题等。

关于 C# 和 Python 之间相互加解密,可以参考 这篇文章代码在这 。以下代码经过测试,C# 加密的数据,Python 可以正确解密,反之亦然。

Python 版的完整代码

需要先安装依赖

pip install pycrypto
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import
import sys
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5

PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3

if PY3:
    text_type = str
    binary_type = bytes
else:
    text_type = unicode
    binary_type = str


def utf8(value):
    if not isinstance(value, binary_type) and not isinstance(value, text_type):
        value = binary_type(value)
    if isinstance(value, text_type):
        return value.encode('utf-8')
    else:
        return value


_TO_UNICODE_TYPES = (text_type, type(None))


def to_unicode(value):
    if isinstance(value, _TO_UNICODE_TYPES):
        return value
    if not isinstance(value, bytes):
        raise TypeError(
            "Expected bytes, unicode, or None; got %r" % type(value)
        )
    return value.decode("utf-8")


def read_file(file_path):
    with open(file_path, 'r') as f:
        return f.read()


rsa_key_content = read_file('rsa.key')
rsa_pub_key_content = read_file('rsa_pub.key')


class RSAHelper(object):
 @classmethod
    def decrypt(cls, enc_data, key_content=rsa_key_content, passphrase=''):
        rsa = RSA.importKey(key_content, passphrase=passphrase)
        # Generate a cypher using the PKCS1.5 standard
        cipher = PKCS1_v1_5.new(rsa)
        return cipher.decrypt(enc_data, '')

 @classmethod
    def encrypt(cls, plain_data, key_content=rsa_pub_key_content, passphrase=''):
        rsa = RSA.importKey(key_content, passphrase=passphrase)
        # Generate a cypher using the PKCS1.5 standard
        cipher = PKCS1_v1_5.new(rsa)
        return cipher.encrypt(utf8(plain_data))

 @classmethod
    def encrypt_b64(cls, plain_data, key_content=rsa_pub_key_content, passphrase=''):
        data = cls.encrypt(plain_data, key_content, passphrase)
        data = to_unicode(base64.b64encode(utf8(data)))
        return data

 @classmethod
    def decrypt_b64(cls, enc_data, key_content=rsa_key_content, passphrase=''):
        enc_data = base64.b64decode(utf8(enc_data))
        data = cls.decrypt(enc_data, key_content, passphrase)
        data = to_unicode(data)
        return data


if __name__ == '__main__':
    enc = RSAHelper.encrypt_b64('123456中文')
    print(enc)
    plain = RSAHelper.decrypt_b64(enc)
    print(plain)

C# 版的完整代码

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace TestConsoleApplication
{
    public class RSAHelper
    {
        public static void Test()
        {
            try
            {
                var privateKey = ReadKeyFile("rsa.xml");
                var publicKey = ReadKeyFile("rsa_pub.xml");
                var dataToEncrypt = "123456中文";
                var encryptedData = EncryptBase64(dataToEncrypt, publicKey);
                Console.WriteLine("加密后:\n{0}", encryptedData);
                var decryptedData = DecryptBase64(encryptedData, privateKey);
                Console.WriteLine("解密后:\n{0}", decryptedData);
            }
            catch (ArgumentNullException)
            {
                Console.WriteLine("Encryption failed.");
            }

            Console.ReadLine();
        }

        public static string ReadKeyFile(stringfilePath)
        {
            using (var fs = new FileStream(filePath, FileMode.Open))
            {
                var certBytes = new byte[fs.Length];
                fs.Read(certBytes, 0, (int)fs.Length);
                return Encoding.UTF8.GetString(certBytes);
            }
        }

        public static string EncryptBase64(stringdataToEncrypt,stringkeyContent,booldoOAEPPadding =false)
        {
            var byteConverter = new UTF8Encoding();
            var bytesToEncrypt = byteConverter.GetBytes(dataToEncrypt);
            var data = Encrypt(bytesToEncrypt, keyContent, doOAEPPadding);
            if (data == null)
            {
                return null;
            }

            var b64Data = Convert.ToBase64String(data);
            return b64Data;
        }

        public static string DecryptBase64(stringdataToDecrypt,stringkeyContent,booldoOAEPPadding =false)
        {
            var bytesToEncrypt = Convert.FromBase64String(dataToDecrypt);
            var data = Decrypt(bytesToEncrypt, keyContent, doOAEPPadding);
            var byteConverter = new UTF8Encoding();
            return byteConverter.GetString(data);
        }

        public static byte[]Encrypt(byte[] dataToEncrypt,stringkeyContent,booldoOAEPPadding =false)
        {
            try
            {
                byte[] encryptedData;
                using (var rsa = new RSACryptoServiceProvider())
                {
                    rsa.FromXmlString(keyContent);
                    encryptedData = rsa.Encrypt(dataToEncrypt, doOAEPPadding);
                }
                return encryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }

        public static byte[]Decrypt(byte[] dataToDecrypt,stringkeyContent,booldoOAEPPadding =false)
        {
            try
            {
                byte[] decryptedData;
                using (var rsa = new RSACryptoServiceProvider())
                {
                    rsa.FromXmlString(keyContent);
                    decryptedData = rsa.Decrypt(dataToDecrypt, doOAEPPadding);
                }
                return decryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());
                return null;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            RSAHelper.Test();
        }
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Uberland

Uberland

Alex Rosenblat / University of California Press / 2018-11-19 / GBP 21.00

Silicon Valley technology is transforming the way we work, and Uber is leading the charge. An American startup that promised to deliver entrepreneurship for the masses through its technology, Uber ins......一起来看看 《Uberland》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具