一、非对称加密
非对称加密又称为公钥密码,该技术是针对私钥密码体制(对称加密算法)的缺陷被提出来的,非对称加密会产生两把密钥,分别为公钥(Public Key)和私钥(Private Key),其中一把密钥用于加密,另一把密钥用于解密。非对称加密的特征是算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了。这样安全性就高了很多。
非对称密钥加密技术,由公钥和私钥形成一个密钥对,其中公钥向公众公开,私钥归密钥持有人单独保管。通讯双方使用非对称密钥对数据进行加密和解密时,必须使用相互匹配的公钥和私钥。它有两种方式:一种是发送方用接收方的公钥来加密信息,接收方用其私钥解密信息,这样接收方可以收到多个发送方传来的加密数据,且此加密数据只有接收方一个用户可以解读;另一种即发送方利用自身的私钥加密信息,接收方用对方公钥解密信息,这样一个信息有可能被多个接收方解密。 非对称密钥加密技术的优点是简化了密钥的发放及管理的过程,支持数字签名等安全认证技术,缺点是加密和解密的计算过程特别复杂,运行数据加密和解密的速度比较慢。
非对称加密有公钥和私钥两个概念,私钥自己拥有,不能给别人,公钥公开。根据应用的不同,我们可以选择使用不同的密钥加密:
- 数字签名(签名验签):私钥签名,公钥验证。验证内容防篡改。但内容会泄露。
- 加密解密:公钥加密,私钥解密。内容防泄漏。但可能被篡改。
私钥能解密,公钥只能验证
常用的非对称加密算法有:
- RSA算法
- DSA算法
- ECC算法
- DH算法
小明和下红经过非对称加密能解决他们通信的安全问题:
1、小明确定了自己的私钥 mPrivateKey,公钥 mPublicKey。自己保留私钥,将公钥mPublicKey发给了小红
2、小红确定了自己的私钥 hPrivateKey,公钥 hPublicKey。自己保留私钥,将公钥 hPublicKey 发给了小明
3、小明发送信息 “周六早10点soho T1楼下见”,并且用小红的公钥 hPublicKey 进行加密。
4、小红收到信息后用自己的私钥 hPrivateKey 进行解密。然后回复 “收到,不要迟到” 并用小明的公钥mPublicKey加密。
5、小明收到信息后用自己的私钥 mPrivateKey 进行解密。读取信息后心里暗想:还提醒我不迟到?每次迟到的都是你吧?
以上过程是一次完整的request和response。通过这个例子我们梳理出一次信息传输的非对称加、解密过程:
1、消息接收方准备好公钥和私钥
2、私钥接收方自己留存、公钥发布给消息发送方
3、消息发送方使用接收方公钥对消息进行加密
4、消息接收方用自己的私钥对消息解密
公钥只能用做数据加密。公钥加密的数据,只能用对应的私钥才能解密。这是非对称加密的核心概念。
1、RSA算法
RSA rsa = new RSA();
//获得私钥
rsa.getPrivateKey();
rsa.getPrivateKeyBase64();
//获得公钥
rsa.getPublicKey();
rsa.getPublicKeyBase64();
//公钥加密,私钥解密
byte[] encrypt = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
System.out.println("解密结果:" + StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
//私钥加密,公钥解密
byte[] encrypt2 = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey);
System.out.println("解密结果:" + StrUtil.str(decrypt2, CharsetUtil.CHARSET_UTF_8));
// 生成公钥私钥
KeyPair pair = SecureUtil.generateKeyPair("RSA");
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
// 将生成的公钥私钥byte[]转化为String
String privateKeyStr = Base64.encode(privateKey.getEncoded());
String publicKeyStr = Base64.encode(publicKey.getEncoded());
System.out.println("私钥->" + privateKeyStr);
System.out.println("公钥->" + publicKeyStr);
String data = "测试报文123456789";
//公钥加密
RSA publicRsa = new RSA(null, publicKeyStr);
String encryptBase64 = publicRsa.encryptBase64(data, KeyType.PublicKey);
System.out.println("传输密文->" + encryptBase64);
//私钥解密
RSA privateRsa = new RSA(privateKeyStr, null);
String decryptStr = privateRsa.decryptStr(encryptBase64, KeyType.PrivateKey);
System.out.println("解密->" + decryptStr);
二、数字签名
数字签名,简单来说就是通过提供 可鉴别 的 数字信息 验证 自身身份 的一种方式。一套 数字签名 通常定义两种 互补 的运算,一个用于 签名,另一个用于 验证。分别由 发送者 持有能够 代表自己身份 的 私钥 (私钥不可泄露),由 接受者 持有与私钥对应的 公钥 ,能够在 接受 到来自发送者信息时用于 验证 其身份。
签名 最根本的用途是要能够唯一 证明发送方的身份,防止 中间人攻击、CSRF
跨域身份伪造。基于这一点在诸如 设备认证、用户认证、第三方认证 等认证体系中都会使用到 签名算法 (彼此的实现方式可能会有差异)。
数字签名是非对称密钥加密技术与数字摘要技术的应用。
简单说,非对称加密可以防止数据被破解,数字签名可以防止数据被篡改。
数字签名实现的具体原理:
1、 将报文按双方约定的HASH算法计算得到一个固定位数的报文摘要。在数学上保证,只要改动报文中任何一位,重新计算出的报文摘要值就会与原先的值不相符。这样就保证了报文的不可更改性。
2、 将该报文摘要值用发送者的私人密钥加密,然后连同原报文和数字证书(包含公钥)一起发送给接收者而产生的报文即称数字签名。
3、接收方收到数字签名后,用同样的HASH算法对报文计算摘要值,然后与用发送者的公开密钥进行解密解开的报文摘要值相比较,如相等则说明报文确实来自所称的发送者。
4、同时通过证书颁发机构CA确认证书的有效性即可确认发送的真实身份。
byte[] data = "我是一段测试字符串".getBytes();
// 生成公钥私钥
KeyPair pair = SecureUtil.generateKeyPair("RSA");
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
// 将生成的公钥私钥byte[]转化为String
String privateKeyStr = Base64.encode(privateKey.getEncoded());
String publicKeyStr = Base64.encode(publicKey.getEncoded());
System.out.println("私钥->" + privateKeyStr);
System.out.println("公钥->" + publicKeyStr);
//签名: 私钥加密
Sign encodeSign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKeyStr, null);
byte[] signed = encodeSign.sign(data);
String encodeData = Base64.encode(signed);
System.out.println("签名->" + encodeData);
//验证签名: 公钥解密
Sign decodeSign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, null, publicKeyStr);
boolean verify = decodeSign.verify(data, Base64.decode(encodeData));
System.out.println("验证结果->" + verify);
三、对称加密
对称加密又称但密钥加密,整个加密过程中只使用一个密钥。所谓对称其实就是使用一把密钥加密,使用同一把密钥解密。对称加密由于加解和解密使用的是同一个密钥算法,故而在加解密的过程中速度比较快,适合于数据量比较大的加解密。
对称加密的主要有优点就是算法公开、计算量小、加密速度快、加密效率高;但是它也存在强大的缺点,缺点就是密钥协商过程中,一旦密钥泄露,别人可以获取到密钥,这样也能对密文进行解密。另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的独一密钥,这会使得收、发双方所拥有的钥匙数量巨大,密钥管理成为双方的负担。
常用的对称加密算法有 DES、3DES、AES、TDEA、Blowfish、RC2、RC4 和 RC5 等。
四、摘要加密
摘要算法是一种能产生特殊输出格式的算法,这种算法的特点是:无论用户输入什么长度的原始数据,经过计算后输出的密文都是固定长度的,这种算法的原理是根据一定的运算规则对原数据进行某种形式的提取,这种提取就是摘要,被摘要的数据内容与原数据有密切联系,只要原数据稍有改变,输出的“摘要”便完全不同,因此,基于这种原理的算法便能对数据完整性提供较为健全的保障。
但是,由于输出的密文是提取原数据经过处理的定长值,所以它已经不能还原为原数据,即消息摘要算法是不可逆的,理论上无法通过反向运算取得原数据内容,因此它通常只能被用来做数据完整性验证。
哈希算法主要用来保障数据真实性(即完整性),即发信人将原始消息和哈希值一起发送,收信人通过相同的哈希函数来校验原始数据是否真实。
哈希算法通常有以下几个特点:
- 正像快速:原始数据可以快速计算出哈希值
- 逆向困难:通过哈希值基本不可能推导出原始数据
- 输入敏感:原始数据只要有一点变动,得到的哈希值差别很大
- 冲突避免:很难找到不同的原始数据得到相同的哈希值
案例:百度云文件秒传功能,就是利用了 MD5 摘要加密算法,在用户上传文件的时候,对文件进行 MD5 加密计算出摘要,再到数据库中查询这个摘要是否存在,如果存在用户就可以不用上传文件,直接返回已存在文件的地址即可,就是利用了摘要加密不同数据的计算的结果是不同的特性
常见的摘要加密算法:
- MD2
- MD5
- SHA-1
- SHA-256
- SHA-384
- SHA-512
下面是通过 Hutool 工具实现的摘要算法的案例
// MD5
String s1 = DigestUtil.md5Hex("123456");
String s2 = DigestUtil.md5Hex("12345");
System.out.println(s1);
System.out.println(s2);
// sha256
String s3 = DigestUtil.sha256Hex("123456");
String s4 = DigestUtil.sha256Hex("12345");
System.out.println(s3);
System.out.println(s4);
关于md5的简介
md5是一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(哈希值),用于确保信息传输完整一致。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。对于任意长度的消息,这个摘要相当于是个长度为16个字节的数组,通常用一个长度为32的十六进制字符串来表示。
关于sha256的简介
sha256算法也是一种密码散列函数,对于任意长度的消息,SHA256都会产生一个256bit长的散列值(哈希值),用于确保信息传输完整一致,称作消息摘要。这个摘要相当于是个长度为32个字节的数组,通常用一个长度为64的十六进制字符串来表示。
md5和sha256算法的区别
相同点:
1、都是密码散列函数,加密不可逆。
2、都可以实现对任意长度对象加密,都不能防止碰撞。
安全性方面:
1、SHA256(⼜称SHA2)的安全性最⾼,但是耗时要⽐其他两种多很多。
2、md5相对来说比较容易碰撞,安全性没这么高。
性能方面:
以⼀个60M的⽂件为测试样本,经过1000次的测试平均值,这两种算法的表现如下:
MD5算法运⾏1000次的平均时间为:226ms
SHA256算法运⾏1000次的平均时间为:473ms
总而言之,md5和sha256都是密码散列函数,加密不可逆。虽然都不能防止碰撞,但是相对而言,md5比较容易碰撞,安全性没有sha256高。
- MD4 1990年 输出128位 (已经不安全)
- MD5 1991年 输出128位 (已经不安全)
- SHA-0 1993年 输出160位 (发布之后很快就被NSA撤回,是SHA-1的前身)
- SHA-1 1995年 输出160位 (已经不安全)
- SHA-2包括SHA-224、SHA-256、SHA-384,和 SHA-512,分别输出224、256、384、512位。 (目前安全)
五、接口加签
此处模仿XX宝的加签方式:https://opendocs.alipay.com/common/02kf5p?pathHash=81acb065
自行加签:https://opendocs.alipay.com/common/057k53?pathHash=7b14a0af
自行验签:https://opendocs.alipay.com/common/02mse7?pathHash=096e611e
商家在应用中使用自己的 私钥 对消息加签之后,消息和签名会传递给支付宝,支付宝则使用应用的 公钥/公钥证书 验证消息的真实性(来自于合法应用的真实消息)。
对于支付宝返回消息给商家应用的情形,应用则使用支付宝的 支付宝公钥 或 支付宝公钥证书及支付宝根证书 等来验证返回消息的真实性。
签名过程:生成签名方(通常为商家)首先将所有参数和值放入一个map 中,并按照 key 值升序排列(调用Collections.sort
方法)。然后将所有参数拼接起来,去掉 key 或 value 为空的参数,并用 &
连接,组成签名原文。最后使用 RSA 的私钥对签名原文进行签名。
调用签名函数 后需将生成的签名作为 sign
的 value
拼接到请求数据中。