数据加密方式有:
单向加密、对称加密、非对称加密、加密盐、散列函数、数字签名。
1、单向加密
单向加密通过对数据进行摘要计算生成密文,密文不可逆推还原。只能加密,不能解密,常用于提取数据的指纹信息以此来验证数据的完整性。但是会引发雪崩效应(雪崩效应就是一种不稳定的平衡状态也是加密算法的一种特征,它指明文或密钥的少量变化会引起密文的很大变化,就像雪崩前,山上看上去很平静,但是只要有一点问题,就会造成一片大崩溃。 可以用在很多场合对于Hash码,雪崩效应是指少量消息位的变化会引起信息摘要的许多位变化。)
算法代表:Base64,MD5,SHA。
2、对称加密
对称加密的加密和解密是使用同一个密钥;加密和解密的速度比较快,效率比较高;但是密钥传输过程不安全,容易破解,而且密钥管理也比较麻烦。
算法代表:DES,3DES,AES,IDEA,RC4,RC5。
对称加密可以分为两类:序列密码和分组密码
2.1、序列密码
从概念上讲,序列密码(stream cipher)的操作过程与我们想象中加密的过程一致。将1字节的明文输入加密算法,就得到1字节的密文输出。在对端则进行相反的过程。整个过程持续重复,直到所有数据处理完成。因为这种思路比较简单,序列密码绝不能第二次使用相同的密钥。这是因为在实际使用中,攻击者知道或者可以预测特定区域的明文(请思考加密HTTP请求的情景;许多请求的请求方法、协议版本、请求头名称都是一样的)当你知道明文,又观察到密文时,就可以解析一部分密钥序列。如果使用了相同的密钥,那么就可以解密后续的部分密文。为了解这个问题,序列密码都与从长期密钥中提取出来的一次性密钥一同使用。
2.2、分组密码
分组密码(block cipher)每次加密一整块数据,并且现代的分组密码倾向于使用128位(16字节)大小的块。一种分组密码就是一个变换函数:接受输入并生成看似杂乱无章的输出。只要使用相同的密钥,每一个可能的输入组合都有唯一的输出。
我们可以理解为更高级的对称加密算法。这种加密算法也是非常常见,例如AES加密,有128位、192位和256位的加密强度。在现在的系统对接时,AES加密非常常见。
3、非对称加密
相对对称加密而言,无需拥有同一组密钥,非对称加密是一种“信息公开的密钥交换协议”。非对称加密需要公开密钥和私有密钥两组密钥,公开密钥和私有密钥是配对起来的,也就是说使用公开密钥进行数据加密,只有对应的私有密钥才能解密。这两个密钥是数学相关,用某用户密钥加密后的密文,只能使用该用户的加密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个密钥性质。这里把公开的密钥为公钥,不公开的密钥为私钥。
算法代表:RSA,DSA。
4、加密盐
加密盐也是比较常听到的一个概念,盐就是一个随机字符串用来和我们的加密串拼接后进行加密。加盐主要是为了提供加密字符串的安全性。假如有一个加盐后的加密串,黑客通过一定手段这个加密串,他拿到的明文,并不是我们加密前的字符串,而是加密前的字符串和盐组合的字符串,这样相对来说又增加了字符串的安全性。
5、散列函数
散列函数在密码学中也是不可缺少的一部分。散列函数(hash function)是将任意长度的输入转化为定长输出的算法。谈到散列函数,肯定会想到MD5加密,这种就是一种最为常见的散列函数。散列函数的特点:
抗原像性(单向性)给定一个散列,计算上无法找到或者构造出生成它的消息。即不能还原,MD5即是一种单项加密,因此,经常用于密码加密,实现即使管理员也无法知道用户的密码的功能。
抗第二原像性(弱抗碰撞性)给定一条消息和它的散列,计算上无法找到一条不同的消息具有相同的散列。
强抗碰撞性 计算上无法找到两条散列相同的消息。
6、数字签名
在通过散列函数来验证消息完整性的时候,仅仅在信息和数据的散列分开传输的时候才可以,否则中间人可以修改数据的同时修改散列,从而避开检测。数字签名主要是验证数据的真伪。微信通过对称加密生成的签名,支付宝通过非对称加密生成签名。效果差别不大。只要足够证明自己的身份即可。
这里着重介绍下RSA,RSA是首个适用以签名作为加密的算法。被用于银行网上支付、电商交易。
RSA是Rivest、Shamir、Adleman三位数学家的缩写。其数学原理是大整数因数分解极其苦难的原因设计的一种算法。
RSA算法优点
不需要进行密钥传递,提高了安全性
可以进行数字签名认证
RSA算法缺点
加密解密效率不高,一般只适用于处理小量数据(如:密钥)
容易遭受小指数攻击
非加密算法实现流程图:
在此可以看到,非对称加密是通过两个密钥(公钥-私钥)来实现对数据的加密和解密的。公钥用于加密,私钥用于解密。
下面做一个简单的登录功能来测试示例
前端:这里用的是 jsencrypt.js
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<div>打开控制台查看</div>
</body>
<!--jquery cdn-->
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<!--引入jsencrypt.js cdn-->
<script src="https://cdn.bootcss.com/jsencrypt/3.0.0-beta.1/jsencrypt.js"></script>
<script type="text/javascript">
var publicKey = '公钥串';
console.log(encrypt(publicKey, 'HelloWord'))
// RSA前端加密
function encrypt(key, oldPwd) {
let encrypt = new JSEncrypt();
encrypt.setPublicKey(key);
let encrypted = encrypt.encrypt(oldPwd);
return encrypted;
}
</script>
</html>
获取表单值并请求接口:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="doLogin">
<!--用户名-->
<input type="text" name="username" required="" autofocus="">
<!--密码-->
<input type="password" name="password" required="">
<button type="button" id="bt">登录</button>
<a th:href="@{/AddPage}">注册</a>
</form>
</body>
<!--jquery cdn-->
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<!--引入jsencrypt.js cdn-->
<script src="https://cdn.bootcss.com/jsencrypt/3.0.0-beta.1/jsencrypt.js"></script>
<script type="text/javascript">
var publicKey = '公钥串';
// RSA前端加密
function encrypt(key, oldPwd) {
let encrypt = new JSEncrypt();
encrypt.setPublicKey(key);
let encrypted = encrypt.encrypt(oldPwd);
return encrypted;
}
$("#bt").click(function () {
let data = $("#doLogin").serializeArray();
console.log(data)
var obj = {}
data.forEach((item) => {
obj[item.name] = item.value
})
console.log(obj)
$.ajax({
url: '/app/v1/checkSign',
type: 'get',
data: getSign(obj),
dataType: 'json',
success: function (res) {
console.log(res)
}
})
})
function getSign(params) {
var signParam = "";
var bandParam = publicKey;
params["signTime"] = (new Date().getTime() / 1000).toFixed(0);
var sign = '';
var newData = Object.keys(params).sort();
for (var i = 0; i < newData.length; i++) {
if (
Object.prototype.toString.call(params[newData[i]]) !==
"[object Array]" &&
Object.prototype.toString.call(params[newData[i]]) !== "[object Object]"
) {
if (newData[i] !== "sign") {
sign += newData[i] + params[newData[i]];
}
}
}
signParam = encrypt(publicKey, sign);
params["sign"] = signParam;
return params;
}
</script>
</html>
也可以在页面放一个隐藏域,用于存放公钥:
<!--也可以页面放置一个隐藏域标签,用于存放公钥-->
<input type="hidden" th:value="${session.publicKey}" id="publicKey">