循序渐进学.Net Core Web Api开发系列【16】:应用安全续-加密与解密
系列目录
本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi
一、概述
应用安全除了用户权限认证外,还要考虑到数据安全,传输安全、系统漏洞等方面。本篇文章重点讨论数据存储安全和传输安全,主要技术手段就是加密和解密。
二、基本概念
信息在传输和存储的过程中有泄密的风险,加密的目的就是解决这些风险。
1、信息存储在数据库中,如果数据库泄露会造成敏感数据泄露,如用户密码,手机号码、工资信息;
2、数据传输过程中数据遭到劫持,泄露用户名、密码等信息。
针对不同的应用场景,对应采用的加密手段:
1、密码存储:任何人都不可以看到密码,验证是只要比对加密后的字符串是否一致即可,所以要采用非可逆加密算法;
2、敏感信息存储:存储的信息是加密的,同时需要解密还原数据,所以采用可逆的算法,加密后可以解密,加密的密码和解密的密码可以一致。
3、敏感信息传输:在浏览器向服务器传输数据的过程,建议优先采用HTTPS传输模式即可解决问题。如果不采用https的传输模式,用户提交的数据会有被窃取的风险,由于客户端加密是采用js进行操作,分析其网页源码可以查询到加密的密钥,所以就不能采用上面的对称加密算法,而是必须采用非对称加密算法,即:加密密码和解密密码不一样,加密密码是可以公开的,有加密密码无法解密信息,这样通过客户端加密、服务端解密解决数据被窃取的风险。
总结如下:
问题场景 |
采用加密手段 |
加密算法 |
用户密码存储过程泄密 |
非可逆加密算法 |
MD5 |
敏感数据存储过程泄密 |
可逆对称加密算法 |
DES/AES |
敏感数据传输过程泄密 |
可逆非对称加密算法 |
RSA |
三、MD5加密
首先需要通过NuGet获取包:NETCore.Encrypt,下同。
加密代码:
var srcString = "ghc123456"; var hashed = EncryptProvider.Md5(srcString);
非可逆加密算法,不好解密。
四、DES加密与解密
DES的加密与解密:
//需要加密的信息 var srcString = "ghc123456";</span><span style="color: #008000;">//</span><span style="color: #008000;">密码:24位字符串,通过EncryptProvider.CreateDesKey()来生成。</span> <span style="color: #0000ff;">var</span> desKey = <span style="color: #800000;">"</span><span style="color: #800000;">i4FftwEAP7enqKRl8JhBC7gv</span><span style="color: #800000;">"</span><span style="color: #000000;">; </span><span style="color: #008000;">//</span><span style="color: #008000;">加密</span> <span style="color: #0000ff;">var</span> encrypted =<span style="color: #000000;"> EncryptProvider.DESEncrypt(srcString, desKey); </span><span style="color: #008000;">//</span><span style="color: #008000;">解密</span> <span style="color: #0000ff;">var</span> decrypted = EncryptProvider.DESDecrypt(encrypted, desKey);</pre>
五、RSA客户端加密、服务端解密
1、利用工具生成密钥
RSA加密与解密需要用到一对密钥,可以通过第三方工具来生成密钥,我这里用的是阿里蚂蚁金服的一个签名工具:
工具下载:https://docs.open.alipay.com/291/105971
密钥格式选择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><span style="color: #000000;"> $(document).ready(function () { $(</span><span style="color: #800000;">"</span><span style="color: #800000;">#query</span><span style="color: #800000;">"</span>).click(function (<span style="color: #0000ff;">event</span><span style="color: #000000;">) { </span><span style="color: #008000;">//</span><span style="color: #008000;">需要加密的信息</span> <span style="color: #0000ff;">var</span> pwd = <span style="color: #800000;">"</span><span style="color: #800000;">ghc123456</span><span style="color: #800000;">"</span><span style="color: #000000;">; </span><span style="color: #008000;">//</span><span style="color: #008000;">公钥</span> <span style="color: #0000ff;">var</span> publicKey = <span style="color: #800000;">"</span><span style="color: #800000;">MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr8a8EjoY4op+k/BF0hjkaOgMDAmSl+VCc2rU6j/Ji9BsX/tRWM4o3wZgkMdAWim9vwM0Ll4x/jTsVr1+Qe7ffnBl6ZLpMs77MbVVG5wowVvie1rMi2fB7akdi+Id+R3SHZNSTnbtEATXtY8nGV5ZDcHMrw6PI30+HlaclxmhFSr+UGERyi6/mLKnC3Y5elxu8Zlj8eHPZhV6DV87ngkGdp4aLdWWfqP1Iz0cIMAJ7hiYP4o4/W/vn1YSyJeTs/oQvUeEaMVGxGIXOY+m9ToZbPwjXWPjeZJ1RLRKfhYoobguyGezQNC0xlBdWNU9gfVw7mk0+q1eCe5XfA45O54MbwIDAQAB</span><span style="color: #800000;">"</span><span style="color: #000000;">; </span><span style="color: #0000ff;">var</span> encrypt = <span style="color: #0000ff;">new</span><span style="color: #000000;"> JSEncrypt(); encrypt.setPublicKey(publicKey); </span><span style="color: #0000ff;">var</span> encrypttext =<span style="color: #000000;"> encrypt.encrypt(pwd); $(</span><span style="color: #800000;">"</span><span style="color: #800000;">#text</span><span style="color: #800000;">"</span><span style="color: #000000;">).val(encrypttext); }); }); </span></script>
</head>
<body>
<h1>Query</h1><button id=<span style="color: #800000;">'</span><span style="color: #800000;">query</span><span style="color: #800000;">'</span>>Qyery</button> <textarea id=<span style="color: #800000;">"</span><span style="color: #800000;">text</span><span style="color: #800000;">"</span>></textarea>
</body>
</html>
3、后端解密
把前端加密获得的密文Copy到下面代码中,验证解密后的信息。
using System.Security.Cryptography;namespace Encrypt { class Program { static void Main(string[] args) { Console.WriteLine("Study Encrypt!");</span><span style="color: #008000;">//</span><span style="color: #008000;">从前端传来的加密后的密文</span> <span style="color: #0000ff;">var</span> encryptedMessage = <span style="color: #800000;">"</span><span style="color: #800000;">Qw0W7P3dScUe3isXIVkumhgTxKk0fcaxzyvyCVnFEHBH6qw/daMKt0BYK1SrFZrqmcX1tRx/yeAa0ea2QsYmLL2OynffoUnPtjLjNHZd9R7+Uqh8e6nmD1r4yK6o37tfb9SmPxJtgLR45dnxcFAIt3xRY5eqXFCMCJKdKwV7aLMayG1+Rg7YBhTfUoNbJoLTODupCweaUVwZdvcxOZrqd5x3SRw3x+bPkQpchJRXc4QoLhEfh/Hw5tiNkuMUzPt/3iyx7iuKQR4q+9Wnd81UaenLaihVlkiU5MEjZJOnz1HaEDhMc9rMlTXV36jAOOAVTufb2jILbNycuhGQkFh9SA==</span><span style="color: #800000;">"</span><span style="color: #000000;">; </span><span style="color: #008000;">//</span><span style="color: #008000;">私钥</span> <span style="color: #0000ff;">var</span> privateKey = <span style="color: #800000;">"</span><span style="color: #800000;">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</span><span style="color: #800000;">"</span><span style="color: #000000;">; </span><span style="color: #008000;">//</span><span style="color: #008000;">根据私钥生成RSA提供者</span> RSACryptoServiceProvider rsaCryptoServiceProvider =<span style="color: #000000;"> CreateRsaProviderFromPrivateKey(privateKey); </span><span style="color: #0000ff;">byte</span>[] decrypted = rsaCryptoServiceProvider.Decrypt(Convert.FromBase64String(encryptedMessage), <span style="color: #0000ff;">false</span><span style="color: #000000;">); Console.WriteLine($</span><span style="color: #800000;">"</span><span style="color: #800000;">decrypted = {Encoding.UTF8.GetString(decrypted)}</span><span style="color: #800000;">"</span><span style="color: #000000;">); Console.ReadLine(); } </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(<span style="color: #0000ff;">string</span><span style="color: #000000;"> privateKey) { </span><span style="color: #0000ff;">var</span> privateKeyBits =<span style="color: #000000;"> System.Convert.FromBase64String(privateKey); </span><span style="color: #0000ff;">var</span> RSA = <span style="color: #0000ff;">new</span><span style="color: #000000;"> RSACryptoServiceProvider(); </span><span style="color: #0000ff;">var</span> RSAparams = <span style="color: #0000ff;">new</span><span style="color: #000000;"> RSAParameters(); </span><span style="color: #0000ff;">using</span> (BinaryReader binr = <span style="color: #0000ff;">new</span> BinaryReader(<span style="color: #0000ff;">new</span><span style="color: #000000;"> MemoryStream(privateKeyBits))) { </span><span style="color: #0000ff;">byte</span> bt = <span style="color: #800080;">0</span><span style="color: #000000;">; </span><span style="color: #0000ff;">ushort</span> twobytes = <span style="color: #800080;">0</span><span style="color: #000000;">; twobytes </span>=<span style="color: #000000;"> binr.ReadUInt16(); </span><span style="color: #0000ff;">if</span> (twobytes == <span style="color: #800080;">0x8130</span><span style="color: #000000;">) binr.ReadByte(); </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (twobytes == <span style="color: #800080;">0x8230</span><span style="color: #000000;">) binr.ReadInt16(); </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> Exception(<span style="color: #800000;">"</span><span style="color: #800000;">Unexpected value read binr.ReadUInt16()</span><span style="color: #800000;">"</span><span style="color: #000000;">); twobytes </span>=<span style="color: #000000;"> binr.ReadUInt16(); </span><span style="color: #0000ff;">if</span> (twobytes != <span style="color: #800080;">0x0102</span><span style="color: #000000;">) </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> Exception(<span style="color: #800000;">"</span><span style="color: #800000;">Unexpected version</span><span style="color: #800000;">"</span><span style="color: #000000;">); bt </span>=<span style="color: #000000;"> binr.ReadByte(); </span><span style="color: #0000ff;">if</span> (bt != <span style="color: #800080;">0x00</span><span style="color: #000000;">) </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> Exception(<span style="color: #800000;">"</span><span style="color: #800000;">Unexpected value read binr.ReadByte()</span><span style="color: #800000;">"</span><span style="color: #000000;">); RSAparams.Modulus </span>=<span style="color: #000000;"> binr.ReadBytes(GetIntegerSize(binr)); RSAparams.Exponent </span>=<span style="color: #000000;"> binr.ReadBytes(GetIntegerSize(binr)); RSAparams.D </span>=<span style="color: #000000;"> binr.ReadBytes(GetIntegerSize(binr)); RSAparams.P </span>=<span style="color: #000000;"> binr.ReadBytes(GetIntegerSize(binr)); RSAparams.Q </span>=<span style="color: #000000;"> binr.ReadBytes(GetIntegerSize(binr)); RSAparams.DP </span>=<span style="color: #000000;"> binr.ReadBytes(GetIntegerSize(binr)); RSAparams.DQ </span>=<span style="color: #000000;"> binr.ReadBytes(GetIntegerSize(binr)); RSAparams.InverseQ </span>=<span style="color: #000000;"> binr.ReadBytes(GetIntegerSize(binr)); } RSA.ImportParameters(RSAparams); </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> RSA; } </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> GetIntegerSize(BinaryReader binr) { </span><span style="color: #0000ff;">byte</span> bt = <span style="color: #800080;">0</span><span style="color: #000000;">; </span><span style="color: #0000ff;">byte</span> lowbyte = <span style="color: #800080;">0x00</span><span style="color: #000000;">; </span><span style="color: #0000ff;">byte</span> highbyte = <span style="color: #800080;">0x00</span><span style="color: #000000;">; </span><span style="color: #0000ff;">int</span> count = <span style="color: #800080;">0</span><span style="color: #000000;">; bt </span>=<span style="color: #000000;"> binr.ReadByte(); </span><span style="color: #0000ff;">if</span> (bt != <span style="color: #800080;">0x02</span><span style="color: #000000;">) </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">; bt </span>=<span style="color: #000000;"> binr.ReadByte(); </span><span style="color: #0000ff;">if</span> (bt == <span style="color: #800080;">0x81</span><span style="color: #000000;">) count </span>=<span style="color: #000000;"> binr.ReadByte(); </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (bt == <span style="color: #800080;">0x82</span><span style="color: #000000;">) { highbyte </span>=<span style="color: #000000;"> binr.ReadByte(); lowbyte </span>=<span style="color: #000000;"> binr.ReadByte(); </span><span style="color: #0000ff;">byte</span>[] modint = { lowbyte, highbyte, <span style="color: #800080;">0x00</span>, <span style="color: #800080;">0x00</span><span style="color: #000000;"> }; count </span>= BitConverter.ToInt32(modint, <span style="color: #800080;">0</span><span style="color: #000000;">); } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> { count </span>=<span style="color: #000000;"> bt; } </span><span style="color: #0000ff;">while</span> (binr.ReadByte() == <span style="color: #800080;">0x00</span><span style="color: #000000;">) { count </span>-= <span style="color: #800080;">1</span><span style="color: #000000;">; } binr.BaseStream.Seek(</span>-<span style="color: #800080;">1</span><span style="color: #000000;">, SeekOrigin.Current); </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> count; } }
}
注意:+号的处理:因为数据在网络上传输时,非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,而base64编码在传输到后端的时候,+会变成空格,因此先替换掉。后端再替换回来。
前端js代码:
1 | var str = encodeURI(pwddata).replace(/\+/g, '%2B' ) |
后端C#代码:
1 | var str = pwd.Replace( "%2B" , "+" ) |