应用安全续 加密与解密

本篇概述

  应用安全除了用户权限认证外,还要考虑到数据安全,传输安全、系统漏洞等方面。本篇文章重点讨论数据存储安全和传输安全,主要技术手段就是加密和解密。

1 基本概念

  大部分web应用习惯采用Session来保存用户认证信息,对于WebApi而言,调用者不一定是Web浏览器,可能是Androi信息在传输和存储的过程中有泄密的风险,加密的目的就是解决这些风险。

  • 信息存储在数据库中,如果数据库泄露会造成敏感数据泄露,如用户密码,手机号码、工资信息;
  • 数据传输过程中数据遭到劫持,泄露用户名、密码等信息。

  针对不同的应用场景,对应采用的加密手段:

  • 密码存储:任何人都不可以看到密码,验证是只要比对加密后的字符串是否一致即可,所以要采用非可逆加密算法;

  • 敏感信息存储:存储的信息是加密的,同时需要解密还原数据,所以采用可逆的算法,加密后可以解密,加密的密码和解密的密码可以一致。

  • 敏感信息传输:在浏览器向服务器传输数据的过程,建议优先采用HTTPS传输模式即可解决问题。如果不采用https的传输模式,用户提交的数据会有被窃取的风险,由于客户端加密是采用js进行操作,分析其网页源码可以查询到加密的密钥,所以就不能采用上面的对称加密算法,而是必须采用非对称加密算法,即:加密密码和解密密码不一样,加密密码是可以公开的,有加密密码无法解密信息,这样通过客户端加密、服务端解密解决数据被窃取的风险。

总结如下:

使用场景

2 MD5加密

  首先需要通过NuGet获取包:NETCore.Encrypt,下同。 使用场景

  加密代码:

var srcString = "ghc123456";
var hashed = EncryptProvider.Md5(srcString);

  非可逆加密算法,不好解密。

3 DES加密与解密

  DES的加密与解密:

//需要加密的信息
var srcString = "ghc123456";
//密码:24位字符串,通过EncryptProvider.CreateDesKey()来生成。
var desKey = "i4FftwEAP7enqKRl8JhBC7gv";
//加密
var encrypted = EncryptProvider.DESEncrypt(srcString, desKey);
//解密
var decrypted = EncryptProvider.DESDecrypt(encrypted, desKey);

4 RSA客户端加密、服务端解密

1、利用工具生成密钥

RSA加密与解密需要用到一对密钥,可以通过第三方工具来生成密钥,我这里用的是阿里蚂蚁金服的一个签名工具: 工具下载

使用场景

  密钥格式选择PKCS1

  公钥给客户端加密使用,私钥给服务器端解密使用。

2、前端加密   首先通过Bower引入两个js的库:jQuery、jsencrypt 使用场景

  前端加密代码:

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <title>Using jQuery</title>
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/jsencrypt/bin/jsencrypt.min.js"></script>

    <script>
        $(document).ready(function () {
            $("#query").click(function (event) {

                //需要加密的信息
                var pwd = "ghc123456";

                //公钥
                var publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr8a8EjoY4op+k/BF0hjkaOgMDAmSl+VCc2rU6j/Ji9BsX/tRWM4o3wZgkMdAWim9vwM0Ll4x/jTsVr1+Qe7ffnBl6ZLpMs77MbVVG5wowVvie1rMi2fB7akdi+Id+R3SHZNSTnbtEATXtY8nGV5ZDcHMrw6PI30+HlaclxmhFSr+UGERyi6/mLKnC3Y5elxu8Zlj8eHPZhV6DV87ngkGdp4aLdWWfqP1Iz0cIMAJ7hiYP4o4/W/vn1YSyJeTs/oQvUeEaMVGxGIXOY+m9ToZbPwjXWPjeZJ1RLRKfhYoobguyGezQNC0xlBdWNU9gfVw7mk0+q1eCe5XfA45O54MbwIDAQAB";

                var encrypt = new JSEncrypt();
                encrypt.setPublicKey(publicKey);

                var encrypttext = encrypt.encrypt(pwd);

                $("#text").val(encrypttext);
            });

        });
    </script>

</head>

<body>
    <h1>Query</h1>

    <button id='query'>Qyery</button>
    <textarea id="text"></textarea>

</body>
</html>
  1. 后端解密

  把前端加密获得的密文Copy到下面代码中,验证解密后的信息。

using System.Security.Cryptography;
namespace Encrypt
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Study Encrypt!");
            //从前端传来的加密后的密文
            var encryptedMessage = "Qw0W7P3dScUe3isXIVkumhgTxKk0fcaxzyvyCVnFEHBH6qw/daMKt0BYK1SrFZrqmcX1tRx/yeAa0ea2QsYmLL2OynffoUnPtjLjNHZd9R7+Uqh8e6nmD1r4yK6o37tfb9SmPxJtgLR45dnxcFAIt3xRY5eqXFCMCJKdKwV7aLMayG1+Rg7YBhTfUoNbJoLTODupCweaUVwZdvcxOZrqd5x3SRw3x+bPkQpchJRXc4QoLhEfh/Hw5tiNkuMUzPt/3iyx7iuKQR4q+9Wnd81UaenLaihVlkiU5MEjZJOnz1HaEDhMc9rMlTXV36jAOOAVTufb2jILbNycuhGQkFh9SA==";
            //私钥
            var privateKey = "MIIEowIBAAKCAQEAr8a8EjoY4op+k/BF0hjkaOgMDAmSl+VCc2rU6j/Ji9BsX/tRWM4o3wZgkMdAWim9vwM0Ll4x/jTsVr1+Qe7ffnBl6ZLpMs77MbVVG5wowVvie1rMi2fB7akdi+Id+R3SHZNSTnbtEATXtY8nGV5ZDcHMrw6PI30+HlaclxmhFSr+UGERyi6/mLKnC3Y5elxu8Zlj8eHPZhV6DV87ngkGdp4aLdWWfqP1Iz0cIMAJ7hiYP4o4/W/vn1YSyJeTs/oQvUeEaMVGxGIXOY+m9ToZbPwjXWPjeZJ1RLRKfhYoobguyGezQNC0xlBdWNU9gfVw7mk0+q1eCe5XfA45O54MbwIDAQABAoIBAH0lfVlsy7Le7+fcNZmz50tZito3JovGylzqPtTYvWIx7jcX837KqQbAv5fUhNisx09rtIcewXE/tNS87Vt7+ttGowh9dFKcUvO9Ku8Ra2LfTIyOxPqr0MKomUSypKxssuAjt4Ht4jJ5gCrf1PKW3ciRpm0sbHTUApoPCEX8FVe/qlxDVv+9S7chJxsGuzIXNDGMW4XfHw+RaUvjOFQgy6R9HPtvEw3EWY53OY+Mvd7Pr0o2ATaetFnBwyZa3AJF+SU1wzqtJZhxd9/ACly1s4yXmG+rvD+2k7bhAXWQRxwxSDXDCd/ibRM4YklNRkAD+IWJrbZ1rh3uoHisiBv3O2ECgYEA2BHdBDKD2Odd2861kkVck5Ar0ii883y4vNKLNPGQaUmPbHCo+i6CREUDW0SxKXkViaIAFjj0Kmhqa6XASu4SCCc8s+1/2Pyrxb5qgSSJY2LvbZ8SeZi8DfBOXKPdCtUqyHMIYBw3rPyzXwoJEAdHSCrk83tt4urm7qKSqgYpRx0CgYEA0EKcQ3mT+Hd0cx+ISJ2D2X01Z1+zb+Dusn23fr5qs5Nox+VE71+9DN9GVAlBFNg4oz7FCsFS3mJRbvQmmybw0KwU/rKHdnvk15B6IglPgshkVar7j7G51UVntHMW4IBU7jTUoMs1yQ6PPzb9E2Z6UeHbGaoRZCVYZidt55rDL/sCgYEApbI9PcTHW4VCcxg4Ie3TKs567HWVQVw6B4OmgXlmd3eT52MWEpWsDFKoWkt5WQakP6HeUyxmAkeEpPy9VDjx1xLP+GN/kZVi3QhDgLnWKkNqvTQp5Nn+DOpmDaEUGASVBJdCqwG4qI45t/5oKMSMI4nRfe7/u+7MHeDKfFyxNvkCgYBYJPkybdC9BwIYf64U3eYiNSZXPGAb6B3fGeqCEGHk420jvdvxXJoNSqrfgpMzGVjPbw/Cv5QtX3uL9HYqkM634z13l2RSN5nhytqGcV5fwiUFRTr31IcMxzVfYJ68IlTQBThBXgDDug/S95khjuwSn/81249EzbGeeu2/avdV5QKBgGhTmFO9/1HRq0F538XlL8WVvUeyM+XUUNT1qQQsc22anUkM09cv+64ZyQZEqDNX46Tbl2BfTwFhL50MMj7Edjp4L+2dROiuffzcuEQCBCkCoV6B58a9NQehwu8h1j1kxNjDDPSeZnmC0ZJ/Eum1TVcnNa2Zb6II5R72qXThwJM9";
            //根据私钥生成RSA提供者
            RSACryptoServiceProvider rsaCryptoServiceProvider = CreateRsaProviderFromPrivateKey(privateKey);
            byte[] decrypted = rsaCryptoServiceProvider.Decrypt(Convert.FromBase64String(encryptedMessage), false);
            Console.WriteLine($ "decrypted = {Encoding.UTF8.GetString(decrypted)}");
            Console.ReadLine();
        }
        private static RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey)
        {
            var privateKeyBits = System.Convert.FromBase64String(privateKey);
            var RSA = new RSACryptoServiceProvider();
            var RSAparams = new RSAParameters();
            using(BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
            {
                byte bt = 0;
                ushort twobytes = 0;
                twobytes = binr.ReadUInt16();
                if(twobytes == 0x8130) binr.ReadByte();
                else if(twobytes == 0x8230) binr.ReadInt16();
                else throw new Exception("Unexpected value read binr.ReadUInt16()");
                twobytes = binr.ReadUInt16();
                if(twobytes != 0x0102) throw new Exception("Unexpected version");
                bt = binr.ReadByte();
                if(bt != 0x00) throw new Exception("Unexpected value read binr.ReadByte()");
                RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.D = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.P = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr));
                RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
            }
            RSA.ImportParameters(RSAparams);
            return RSA;
        }
        private static int GetIntegerSize(BinaryReader binr)
        {
            byte bt = 0;
            byte lowbyte = 0x00;
            byte highbyte = 0x00;
            int count = 0;
            bt = binr.ReadByte();
            if(bt != 0x02) return 0;
            bt = binr.ReadByte();
            if(bt == 0x81) count = binr.ReadByte();
            else
            if(bt == 0x82)
            {
                highbyte = binr.ReadByte();
                lowbyte = binr.ReadByte();
                byte[] modint = {
                    lowbyte, highbyte, 0x00, 0x00
                };
                count = BitConverter.ToInt32(modint, 0);
            }
            else
            {
                count = bt;
            }
            while(binr.ReadByte() == 0x00)
            {
                count -= 1;
            }
            binr.BaseStream.Seek(-1, SeekOrigin.Current);
            return count;
        }
    }
}

注意:+号的处理:因为数据在网络上传输时,非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,而base64编码在传输到后端的时候,+会变成空格,因此先替换掉。后端再替换回来。

  前端js代码:

var str = encodeURI(pwddata).replace(/\+/g, '%2B')

  后端C#代码:

var str = pwd.Replace("%2B","+")
版权声明: 本文为智客工坊「seabluescn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

results matching ""

    No results matching ""