威势网络,为您的企业和团队注入互联网活力!
服务热线:138-9741-0341

[原创] C# .NetCore 跨平台RSA加密实现

发布日期:2022/11/10 作者: 浏览:896

    注意,文中提到的代码由于时间原因,没有经过严格跨平台测试,只是编译通过,在本地windows平台下测试正常。其它平台大家自行测试,如有时间,本人后面测试完后再来更新此文。

    相信很多朋友.NetCore下使用RSA时,如下图所示,都是用的这个第三方NuGet包

    这个包用起来确实很方便,它可以很方便的生成密钥对供我们调用,也可以很方便的实现RSA加密和解密。网上搜索到的结果基本上都是它的。本以为我也可以这样风平浪静心安理得的一直使用,但是直到有一天,我把它引入了一个项目,发现它上面有个黄色的警告。提示更新,但明明已经是最新版本了,这让原本就九年义务教育不合格的我束手无策。。。项目发布以后也莫明其妙的报错,网上查询的结果是.NET版本不匹配之类的,怀疑和它有关,于是决定替换了它。本以为这是一件很简单的事情,没想到实现起来还是浪费了不少时间。

    本来以为网上有很多现成的轮子拿来直接用就OK了,没想到搜索到的几乎全是BouncyCastle.NetCore的, 国外技术论坛上面也搜索到了一些.NET自带的System.Security.Cryptography的相关结果,但都是只言片语,看的人云里雾里,头昏奶涨的,不知其所以然。当翻看了MS官方的文档后看到MS这样介绍,为了跨平台,

You should avoid using RSACryptoServiceProvider as it is tightly bound to the Windows platform. Instead, we'll be using the RSA base class, which will return a platform-specific RSA implementation

  要尽量用基类开发,以更好的为平台解耦。网上搜索的好多代码都是以前ASP.NET框架下的,如果移植到。netcore下过来,WINDOWS平台下就算没问题,其它平台下也会报错,阅读了一些老外的讨论,再加上MS官网说明,大概了解到,能用 System.Security.Cryptography.RSA.Create() 的时候,尽量用RSA.Create(),而 new RSACryptoServiceProvider(2048) 方法能不用就不用。基于以上需求,简单写一个类,实现以下功能:

 public class RSAService
    {
        private RSA rsa = RSA.Create();
        public RSAService() { }
        public RSAService(string PubKey_Base64String,string PriKey_Base64String)

      //......省略若干行
 }

初始化构造函数RSAService,用公钥私钥初始化RSA对象

public (string, string) CreateKeyPair   //向调用者返回一对密钥对

public string PriKey()//返回成员rsa的私钥

public string PubKey()  //返回公钥

public string Encrypt(string plainText) //对象对加密明文

public string Decrypt(string cipherText)  //对象内解密密文

public  string Decrypt(string privateKey, string cipherText) //解密对象外密文,即,传递一条密钥和一条密文,由密钥解出密文

   因为我的需求很简单,就是前端加密,后端能解密就可以了,所以代码也是相对简单。但是在调试的过程中其实还是浪费了不少周折。因为前台是用JS加密,用AJAX发送的,JS用/js/jsencrypt.js 这一个好像就够了。因为RSA算法所限,加密密文不可以超过117个(1024位时),所以长的明文要分段加密才可以。另外,.NETCORE生成的密钥也分很多类型,什么PEM之类的,还有PKCS1\PKCS8之类的,PKCS8又分带密码的,不带密码的,拼写也相近,眼神不好,一看走眼就浪费半天时间,大家调试的时候注意一点,这里值得强调的一点就是,.NETCORE生成的PKCS1的密钥对,返回前台JS调用的时候,加密直接返回FALSE,为此浪费了我不少时间,开始以为是自己COPY密钥的时候是因为格式转换引起的,所以一顿查找,浪费不少时间,后来才发现,是PKCS1的密钥不受JS支持,所以我们返回的时候要向前台返回PKCS8格式的。废话不多说,以下是代码:

此处内容仅对会员开放,非会员用户打赏后才可以查看
相关的JS分段加密代码如下:


<script>
    function EncryptPost(data,url){
        var returnData;
        $.ajax({
                beforeSend: function (xhr) {
                    if($("#inbox").val().length>113)
                    {
                    }
                },
                type: "get",
                async: false,
                url: '/api/cer/GetPublicKey',
                success: function (_data) {
                    var cid=_data.cid;
                    var encrypt = new JSEncrypt();
                    encrypt.setPublicKey('-----BEGIN PUBLIC KEY-----' + _data.spbKey + '-----END PUBLIC KEY-----');
                    var encrypted = encrypt.encrypt(JSON.stringify(data));
                    $.ajax({
                        url:url,
                        type: "post",
                        data:JSON.stringify({"cid":cid, "json": Base64.encode(encrypted) }),
                        dataType: "json",
                        contentType:"application/json",
                        async: false,
                        success: function (data) {
                            returnData= data;
                        }
                    });
                },
                error: function (msg) {
                    alert('遇到网络错误');
                }
            });
        return returnData;
    }
    function mod(n, m) {
        return ((n % m) + m) % m;
    }
    function splitencrypt(str,spbKey)
    {
        if(str=="") return "";
        if(str.length>117)//1024位密钥要改成117
        {

            var strArr = [];
            var n = 117;
            var encrypt = new JSEncrypt();
            encrypt.setPublicKey('-----BEGIN PUBLIC KEY-----' + spbKey + '-----END PUBLIC KEY-----');
            for (var i = 0, l = str.length; i < l/n; i++) {
                var a = str.slice(n*i, n*(i+1));
                strArr.push(a);
                if(i==0)
                {
                    //c= encrypt.encrypt(JSON.stringify(a));
                    c= encrypt.encrypt(a);
                }
                else
                {
                    //c=c+"|"+encrypt.encrypt(JSON.stringify(a));
                    c=c+"|"+encrypt.encrypt(a);
                }
            }
            return c;
        }
        else
        {
            var encrypt = new JSEncrypt();
            encrypt.setPublicKey('-----BEGIN PUBLIC KEY-----' + spbKey + '-----END PUBLIC KEY-----');
            return encrypt.encrypt(str);
        }
    }
    $(document).ready(function(){

        $("#de2").click(function(){     
                //这是要发送的内容
                var data = {
                    "username":"admin",
                    "password": "123465",
                    "age":18
                };
                $("#inbox").text("待发送数据:" + JSON.stringify(data));
                var result= EncryptPost(data, "/api/cer/ReciveDemoURL");
                $("#outbox").text(result.Msg+result.content);
            });
        $("#en").click(function() {
            if (($("#pbk").val() == "") || ($("#inbox").val() == "")) { alert("公钥和待加密字符串不能为空!"); }
            var txt = Base64.encode($("#inbox").val());
            var pbk = Base64.decode($("#pbk").val());
            var inbox=splitencrypt(txt,pbk);
            $("#inbox").val(inbox);
         });

        $("#de").click(function(){
            if (($("#inbox").val() == "") ||($("#prk").val() == "")){ alert("待解密密文和私钥都不能为空"); }
            $.ajax({
                beforeSend: function (xhr) {
                },
                type: "post",
                url: '/api/cer/UnRSA',  
                data:{Base64EncodeString:Base64.encode($("#inbox").val()),Base64PRKey:$("#prk").val()},//发送前BASE64编码,减少出错。收到后记得decode回来
                dataType: "json",
                success: function (data) {
                   if(data.Result!="Error") { $("#outbox").val(Base64.decode(data.content));}
                   else{
                       $("#outbox").val("解密失败!"+data.content);
                   }
                },
                error: function (msg) {
                    alert('遇到网络错误');
                }
            });        
        });


        $("#getkey").click(function () {                
            $.ajax({
                    beforeSend: function (xhr) {
                    },
                    type: "get",
                    async: false,
                    url: '/api/cer/GetRsaPairKey',
                    headers: { "RequestVerificationToken": $('@Html.AntiForgeryToken()').val() },
                    success: function (_data) {
                        if (_data.spbKey != undefined) {
                            $("#pbk").text(Base64.encode(_data.spbKey));
                            $("#prk").text(Base64.encode(_data.sprKey));
                            $("#inbox").text("AABBCCDDaabbccdd");
                        }
                        else {
                            alert('请刷新页面重试');
                        }
                    },
                    error: function (msg) {
                        alert('遇到网络错误');
                    }
                });
        });
    });
</script>

简单说明,点击登录的时候,AJAX用同步提交的方法向服务器临时拿回一个RSA公钥,然后将需要发送的内容加密后,分段加密后发送到服务器

 原创代码,转载请注明出处,翻版必究。


下拉加载更多评论
最新评论
暂无!