HTTP接口是互联网各系统之间对接的重要方式之一,使用HTTP接口开发和调用都很方便,也是被大量采用的方式,它可以让不同系统之间实现数据的交换和共享。
由于HTTP接口开放在互联网上,所以我们就需要有一定的安全措施来保证接口安全。
个人觉得安全措施大体来看主要在两个方面:
HTTP API接口安全性演进如下
用户身份认证的流程图如下:
具体说明如下:
1、 用户登录时,客户端请求接口,传入用户名和密文的密码
2、 后台服务对用户身份进行验证。若验证失败,则返回错误结果;若验证通过,则生成一个随机不重复的token,并将其存储在redis中,设置一个过期时间。
3、 用户身份校验通过后,后台服务将生成的token返回客户端。
4、客户端请求后续其他接口时,需要带上这个token。后台服务会统一拦截接口请求,进行token有效性校验,并从中获取用户信息,供后续业务逻辑使用
现在主流的加密方式有对称加密和非对称加密
两种方式各有优缺点,而https的实现方式正好是结合了两种加密方式,整合了双方的优点,在安全和性能方面都比较好;
常见的签名方式实现一般分为以下几个步骤 :
1 . 将所有(或者特殊)请求参数按特定规则排序;在 Java 中可以使用 TreeMap 进行排序。
2 . 将请求参数按特定规则拼装为加密字符串;
3 . 加密算法对加密字符串进行加密,得到签名。
str:参数1={参数1}&参数2={参数2}&……&参数n={参数n}$key={用户密钥};
MD5.encrypt(str);
sign机制可以防止参数被篡改,但无法防dos攻击(第三方使用正确的参数,不停请求服务器,使之无法正常提供服务)。因此,还需要引入时间戳机制。
具体的操作为:客户端在形成sign值时,除了使用所有参数和token外,再加一个发起请求时的时间戳。即
sign值来源 = 所有非空参数升序排序+token+timestamp
后端则需要根据当前时间和sign值的时间戳进行比较,差值超过一段时间则不予放行。
long interval=5*60*1000;//超时时间
long clientTime=request.getparameter("clientTime");
long serverTime=System.currentTimeMillis();
if(serverTime-clientTime>interval){
return new Response("超过处理时长")
}
接口层对参数进行合法校验,只有在数据是合法的情况下才会进行数据处理。
对应的对外提供的接口,并不是谁都可以调用,需要使用接口的用户需要在后台开通appid,提供给用户相关的密钥;在调用的接口中需要提供 appid+密钥,服务器端会进行相关的验证;
常用的限流算法有令牌桶和漏桶算法。
对于一些重要的操作需要防止客户端重复提交的(如非幂等性重要操作),具体办法是当请求第一次提交时将sign作为key保存到redis,并设置超时时间,超时时间和Timestamp中设置的差值相同。
当同一个请求第二次访问时会先检测redis是否存在该sign,如果存在则证明重复提交了,接口就不再继续调用了。如果sign在缓存服务器中因过期时间到了,而被删除了,此时当这个url再次请求服务器时,因token的过期时间和sign的过期时间一直,sign过期也意味着token过期,那样同样的url再访问服务器会因token错误会被拦截掉,这就是为什么sign和token的过期时间要保持一致的原因。拒绝重复调用机制确保URL被别人截获了也无法使用(如抓取数据)。
在客户端与服务端请求交互的过程中,请求的数据容易被拦截并篡改,比如在支付场景中,请求支付金额为 1 元,被拦截后篡改为 100 元,由于没有防篡改校验,导致多支付了金钱,造成了用户损失。因此我们在接口设计时必须考虑防篡改校验,加签、验签就是用来解决这个问题的。加签、验签是用来解决防篡改问题的。
什么是公钥私钥
如果用到的是非对称加密,那么你和第三方之间就有两对公私钥,各自持有对方的公钥和自己的私钥
签名主要包含摘要和非对称加密两部分内容,首先对需要签名的数据进行摘要计算得到摘要值,然后通过签名者的私钥对摘要值进行非对称加密即可得到签名结果。
一般情况下,发送请求时,会将数据和数字签名一起打包发送给接收方。
互联网网上的这个图,更容易理解一点:
用该用户的公钥加密后只能该用户的私钥才能解密。这种情况下,公钥是用来加密信息的,确保只有特定的人(用谁的公钥就是谁)才能解密该信息。所以这种我们称之为加密和解密。
一、发送方(RSAwithSHA、RSAwithMD5):
1.对传输的报文进行摘要,主要的算法有MD5和SHA
2.对摘要用自己的私钥进行加密生成签名,一般用到的算法如RS
3.传输报文及签名
二、接收方:
1.对接收到的报文用同样的算法进行摘要
2.用发送方的公钥对发送方的签名进行解密得到发送方的摘要
3.对比两份摘要看是否有不同以验证是否被篡改
数据摘要算法是密码学算法中非常重要的一个分支,它通过对所有数据提取指纹信息以实现数据签名、数据完整性校验等功能,由于其不可逆性,有时候会被用做敏感信息的加密。数据摘要算法也被称为哈希(Hash)算法或散列算法。
消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。(摘要可以比方为指纹,消息摘要算法就是要得到文件的唯一职位)
消息摘要算法主要分三类:MD(Message Digest,消息摘要算法)、SHA(Secure Hash Algorithm,安全散列算法)和MAC(Message Authentication Code,消息认证码算法)。
MD(Message Digest,消息摘要算法)家族,包括MD2,MD4,MD5。
举个例子,看看如何获取字符串的MD5值吧:
public class MD5Test {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "123";
byte[] result = getMD5Bytes(s.getBytes());
StringBuilder stringBuilder = new StringBuilder();
for (byte temp : result) {
if (temp >= 0 && temp < 16) {
stringBuilder.append("0");
}
stringBuilder.append(Integer.toHexString(temp & 0xff));
}
System.out.println(s + ",MD5加密后:" + stringBuilder.toString());
}
private static byte[] getMD5Bytes(byte[] content) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
return md5.digest(content);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
运行结果:
123,MD5加密后:202cb962ac59075b964b07152d234b70
SHA(Secure Hash Algorithm,安全散列算法),包括SHA-0、SHA-1、SHA-2(SHA-256,SHA-512,SHA-224,SHA-384等)、SHA-3。它是在MD算法基础上实现的,与MD算法区别在于**「摘要长度」,SHA 算法的摘要「长度更长,安全性更高」**。
❝
- SHA-0发布之后很快就被NSA撤回,因为含有会降低密码安全性的错误,它是SHA-1的前身。
- SHA-1在许多安全协议中广为使用,包括TLS、GnuPG、SSH、S/MIME和IPsec,是MD5的后继者。
- SHA-2包括SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。它的算法跟SHA-1基本上相似,目前还没有出现明显弱点。
- SHA-3是2015年正式发布,由于对**「MD5出现成功的攻破」**,以及对SHA-0和SHA-1出现理论上攻破的方法,SHA-3应运而生。它与之前算法不同的是,它是可替换的加密散列算法。
❞
SHA-1、SHA-2(SHA-256,SHA-512,SHA-224,SHA-384)等算法是比较常用的,我们来看看跟MD5的对比吧
算法类型 | 摘要长度(bits) | 最大输入消息长度(bits) | 碰撞攻击(bits) | 性能示例(MiB/s) |
---|---|---|---|---|
MD5 | 128 | 无限 | ≤18(发现碰撞) | 335 |
SHA-1 | 160 | 2^64 − 1 | <63(发现碰撞) | 192 |
SHA-224 | 224 | 2^64 − 1 | 112 | 139 |
SHA-256 | 256 | 2^64 − 1 | 128 | 139 |
SHA-384 | 384 | 2^128 − 1 | 192 | 154 |
SHA-512 | 512 | 2^128 − 1 | 256 | 154 |
try {
// 生成一个MD5加密计算摘要
MessageDigest md = MessageDigest.getInstance("MD5");
// 计算md5函数
md.update(str.getBytes());
// digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
return new BigInteger(1, md .digest()).toString(16);
} catch (Exception e) {
throw new Exception("MD5加密出现错误,"+e.toString());
}
}
常用的签名算法:
MAC算法 MAC(Message Authentication Code,消息认证码算法),是带密钥的Hash函数。输入密钥和消息,输出一个消息摘要。 它集合了MD和SHA两大系列消息摘要算法。
加密和解密使用**「相同密钥」**的加密算法就是对称加密算法。常见的对称加密算法有AES、3DES、DES、RC5、RC6等。
数据加密标准(英语:Data Encryption Standard,缩写为 DES)是一种对称密钥加密块密码算法。 DES算法的入口参数有三个:Key、Data、Mode。
三重数据加密算法(英语:Triple Data Encryption Algorithm,又称3DES(Triple DES),是一种对称密钥加密块密码,相当于是对每个数据块应用三次数据加密标准(DES)算法。
AES,高级加密标准(英语:Advanced Encryption Standard),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
非对称加密算法需要两个密钥:公钥和私钥。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有用对应的私钥才能解密。主要的非对称加密算法有:RSA、Elgamal、DSA、D-H、ECC。
国密即国家密码局认定的国产密码算法。为了保障商用密码的安全性,国家商用密码管理办公室制定了一系列密码标准,即SM1,SM2,SM3,SM4等国密算法。
下面使用用的是SHA-256作为摘要算法,RSA作为签名验签算法,如下:
package com.demo.util;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
public class RSAUtil {
private static final String signature_algorithm = "SHA256withRSA"; // 签名算法
private static final String encryptAlgorithm = "RSA"; // 加密算法
private static final String decryptAlgorithm = "RSA"; // 解密算法
private static final String charset = "UTF-8";
private static final int max_encrypt_block = 234; //2048位rsa单次最大加密长度
private static final int max_decrypt_block = 256; //2048位rsa单次最大解密长度
private static PublicKey sign_public_key = null;
private static PrivateKey sign_private_key = null;
private static PublicKey crypt_public_key = null;
private static PrivateKey crypt_private_key = null;
static {
// 静态加载,提高效率,但是配置的修改都需要重启server才能生效
// 对方base64后公钥字符串,用于验签
String sign_pub_key_str = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnBYMjGWVjlWk3u7aXDtKmGghWV5glVTYFQ3ijelj3DwHB71jZQDKMnPkCeHMJk6iMJ04r+akLbxiaMIhF/qx/RYfTvgrM9Y3ZNiK0PYTS2W8Iw2wjKDjOKO21N+R4jl/PugOYDi6Ru6dmQjKPeGiDtJAPvqa7tyUyBrB0F4X/h+FX+M+mTJ7TsBV+4cTgzJy5iz9nntS6ccMUJ92xBimqLwDUTDeRtm1+a2x3sIAOstCFOBsqyCXFWmOpElvGQcsMXvgw0DvNMG+M+pOEkqZoTt47Pp1FweOBqT6gT/q9xbAqFxTMUCI0XZan0mJ7MYBqA4ySqxg9J/uz4p+pOEshwIDAQAB";
// 己方base64后私钥字符串,用于签名
String sign_pri_key_str = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCcFgyMZZWOVaTe7tpcO0qYaCFZXmCVVNgVDeKN6WPcPAcHvWNlAMoyc+QJ4cwmTqIwnTiv5qQtvGJowiEX+rH9Fh9O+Csz1jdk2IrQ9hNLZbwjDbCMoOM4o7bU35HiOX8+6A5gOLpG7p2ZCMo94aIO0kA++pru3JTIGsHQXhf+H4Vf4z6ZMntOwFX7hxODMnLmLP2ee1LpxwxQn3bEGKaovANRMN5G2bX5rbHewgA6y0IU4GyrIJcVaY6kSW8ZBywxe+DDQO80wb4z6k4SSpmhO3js+nUXB44GpPqBP+r3FsCoXFMxQIjRdlqfSYnsxgGoDjJKrGD0n+7Pin6k4SyHAgMBAAECggEASx1lTo94iLX4kPybgzVZcbzzB6Iekt7w2jkDZU4DK7KLo5Ll6W6W3+7buFG8wFapQQH4jNZO/l+hcE60RGj2DRj/Wi6eA+U8ZUC8lVFG+crs3mWxKAwpLVHEI++vshH/hZOBj5bdNlOQ7lvHkD4skjtmhahutTLcOux9hzwxCa76ZPeeznN/J+HI/CjI3J2JGRPDZmbcnezVdnJbQYY8K1YeuKIRFGkTh/sGIR1n3PDdxFPQSPv5VA/Ykd2IHXKTVoN+huozzxGIKbEThIvlBZo9CgXQ9+AnZRELZRLag+M+UPUhbeeqpaTQYXMPAmziEN24TU9DfkhUl9rD3vO0CQKBgQDcBfxcw0awDwRTVbWZBKc01vn+DhbWmt8GoJ2cITLvnR2FW8KgOFFMhCUjjmOgSBhGZnk8aKhMC1qQEd9e0VrmQ+0+IG0q8Q+lNf67CyHebVHBarcHcct68lyIRU498LfjUnbs3kswDrdR4+zzEDMpQwYq6/TYKY513iiWwlygQwKBgQC1m7KYPzYj3+wveErRgS2+6zngqVmXriWT8VFMAaqqs/hYlQ1Ho5U2e/tcZts4t1FfCgGPPM9vGkAc7Gn/sn6LSVQet1+VRfc2IT5AcPOutwP8c7PpsOGoZYWWXJadhg1JqZo3rxGOhEQ1XWFXk/wX9d0Sllyu35sD0GRusXRQbQKBgBfIXdr5EK7/MIyBezurERfZFPStOTLBUtI4klDKFeNorEQ6AvOmosMOlaUeQw6UPGt/sCMjfO2bXJKuG+L35kd1mDNa9fHqVLKa/4ngTizozCmIC3i2iDQl9nKUazyuxHHB/DDmZmIvdQlZBcfQPHd9UzFYiALFmyyKcwC4yaJZAoGAVWHqSaIOdjdk97x6kJ1HQKefAn0cXi/GAxRFwJJYBwGuFReesru5/2+y8fJ5xuSJIUG3EfzpGbchxXdxLoJg9GN5ZSeZjLjkTVK7zdhM+SuaeCp9v7UlouJ4OAU32r+Xp7ZRhzSL8JFG8EAC8AXnU+yID6EZ2i3O17A2R8SuhtECgYBT/D3FZxSDvfNC3GFIRfIvsXMtAuM4MBvT6Z9ucyYluVORkEXcSRT3PcCTusUUEzN6gk2tgzhhHYY5O23LC+svFEy/nE8nkcDsqupvzLEces0OwRfUIj2KrrHkGGjN+a5ioxu5ieO1VxoLu5HjP8shm/Oypfnk9A6x86Av3L3ScA==";
// 对方base64后公钥字符串,用于加密
String crypt_pub_key_str = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnBYMjGWVjlWk3u7aXDtKmGghWV5glVTYFQ3ijelj3DwHB71jZQDKMnPkCeHMJk6iMJ04r+akLbxiaMIhF/qx/RYfTvgrM9Y3ZNiK0PYTS2W8Iw2wjKDjOKO21N+R4jl/PugOYDi6Ru6dmQjKPeGiDtJAPvqa7tyUyBrB0F4X/h+FX+M+mTJ7TsBV+4cTgzJy5iz9nntS6ccMUJ92xBimqLwDUTDeRtm1+a2x3sIAOstCFOBsqyCXFWmOpElvGQcsMXvgw0DvNMG+M+pOEkqZoTt47Pp1FweOBqT6gT/q9xbAqFxTMUCI0XZan0mJ7MYBqA4ySqxg9J/uz4p+pOEshwIDAQAB";
// 己方base64后私钥字符串,用于解密
String crypt_pri_key_str = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCcFgyMZZWOVaTe7tpcO0qYaCFZXmCVVNgVDeKN6WPcPAcHvWNlAMoyc+QJ4cwmTqIwnTiv5qQtvGJowiEX+rH9Fh9O+Csz1jdk2IrQ9hNLZbwjDbCMoOM4o7bU35HiOX8+6A5gOLpG7p2ZCMo94aIO0kA++pru3JTIGsHQXhf+H4Vf4z6ZMntOwFX7hxODMnLmLP2ee1LpxwxQn3bEGKaovANRMN5G2bX5rbHewgA6y0IU4GyrIJcVaY6kSW8ZBywxe+DDQO80wb4z6k4SSpmhO3js+nUXB44GpPqBP+r3FsCoXFMxQIjRdlqfSYnsxgGoDjJKrGD0n+7Pin6k4SyHAgMBAAECggEASx1lTo94iLX4kPybgzVZcbzzB6Iekt7w2jkDZU4DK7KLo5Ll6W6W3+7buFG8wFapQQH4jNZO/l+hcE60RGj2DRj/Wi6eA+U8ZUC8lVFG+crs3mWxKAwpLVHEI++vshH/hZOBj5bdNlOQ7lvHkD4skjtmhahutTLcOux9hzwxCa76ZPeeznN/J+HI/CjI3J2JGRPDZmbcnezVdnJbQYY8K1YeuKIRFGkTh/sGIR1n3PDdxFPQSPv5VA/Ykd2IHXKTVoN+huozzxGIKbEThIvlBZo9CgXQ9+AnZRELZRLag+M+UPUhbeeqpaTQYXMPAmziEN24TU9DfkhUl9rD3vO0CQKBgQDcBfxcw0awDwRTVbWZBKc01vn+DhbWmt8GoJ2cITLvnR2FW8KgOFFMhCUjjmOgSBhGZnk8aKhMC1qQEd9e0VrmQ+0+IG0q8Q+lNf67CyHebVHBarcHcct68lyIRU498LfjUnbs3kswDrdR4+zzEDMpQwYq6/TYKY513iiWwlygQwKBgQC1m7KYPzYj3+wveErRgS2+6zngqVmXriWT8VFMAaqqs/hYlQ1Ho5U2e/tcZts4t1FfCgGPPM9vGkAc7Gn/sn6LSVQet1+VRfc2IT5AcPOutwP8c7PpsOGoZYWWXJadhg1JqZo3rxGOhEQ1XWFXk/wX9d0Sllyu35sD0GRusXRQbQKBgBfIXdr5EK7/MIyBezurERfZFPStOTLBUtI4klDKFeNorEQ6AvOmosMOlaUeQw6UPGt/sCMjfO2bXJKuG+L35kd1mDNa9fHqVLKa/4ngTizozCmIC3i2iDQl9nKUazyuxHHB/DDmZmIvdQlZBcfQPHd9UzFYiALFmyyKcwC4yaJZAoGAVWHqSaIOdjdk97x6kJ1HQKefAn0cXi/GAxRFwJJYBwGuFReesru5/2+y8fJ5xuSJIUG3EfzpGbchxXdxLoJg9GN5ZSeZjLjkTVK7zdhM+SuaeCp9v7UlouJ4OAU32r+Xp7ZRhzSL8JFG8EAC8AXnU+yID6EZ2i3O17A2R8SuhtECgYBT/D3FZxSDvfNC3GFIRfIvsXMtAuM4MBvT6Z9ucyYluVORkEXcSRT3PcCTusUUEzN6gk2tgzhhHYY5O23LC+svFEy/nE8nkcDsqupvzLEces0OwRfUIj2KrrHkGGjN+a5ioxu5ieO1VxoLu5HjP8shm/Oypfnk9A6x86Av3L3ScA==";
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
sign_public_key = keyFactory.generatePublic(new X509EncodedKeySpec(Base64Utils.decodeFromString(sign_pub_key_str))); // 用于验签
sign_private_key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64Utils.decodeFromString(sign_pri_key_str))); // 用于签名
crypt_public_key = keyFactory.generatePublic(new X509EncodedKeySpec(Base64Utils.decodeFromString(crypt_pub_key_str))); // 用于加密
crypt_private_key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64Utils.decodeFromString(crypt_pri_key_str))); // 用于解密
} catch (Exception e) {
//日志记录
}
}
/**
* 签名
* @param param
* @return
*/
public static String sign(String param) {
try {
if (sign_private_key == null) {
throw new RuntimeException("私钥未初始化");
}
Signature signature = Signature.getInstance(signature_algorithm);
signature.initSign(sign_private_key);
signature.update(param.getBytes(charset));
return Base64Utils.encodeToString(signature.sign());
} catch (Exception e) {
throw new RuntimeException("签名异常", e);
}
}
/**
* 加密
* @param param
* @return
*/
public static String encrypt(String param) {
if (crypt_public_key == null) {
throw new RuntimeException("公钥未初始化");
}
if (StringUtils.isEmpty(param)) {
throw new IllegalArgumentException("待加密数据为空");
}
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
Cipher cipher = Cipher.getInstance(encryptAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, crypt_public_key);
byte[] data = param.getBytes(charset);
int inputLen = data.length;
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen > max_encrypt_block + offSet) {
cache = cipher.doFinal(data, offSet, max_encrypt_block);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * max_encrypt_block;
}
return Base64Utils.encodeToString(out.toByteArray());
} catch (Exception e) {
throw new RuntimeException("加密异常", e);
}
}
/**
* 验签
* @param param
* @param sign
* @return
*/
public static boolean veriSign(String param, String sign) {
try {
if (sign_public_key == null) {
throw new RuntimeException("公钥未初始化");
}
Signature signature = Signature.getInstance(signature_algorithm);
signature.initVerify(sign_public_key);
signature.update(param.getBytes(charset));
return signature.verify(Base64Utils.decodeFromString(sign));
} catch (Exception e) {
throw new RuntimeException("验签失败", e);
}
}
/**
* 解密
* @param param
* @return
*/
public static String decrypt(String param) {
if (crypt_private_key == null) {
throw new RuntimeException("私钥未初始化");
}
if (StringUtils.isEmpty(param)) {
throw new IllegalArgumentException("待解密数据为空");
}
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
Cipher cipher = Cipher.getInstance(decryptAlgorithm);
cipher.init(Cipher.DECRYPT_MODE, crypt_private_key);
byte[] data = Base64Utils.decodeFromString(param);
int inputLen = data.length;
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen > max_decrypt_block + offSet) {
cache = cipher.doFinal(data, offSet, max_decrypt_block);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
i++;
out.write(cache, 0, cache.length);
offSet = i * max_decrypt_block;
}
return new String(out.toByteArray(), charset);
} catch (Exception e) {
throw new RuntimeException("解密处理异常", e);
}
}
public static void main(String[] args){
// generateKey();
// requestDemo();
serverDemo();
}
/**
* 发起请求
*/
private static void requestDemo() {
try {
String param = new JSONObject().fluentPut("name", "张三").toJSONString();
String data = encrypt(param);
String sign = sign(data);
JSONObject jsonObject = new JSONObject().fluentPut("sign", sign).fluentPut("data", data);
System.out.println("加密后:"+jsonObject.toJSONString());
HttpPost httpPost = new HttpPost("url");
httpPost.setEntity(new StringEntity(jsonObject.toJSONString(), "application/json", "utf-8"));
HttpClient httpClient = HttpClientBuilder.create().build();
httpClient.execute(httpPost);
((CloseableHttpClient) httpClient).close();
} catch (IOException ignore) {
}
}
/**
* 服务方
* @return
*/
private static String serverDemo() {
// http获取请求头, 请求体
String requestBody = "{\"data\":\"hlnWYqUGXLgZVPifPrVtEp//4GY6t6GM03m1exbDFhsnYGDyADlOREBDsOHvqgfZ3lPhZCKiva0tK5zgSTfbpXPymQxtOhx5x1PekLxx2G/myK74lErC5Vj5OY5oyI12o07TWbaqENATYldViCGf5pZ+Ms2LgYYLpfdVKoth2Xs80MEU/2RzivGp5f1CJST+aG5Vz4cFXWVqorhG3/9Kt+1d1sSv8VYaM0fREx1TBKzEzTq1G++tEMW/9eRPMj8YY5W2vo1JsdhgfzoAC8m/pdBbQ493ePJzMpdRI/Zt/bbnZVkGw5YYpI7saTrWDxjtpEeMaZ9RHyMWrusacAD1SA==\",\"sign\":\"gIC9PdGO99e4IXDP06LRz4pktULp5nQ50lI3nTL0pi8N9yjG6laODm+eOzTsSbsoSGeGuTaikaqGBTkN2Fmj5923K8jTJ/A0bFCuNlIpVaoTtC6r8S7J/whW63EPJ3JRWWjWZ2QWWWYNMofGLVZ4veps8Q02zyrW0pQh0Sc5TykaOO+rX3eu0wcXF4mep5CLutQnDCVBWBwTn1D3Lfhc42UsuSKvPpteCTOwI2mJ/m8swJg0lMvt/iDVBOSx55ItVEbg3OVmNvki4DTBsGuhy6inPQ5zF4S/SDVqseHL63g6OAb8EX/fW9GleFBuzcrs1zkYF4Gd0fUXm1N/uFsHqQ==\"}";
// 接收请求获取参数
JSONObject requestParams = JSONObject.parseObject(requestBody);
/*取参数及签名*/
String data = requestParams.getString("data");
String sign = requestParams.getString("sign");
/*验签*/
boolean f = veriSign(data, sign);
if (f) { // 验签通过才需要解密,否则认为非法请求
/*解密*/
String dec = decrypt(data);
System.out.println("解密后:"+dec);
/*业务逻辑*/
JSONObject businessResult = doBusiness(dec);
/*加密*/
String responseParam = encrypt(businessResult.toJSONString());
/*签名*/
String responseSign = sign(responseParam);
Map<String, String> response = new HashMap<>(2);
response.put("data", responseParam);
response.put("sign", responseSign);
/*返回*/
return JSONObject.toJSONString(response);
}else {
return null;
}
}
private static JSONObject doBusiness(String dec) {
/*响应结果*/
return new JSONObject();
}
/**
* 密钥生成
*/
private static void generateKey() {
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(encryptAlgorithm);
keyPairGen.initialize(2048);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
System.out.println("privateKey: " + Base64Utils.encodeToString(privateKey.getEncoded()));
System.out.println("publicKey: " + Base64Utils.encodeToString(publicKey.getEncoded()));
} catch (NoSuchAlgorithmException ignore) {
}
}
}