zoukankan      html  css  js  c++  java
  • NET应用——你的数据安全有必要升级

    最近又被【现场破解共享单车系统】刷了一脸,不得不开始后怕:如何防止类似的情况发生?

    想来想去,始终觉得将程序加密是最简单的做法。但是摩拜、ofo也有加密,为什么仍然被破解?那是因为请求在传输过程中被篡改了关键参数,从而导致服务器做出了错误的响应。

    如何防止篡改?自然是使用加密,加密方式的不同会决定篡改的成本(开头故事里的哥们大概用了2天),目前大多数的加密方法是暴露其他参数,只加密一个token,然后服务端验证token是否有效。从我的角度来讲,这种加密方式虽然破解难度很高,但多半是用MD5、SHA1、hmacMd5或AES等方式进行加密,结果往往是不可逆的,并且固定内容加密——不管多少次结果总是相同的。

    对于部分人来讲,只要你暴露出来的数据就可以做很多事情了,而且如果使用这种加密手段,那么服务端和客户端必须约定好加密手段和参与加密的字段(规则),这就相当于:我(服务端)和你(客户端)一起设计了钥匙,然后在需要的时候(发起请求)你就往指定地方(服务器)丢一个上了锁的箱子(加密后的数据),然后我制作钥匙打开锁。我也可以丢箱子,然后你用同样方式去打开。

    大家可以打开的锁安全么?想想都不靠谱。

    更好的是什么?我们设想一下:我(服务端)给你(客户端)制造锁的图纸,我有制造钥匙的图纸,在你需要的时候(发起请求)就做一把锁,然后把货物(原始数据)放在箱子里锁好(加密完成)丢在指定的地方(服务器),然后我制作出钥匙,开锁拿走货物(解密)。

    现在感觉如何?就像情报机构,侦察员只负责按照规则传递密码,而总部按照另一套规则解读,这个过程中就算被监听到,第三方也无法解读——这种方式就是今天要说的RSA。

    该怎么做呢?很简单:基于现在基本都是用http交互数据,相互传输数据用json再好不过,所以我会要求客户端将所有参数以json格式全部拼装起来,然后整串加密后传输到服务器,请求内容看上去是这样的:

    完全不知道这是一串什么鬼(如果我不是服务器的话):实际上这是一次登陆请求,里面包含账户、密码、时间戳、执行方法等,在服务端解密后是这样的:

    {"msg":{"method":"log_in","time":"2017-09-28 10:30:27","user_name":"test","user_pwd":"test"}}
    

    这就完成了一次正确的数据提交。并且如果有人修改了密文都会导致服务端解密失败从而驳回请求。这种情况下,就算你破译了整套客户端,也仅仅能拿到一个公钥,而远在服务端的私钥还是好好的(除非你已经攻破了人家的服务器),从而阻止参数在传递过程中被篡改(下面上点干货)。

    生成公私钥:

        /// <summary>
        /// RSA 的密钥产生 产生私钥 和公钥 
        /// </summary>
        /// <param name="priKeys">私钥</param>
        /// <param name="pubKey">公钥</param>
        public static void rsaKey(out string priKeys, out string pubKey)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            priKeys = rsa.ToXmlString(true);
            pubKey = rsa.ToXmlString(false);
        }

    RSA加密:

        /// <summary>
        /// RSA加密(不限长度)
        /// </summary>
        /// <param name="publicKey">公钥</param>
        /// <param name="text">要加密的文本</param>
        /// <param name="enc">编码方式</param>
        /// <returns>密文</returns>
        public static string rsaEncrypt(string pubKey, string text, Encoding enc)
        {
            using (var rsaProvider = new RSACryptoServiceProvider())
            {
                var inputBytes = enc.GetBytes(text);
                rsaProvider.FromXmlString(pubKey);
                int bufferSize = (rsaProvider.KeySize / 8) - 11;
                var buffer = new byte[bufferSize];
                using (MemoryStream inputStream = new MemoryStream(inputBytes), outputStream = new MemoryStream())
                {
                    while (true)
                    {
                        int readSize = inputStream.Read(buffer, 0, bufferSize);
                        if (readSize <= 0)
                        {
                            break;
                        }
                        var temp = new byte[readSize];
                        Array.Copy(buffer, 0, temp, 0, readSize);
                        var encryptedBytes = rsaProvider.Encrypt(temp, false);
                        outputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
                    }
                    return Convert.ToBase64String(outputStream.ToArray());
                }
            }
        }

    RSA解密:

        /// <summary>
        /// rsa解密
        /// </summary>
        /// <param name="priKey">私钥</param>
        /// <param name="encrypted">要解密的字符串</param>
        /// <param name="enc">编码方式</param>
        /// <returns>明文</returns>
        public static string rsaDecrypt(string priKey, string encrypted, Encoding enc)
        {
            if (string.IsNullOrEmpty(encrypted))
            {
                return string.Empty;
            }
    
            if (string.IsNullOrWhiteSpace(priKey))
            {
                throw new ArgumentException("Invalid Private Key");
            }
    
            using (var rsaProvider = new RSACryptoServiceProvider())
            {
                var inputBytes = Convert.FromBase64String(encrypted);
                rsaProvider.FromXmlString(priKey);
                int bufferSize = rsaProvider.KeySize / 8;
                var buffer = new byte[bufferSize];
                using (MemoryStream inputStream = new MemoryStream(inputBytes),
                     outputStream = new MemoryStream())
                {
                    while (true)
                    {
                        int readSize = inputStream.Read(buffer, 0, bufferSize);
                        if (readSize <= 0)
                        {
                            break;
                        }
    
                        var temp = new byte[readSize];
                        Array.Copy(buffer, 0, temp, 0, readSize);
                        var rawBytes = rsaProvider.Decrypt(temp, false);
                        outputStream.Write(rawBytes, 0, rawBytes.Length);
                    }
                    return enc.GetString(outputStream.ToArray());
                }
            }
        }

    因为本人对js也有一些兴趣,所以尝试着利用js与服务器交互,然鹅发现js需要java格式的公私钥,于是找到了转换公私钥的代码,亲测有效,在这里搬运下,利己利他:

        /// <summary>
        /// RSA公钥格式转换(net转java)
        /// </summary>
        /// <param name="pubKey">net格式公钥</param>
        /// <returns>java格式的公钥</returns>
        public static string rsaPubKeyNet2Java(string pubKey)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(pubKey);
            BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
            BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
            RsaKeyParameters pub = new RsaKeyParameters(false, m, p);
            SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub);
            byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
            return Convert.ToBase64String(serializedPublicBytes);
        }
    
        /// <summary>
        /// RSA私钥格式转换(net转java)
        /// </summary>
        /// <param name="priKey">net格式私钥</param>
        /// <returns>java格式的私钥</returns>
        public static string rsaPriKeyNet2Java(string priKey)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(priKey);
            BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
            BigInteger exp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
            BigInteger d = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("D")[0].InnerText));
            BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("P")[0].InnerText));
            BigInteger q = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Q")[0].InnerText));
            BigInteger dp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DP")[0].InnerText));
            BigInteger dq = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DQ")[0].InnerText));
            BigInteger qinv = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("InverseQ")[0].InnerText));
            RsaPrivateCrtKeyParameters privateKeyParam = new RsaPrivateCrtKeyParameters(m, exp, d, p, q, dp, dq, qinv);
            PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKeyParam);
            byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetEncoded();
            return Convert.ToBase64String(serializedPrivateBytes);
        }

    进行转换需要用到一个三方类库,我的百度网盘里可以下载 http://pan.baidu.com/s/1b9Wy42,可以直接使用。

    js的RSA加密也要用到一个库,在这里捎带贴一下:

        <script type="text/javascript" src="https://passport.cnblogs.com/scripts/jsencrypt.min.js"></script>
        <script>
            //实例化对象
            var crypt = new JSEncrypt();
            //设置私钥
            crypt.setPrivateKey('你的RSA公钥');
    
            $('#log_in').click(function () {
                //拼装发送数据
                var post_data = crypt.encrypt('{"msg":{"method":"log_in","time":"' + Tool.getNowTime() + '","user_name":"' + $('.name').val() + '","user_pwd":"' + $('.pwd').val() + '"}}');
                //在这里发起你的请求
            });
        </script>

    因为js可以被看到,所以你更需要RSA,因为全部被看到也无所谓了。希望对大家有帮助~

  • 相关阅读:
    zabbix监控大数据
    MongoDB
    CDH管理节点扩容磁盘步骤
    CDH的ntp时间同步
    监控文件是否更新
    crontab配置
    hue的安装
    在编译内核的最后阶段出现sdhci_esdhc_imx_pdata未定义的错误
    java程序,在windows下设置为开机自启动
    全局启动函数start_kernel函数注解
  • 原文地址:https://www.cnblogs.com/muchengqingxin/p/7607190.html
Copyright © 2011-2022 走看看