当前位置: 首页 > news >正文

API接口安全设计

简介

HTTP接口是互联网各系统之间对接的重要方式之一,使用HTTP接口开发和调用都很方便,也是被大量采用的方式,它可以让不同系统之间实现数据的交换和共享。
由于HTTP接口开放在互联网上,所以我们就需要有一定的安全措施来保证接口安全。

个人觉得安全措施大体来看主要在两个方面:

  1. 一方面就是如何保证数据在传输过程中的安全性;
  2. 另一个方面是数据已经到达服务器端,服务器端如何识别数据,如何不被攻击;

HTTP API接口安全性演进如下

  1. HTTP + 完全开放 (毫无安全可言)
  2. HTTP + 参数签名 (基本安全)
  3. HTTP + 私钥签名公钥验签 (安全性高)
  4. HTTPS + 参数签名 (安全性更高)
  5. HTTPS + 私钥签名公钥验签 (最安全)

使用token进行用户身份认证

用户身份认证的流程图如下:

具体说明如下:

1、 用户登录时,客户端请求接口,传入用户名和密文的密码
2、 后台服务对用户身份进行验证。若验证失败,则返回错误结果;若验证通过,则生成一个随机不重复的token,并将其存储在redis中,设置一个过期时间。
3、 用户身份校验通过后,后台服务将生成的token返回客户端。
4、客户端请求后续其他接口时,需要带上这个token。后台服务会统一拦截接口请求,进行token有效性校验,并从中获取用户信息,供后续业务逻辑使用

数据加密

现在主流的加密方式有对称加密和非对称加密

  1. 对称加密:对称密钥在加密和解密的过程中使用的密钥是相同的,常见的对称加密算法有DES,AES;优点是计算速度快,缺点是在数据传送前,发送方和接收方必须商定好秘钥,然后使双方都能保存好秘钥,如果一方的秘钥被泄露,那么加密信息也就不安全了;
  2. 非对称加密:服务端会生成一对密钥,私钥存放在服务器端,公钥可以发布给任何人使用;优点就是比起对称加密更加安全,但是加解密的速度比对称加密慢太多了;广泛使用的是RSA算法;

两种方式各有优缺点,而https的实现方式正好是结合了两种加密方式,整合了双方的优点,在安全和性能方面都比较好;

数据加签名sign

常见的签名方式实现一般分为以下几个步骤 :

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,提供给用户相关的密钥;在调用的接口中需要提供 appid+密钥,服务器端会进行相关的验证;

限流

常用的限流算法有令牌桶和漏桶算法。

防止重复提交

对于一些重要的操作需要防止客户端重复提交的(如非幂等性重要操作),具体办法是当请求第一次提交时将sign作为key保存到redis,并设置超时时间,超时时间和Timestamp中设置的差值相同。

当同一个请求第二次访问时会先检测redis是否存在该sign,如果存在则证明重复提交了,接口就不再继续调用了。如果sign在缓存服务器中因过期时间到了,而被删除了,此时当这个url再次请求服务器时,因token的过期时间和sign的过期时间一直,sign过期也意味着token过期,那样同样的url再访问服务器会因token错误会被拦截掉,这就是为什么sign和token的过期时间要保持一致的原因。拒绝重复调用机制确保URL被别人截获了也无法使用(如抓取数据)。

接口参数私钥签名公钥验签

在客户端与服务端请求交互的过程中,请求的数据容易被拦截并篡改,比如在支付场景中,请求支付金额为 1 元,被拦截后篡改为 100 元,由于没有防篡改校验,导致多支付了金钱,造成了用户损失。因此我们在接口设计时必须考虑防篡改校验,加签、验签就是用来解决这个问题的。加签、验签是用来解决防篡改问题的。

相关概念

  • 明文:指没有经过加密的信息/数据。
  • 密文:明文被加密算法加密之后,会变成密文,以确保数据安全。
  • 密钥:是一种参数,它是在明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥。
  • 加密:将明文变成密文的过程。
  • 解密:将密文还原为明文的过程。

对称加密、非对称加密

  • 对称加密:加密和解密使用相同密钥的加密算法。 img
  • 非对称加密:非对称加密算法中加密和解密用的不是同一个秘钥,所以叫作非对称加密算法。在非对称加密算法每个用户都有两把钥匙,一把公钥一把私钥。公钥是对外发布的,所有人都看的到所有人的公钥,私钥是自己保存,每个人都只知道自己的私钥而不知道别人的。

img

非对称加密

什么是公钥私钥

  • 公钥与私钥是成对存在的密钥,如果用公钥对数据进行加密,只有用对应的私钥才能解密。
  • 公钥就是公开的秘钥,私钥就是要你自己保存好的秘钥。
  • 非对称加密算法需要有一对公私钥

如果用到的是非对称加密,那么你和第三方之间就有两对公私钥,各自持有对方的公钥和自己的私钥

加签验签

加签

签名主要包含摘要和非对称加密两部分内容,首先对需要签名的数据进行摘要计算得到摘要值,然后通过签名者的私钥对摘要值进行非对称加密即可得到签名结果。

一般情况下,发送请求时,会将数据和数字签名一起打包发送给接收方。

验签
  • 接收方拿到原始报文和数字签名后,用**「同一个Hash函数」**从报文中生成摘要A。另外,用对方提供的公钥对数字签名进行解密,得到摘要B,对比A和B是否相同,就可以得知报文有没有被篡改过。

img

互联网网上的这个图,更容易理解一点:

加密和解密

用该用户的公钥加密后只能该用户的私钥才能解密。这种情况下,公钥是用来加密信息的,确保只有特定的人(用谁的公钥就是谁)才能解密该信息。所以这种我们称之为加密和解密。

一、发送方(RSAwithSHA、RSAwithMD5):
1.对传输的报文进行摘要,主要的算法有MD5和SHA
2.对摘要用自己的私钥进行加密生成签名,一般用到的算法如RS
3.传输报文及签名

二、接收方:
1.对接收到的报文用同样的算法进行摘要
2.用发送方的公钥对发送方的签名进行解密得到发送方的摘要
3.对比两份摘要看是否有不同以验证是否被篡改

常见加密相关算法简介

数据摘要算法是密码学算法中非常重要的一个分支,它通过对所有数据提取指纹信息以实现数据签名、数据完整性校验等功能,由于其不可逆性,有时候会被用做敏感信息的加密。数据摘要算法也被称为哈希(Hash)算法或散列算法。

消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。(摘要可以比方为指纹,消息摘要算法就是要得到文件的唯一职位)

  • 消息摘要算法
  • 对称加密算法
  • 非对称加密算法
  • 国密算法

消息摘要算法

  • 相同的明文数据经过相同的消息摘要算法会得到相同的密文结果值。
  • 数据经过消息摘要算法处理,得到的摘要结果值,是无法还原为处理前的数据的。
  • 数据摘要算法也被称为哈希(Hash)算法或散列算法。
  • 消息摘要算法一般用于签名验签。

消息摘要算法主要分三类:MD(Message Digest,消息摘要算法)、SHA(Secure Hash Algorithm,安全散列算法)和MAC(Message Authentication Code,消息认证码算法)。

MD家族算法

MD(Message Digest,消息摘要算法)家族,包括MD2,MD4,MD5。

  • MD2,MD4,MD5 计算的结果都是是一个128位(即16字节)的散列值,用于确保信息传输完整一致。
  • MD2的算法较慢但相对安全,MD4速度很快,但安全性下降,MD5则比MD4更安全、速度更快。
  • MD5被广泛应用于数据完整性校验、数据(消息)摘要、数据加密等。
  • MD5,可以被攻破,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞攻击,因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。

举个例子,看看如何获取字符串的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家族算法

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)
MD5128无限≤18(发现碰撞)335
SHA-11602^64 − 1<63(发现碰撞)192
SHA-2242242^64 − 1112139
SHA-2562562^64 − 1128139
SHA-3843842^128 − 1192154
SHA-5125122^128 − 1256154
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算法 MAC(Message Authentication Code,消息认证码算法),是带密钥的Hash函数。输入密钥和消息,输出一个消息摘要。 它集合了MD和SHA两大系列消息摘要算法。

  • MD 系列算法: HmacMD2、HmacMD4 和 HmacMD5 ;
  • SHA 系列算法:HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384 和 HmacSHA512 。

对称加密算法

加密和解密使用**「相同密钥」**的加密算法就是对称加密算法。常见的对称加密算法有AES、3DES、DES、RC5、RC6等。

DES

数据加密标准(英语:Data Encryption Standard,缩写为 DES)是一种对称密钥加密块密码算法。 DES算法的入口参数有三个:Key、Data、Mode。

  • Key: 7个字节共56位,是DES算法的工作密钥;
  • Data: 8个字节64位,是要被加密或被解密的数据;
  • Mode: 加密或解密。
3DES

三重数据加密算法(英语:Triple Data Encryption Algorithm,又称3DES(Triple DES),是一种对称密钥加密块密码,相当于是对每个数据块应用三次数据加密标准(DES)算法。

AES

AES,高级加密标准(英语:Advanced Encryption Standard),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。

  • 采用对称分组密码体制,密钥长度为 128 位、 192 位、256 位,分组长度128位
  • 相对于DES ,AES具有更好的 安全性、效率 和 灵活性。

非对称加密算法

非对称加密算法需要两个密钥:公钥和私钥。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有用对应的私钥才能解密。主要的非对称加密算法有:RSA、Elgamal、DSA、D-H、ECC。

RSA算法
  • RSA加密算法是一种非对称加密算法,广泛应用于加密和数字签名
  • RSA算法原理:两个大素数的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
  • RSA是被研究得最广泛的公钥算法,从提出到现在,经历了各种攻击的考验,普遍认为是目前最优秀的公钥方案之一。
DSA
  • DSA(Digital Signature Algorithm,数字签名算法),也是一种非对称加密算法。
  • DSA和RSA区别在,DSA仅用于数字签名,不能用于数据加密解密。其安全性和RSA相当,但其性能要比RSA好。
ECC 算法
  • ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学),基于椭圆曲线加密。
  • Ecc主要优势是,在某些情况下,它比其他的方法使用更小的密钥,比如RSA加密算法,提供相当的或更高等级的安全级别。
  • 它的一个缺点是,加密和解密操作的实现比其他机制时间长 (相比RSA算法,该算法对CPU 消耗严重)。

国密算法

国密即国家密码局认定的国产密码算法。为了保障商用密码的安全性,国家商用密码管理办公室制定了一系列密码标准,即SM1,SM2,SM3,SM4等国密算法。

SM1
  • SM1,为对称加密算法,加密强度为128位,基于硬件实现。
  • SM1的加密强度和性能,与AES相当。
SM2
  • SM2主要包括三部分:签名算法、密钥交换算法、加密算法
  • SM2用于替换RSA加密算法,基于ECC,效率较低。
SM3
  • SM3,即国产消息摘要算法。
  • 适用于商用密码应用中的数字签名和验证,消息认证码的生成与验证以及随机数的生成。
SM4
  • SM4是一个分组算法,用于无线局域网产品。
  • 该算法的分组长度为128比特,密钥长度为128比特。
  • 加密算法与密钥扩展算法都采用32轮非线性迭代结构。
  • 解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。
  • 它的功能类似国际算法的DES。

RSA加签验签代码实现

下面使用用的是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) {}}}

相关文章:

API接口安全设计

简介 HTTP接口是互联网各系统之间对接的重要方式之一&#xff0c;使用HTTP接口开发和调用都很方便&#xff0c;也是被大量采用的方式&#xff0c;它可以让不同系统之间实现数据的交换和共享。 由于HTTP接口开放在互联网上&#xff0c;所以我们就需要有一定的安全措施来保证接口…...

服务器的管理口和业务口

服务器管理接口和业务接口 服务器管理接口&#xff08;Server Management Interface&#xff0c;SMI&#xff09;&#xff1a; 服务器管理接口是一种用于管理服务器硬件和操作系统的标准接口。它通常用于远程管理和监控服务器&#xff0c;包括但不限于以下功能&#xff1a; …...

【gpt redis】原理篇

用的黑马程序员redis课程的目录&#xff0c;但是不想听讲了。后续都是用gpt文档获取的。 1.课程介绍(Av766995956,P145) 2.Redis数据结构-动态字符串(Av766995956,P146) sds 1M是个界限 其实他是个由c语言实现的结构体 有这么几个参数 len alloc flag char[] len是实际长度 …...

python二次开发Solidworks:排雷以及如何排雷?

目录 1、重要文档 2、MSDN VARIANTS 3、错误排除实例 3.1 第一个例子 3.2 第二个例子 1、重要文档 SolidWorks API 帮助文档&#xff1a;在 SolidWorks 的安装路径下可以找到本地文件&#xff0c;如 ...\Program Files\SolidWorks Corp\SolidWorks\api\apihelp.chm 。 s…...

广告引擎检索技术快速学习

目录 一、广告系统与广告引擎介绍 &#xff08;一&#xff09;广告系统与广告粗分 &#xff08;二&#xff09;广告引擎在广告系统中的重要性分析 二、广告引擎整体架构和工作过程 &#xff08;一&#xff09;一般概述 &#xff08;二&#xff09;核心功能架构图 三、标…...

Scala的类和对象

1. 初识类和对象 Scala 的类与 Java 的类具有非常多的相似性&#xff0c;示例如下&#xff1a; // 1. 在 scala 中&#xff0c;类不需要用 public 声明,所有的类都具有公共的可见性 class Person {// 2. 声明私有变量,用 var 修饰的变量默认拥有 getter/setter 属性private var…...

SQL中 <>(不等于)运算符只会匹配那些具有非空值的记录

0. 场景 idflag112null3 一张表的有有个varchar类型的flag字段,字段值有 null值/空值和 1。 flag为 1即表示逻辑删除,我想找出flag字段非 1 的所有情况: 一开始sql写法: select * from table where flag<>‘1’理想情况,结果集应该有2条记录(id为 2 和 3 的记录)实际情…...

冒泡排序(Java)

基本思想 比较前后相邻的二个数据&#xff0c;如果前面数据大于后面的数据&#xff0c;就将这二个数据交换。这样对数组的第 0 个数据到 N-1 个数据进行一次遍历后&#xff0c;最大的一个数据就“沉”到数组第N-1 个位置。如此循环 (N-1)次&#xff0c;每次循环需要比较的个数…...

k8s集群调度

目录 1、理论&#xff1a; 1.1、 概述&#xff1a; 1.2、Pod 是 Kubernetes 的基础单元&#xff0c;Pod 启动典型创建过程如下&#xff1a; 工作机制 **** 1.3、调度过程 *** 1.4、Predicate 有一系列的常见的算法可以使用&#xff1a; ** 1.5、 优先级由一系列键…...

Scala中类的继承、抽象类和特质

1. 类的继承 1.1 Scala中的继承结构 Scala 中继承关系如下图&#xff1a; Any 是整个继承关系的根节点&#xff1b; AnyRef 包含 Scala Classes 和 Java Classes&#xff0c;等价于 Java 中的 java.lang.Object&#xff1b; AnyVal 是所有值类型的一个标记&#xff1b; Nul…...

小程序如何实现登录数据持久化

在小程序中实现登录数据的持久化可以通过以下几种方式&#xff1a; 使用本地缓存 在用户登录成功后&#xff0c;将登录凭证或用户信息等数据使用 wx.setStorageSync 方法存储到本地缓存中&#xff1a; // 存储登录数据到本地缓存 wx.setStorageSync(token, 登录凭证); wx.set…...

Maven本地配置获取nexus私服的依赖

场景 Nexus-在项目中使用Maven私服&#xff0c;Deploy到私服、上传第三方jar包、在项目中使用私服jar包&#xff1a; Nexus-在项目中使用Maven私服&#xff0c;Deploy到私服、上传第三方jar包、在项目中使用私服jar包_nexus maven-releases 允许deploy-CSDN博客 在上面讲的是…...

第02章-变量与运算符

1 关键字 关键字&#xff1a;被Java语言赋予了特殊含义&#xff0c;用作专门用途的字符串&#xff08;或单词&#xff09;。如class、public、static、void等&#xff0c;这些单词都被Java定义好了&#xff0c;称为关键字。 特点&#xff1a;关键字都是小写字母&#xff1b;官…...

SpringBoot数据响应、分层解耦、三层架构

响应数据 ResponseBody 类型&#xff1a;方法注解、类注解位置&#xff1a;Controller方法、类上作用&#xff1a;将方法返回值直接响应&#xff0c;如果返回值类型是 实体对象/集合 &#xff0c;将会转换为json格式响应说明&#xff1a;RestController Controller Respons…...

go测试库之apitest

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…...

K8S删除资源后一直处于Terminating状态无法删除解决方法

原因 使用kubectl delete 删除某命名空间是一直处于Terminating状态无法删除&#xff0c;首先排查了该命名空间下是否还存在deployment pod等资源发现没有后&#xff0c;等了很久还是无法删除后发现是因为该名称空间的“finalizers”字段有值导致 Finalizer&#xff08;终结器…...

jvm实践

说一下JVM中的分代回收 堆的区域划分 1.堆被分为了两份:新生代和老年代[1:2] 2.对于新生代&#xff0c;内部又被分为了三个区域。Eden区&#xff0c;幸存者区survivor(分成from和to)[8:1:1] 对象回收分代回收策略 1.新创建的对象&#xff0c;都会先分配到eden区 2.当伊园内存…...

redis-plus-plus访问REDIS集群

编程语言&#xff1a;C 开源库&#xff1a;redis-plus-plus 接口类&#xff1a;RedisCluster 初始化需要输入任意一个结点的IP和端口&#xff0c;如果设置了密码&#xff0c;还需要密码的明文并使用ConnectionOptions类。 初始化完成后可以直接进行读/写操作。 RedisClust…...

python把Word题库转成Excle题库

又到了一年一度的背题时刻&#xff0c;但是收到的题库是Word版的&#xff0c;页数特别多 话不多说&#xff0c;上代码&#xff0c;有图有真相&#xff0c;代码里面备注的很详细 # 导入所需库 import csv import os import refrom docx import Document from win32com import c…...

算法通关村第六关-白银挑战树

大家好我是苏麟 , 今天聊聊树 . 大纲 树的概念二叉树满二叉树完全二叉树 树的性质树的定义与存储方式树的遍历通过序列构造二叉树前中序列遍历 树的概念 树是我们计算机中非常重要的一种数据结构&#xff0c;同时使用树这种数据结构&#xff0c;可以描述现实生活中的很多事物&…...

【Java对象】一文读懂 Java 对象庐山真面目及指针压缩

文章目录 版本及工具介绍Java 对象结构对象头mark word 标记字mark word 标记字解析Lock Record class point 类元数据指针 实例数据对齐填充为什么需要对齐填充 常见 Java 数据类型对象分析ArrayListLongStringByteBoolean 其它指针压缩前置知识&#xff1a;32位操作系统为什么…...

leetcode做题笔记210. 课程表 II

现在你总共有 numCourses 门课需要选&#xff0c;记为 0 到 numCourses - 1。给你一个数组 prerequisites &#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示在选修课程 ai 前 必须 先选修 bi 。 例如&#xff0c;想要学习课程 0 &#xff0c;你需要先完成课程 1…...

【深度学习 AIGC】stable diffusion webUI 使用过程,参数设置,教程,使用方法

文章目录 docker快速启动vae.ckpt或者.safetensorsCFG指数/CFG Scale面部修复/Restore facesRefinerTiled VAEClip Skipprompt提示词怎么写 docker快速启动 如果你想使用docker快速启动这个项目&#xff0c;你可以按下面这么操作&#xff08;显卡支持CUDA11.8&#xff09;。如…...

论文阅读 - Detecting Social Bot on the Fly using Contrastive Learning

目录 摘要&#xff1a; 引言 3 问题定义 4 CBD 4.1 框架概述 4.2 Model Learning 4.2.1 通过 GCL 进行模型预训练 4.2.2 通过一致性损失进行模型微调 4.3 在线检测 5 实验 5.1 实验设置 5.2 性能比较 5.5 少量检测研究 6 结论 https://dl.acm.org/doi/pdf/10.1145/358…...

PaddleMIX学习笔记(1)

写在前面 之前对HyperLedger的阅读没有完全结束&#xff0c;和很多朋友一样&#xff0c;同时也因为工作的需要&#xff0c;最近开始转向LLM方向。 国内在大模型方面生态做的最好的&#xff0c;目前还是百度的PaddlePaddle&#xff0c;所以自己也就先从PP开始看起了。 众所周知…...

【网络协议】聊聊HTTPS协议

前面的文章&#xff0c;我们描述了网络是怎样进行传输数据包的&#xff0c;但是网络是不安全的&#xff0c;对于这种流量门户网站其实还好&#xff0c;对于支付类场景其实容易将数据泄漏&#xff0c;所以安全的方式是通过加密&#xff0c;加密方式主要是对称加密和非对称加密。…...

2023.11.2事件纪念

然而造化又常常为庸人设计,以时间的流逝,来洗涤旧迹,仅以留下淡红的血色和微漠的悲哀。 回顾这次事件&#xff0c;最深的感触就是什么是团队的力量&#xff01; 当我们看到希望快要成功的时候&#xff0c;大家洋溢出兴奋开心的表情&#xff0c;一起的欢声笑语&#xff1b;但看…...

Scala和Play WS库编写的爬虫程序

使用Scala和Play WS库编写的爬虫程序&#xff0c;该程序将爬取网页内容&#xff1a; import play.api.libs.ws._ import scala.concurrent.ExecutionContext.Implicits.global ​ object BaiduCrawler {def main(args: Array[String]): Unit {val url ""val proxy…...

佳易王配件进出库开单打印进销存管理系统软件下载

用版配件进出库开单打印系统&#xff0c;可以有效的管理&#xff1a;供货商信息&#xff0c;客户信息&#xff0c;进货入库打印&#xff0c;销售出库打印&#xff0c;进货明细或汇总统计查询&#xff0c;销售出库明细或汇总统计查询&#xff0c;库存查询&#xff0c;客户往来账…...

【深度学习基础】专业术语汇总(欠拟合和过拟合、泛化能力与迁移学习、调参和超参数、训练集、测试集和验证集)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…...