系列目录

循序渐进学.Net Core Web Api开发系列目录

本系列涉及到的源码下载地址: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"; //密码:24位字符串,通过EncryptProvider.CreateDesKey()来生成。
var desKey = "i4FftwEAP7enqKRl8JhBC7gv"; //加密
var encrypted = EncryptProvider.DESEncrypt(srcString, desKey); //解密
var decrypted = EncryptProvider.DESDecrypt(encrypted, desKey);

五、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>
$(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>

3、后端解密

把前端加密获得的密文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 = ;
ushort twobytes = ;
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 = ;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = ;
bt = binr.ReadByte();
if (bt != 0x02)
return ;
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, );
}
else
{
count = bt;
} while (binr.ReadByte() == 0x00)
{
count -= ;
}
binr.BaseStream.Seek(-, SeekOrigin.Current);
return count;
}
}
}

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

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

后端C#代码:

var str = pwd.Replace("%2B","+")

 

循序渐进学.Net Core Web Api开发系列【16】:应用安全续-加密与解密的更多相关文章

  1. 循序渐进学.Net Core Web Api开发系列【0】:序言与目录

    一.序言 我大约在2003年时候开始接触到.NET,最初在.NET framework 1.1版本下写过代码,曾经做过WinForm和ASP.NET开发.大约在2010年的时候转型JAVA环境,这么多 ...

  2. 循序渐进学.Net Core Web Api开发系列【15】:应用安全

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍W ...

  3. 循序渐进学.Net Core Web Api开发系列【14】:异常处理

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍异 ...

  4. 循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍如 ...

  5. 循序渐进学.Net Core Web Api开发系列【12】:缓存

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍如 ...

  6. 循序渐进学.Net Core Web Api开发系列【11】:依赖注入

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍如 ...

  7. 循序渐进学.Net Core Web Api开发系列【10】:使用日志

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.本篇概述 本篇介 ...

  8. 循序渐进学.Net Core Web Api开发系列【9】:常用的数据库操作

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇描述一 ...

  9. 循序渐进学.Net Core Web Api开发系列【8】:访问数据库(基本功能)

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇讨论如 ...

随机推荐

  1. Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction 异常一例

    参考下面的文章,最终找到我的报错原因: 我是在 service中一个以 get开头的方法中,加入了一行数据库数据删除代码,因为 spring的事务配置中,配置了get开头的方法 是 readonle的 ...

  2. map文件的使用

    map文件相信大家并不陌生,大家都知道是用来调试的,但是具体怎么用你又清不清楚呢? 其实也很简单 1.拿JQ为例,我们需要备有jquery.js.jquery.min.js.jquery.min.ma ...

  3. Sql语句里面调用变量

    sql语句里面调用变量的话有两种情况,一种是字符类型,一种是整型.浮点型之类的数字 db1.Execute("insert DataInformation values('" + ...

  4. swift学习笔记3

    1.在 Swift 中,枚举类型是一等(first-class)类型.它们采用了很多在传统上只被类(class)所支持的特性,例如计算型属性(computed properties),用于提供枚举值的 ...

  5. Codeforces 666 B. World Tour

    http://codeforces.com/problemset/problem/666/B 题意: 给定一张边权均为1的有向图,求四个不同的点A,B,C,D,使得dis[A][B]+dis[B][C ...

  6. 第一次使用 markdown 写博客

    Web前端 js 框架(四选一) 有可能的话,学 Vue.js ,React.js ,Angular.js,Awrelia css 学习 Sass 学会 css 的可编程 HTML5 详细语法 Nod ...

  7. MongoDB探索之路(二)——系统设计之CRUD

    1.构造实体类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using ...

  8. HTML5+CSS把footer固定在底部

    在刚开始给网页写footer的时候,我们会碰到一个让人烦躁的问题:当页面内容太少时,footer显示在了页面中间,这是我们不希望出现的,我们希望它能够永远呆在底部,不管网页的内容是多还是少.下面的代码 ...

  9. python 入门基础22 --复习 面向对象

    面向过程编程思想: 核心:过程 过程指的是解决问题的具体步骤,即先干什么再干什么. 基于该编程思想编写程序,相当于一条流水线,一种机械式的思维方式. 面向对象编程思想: 核心:对象 对象指的是数据与方 ...

  10. linux(CentOS7)中安装erlang(20.3)以及rabbitmq(3.7.9)的步骤以及一些注意事项

    首先下载安装包,之后先安装erlang,安装erlang需要很多依赖,所以一步步来: 首先 wxWidgets会报错,这个不是必须的,可以不安装,不影响 然后需要安装一些必须的依赖: yum inst ...