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) {}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/133930.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

#龙迅视频转换IC LT7911D是一款高性能Type-C/DP/EDP 转MIPI®DSI/CSI/LVDS 芯片,适用于VR/显示应用。

1.说明 应用功能&#xff1a;LT7911D适用于DP1.2转MIPIDSI/MIPICSI/LVDS&#xff0c;EDP转MIPIDSI/MIPICSI/LVDS&#xff0c;TYPE-C转MIPIDSI/MIPICSI/LVDS应用方案 分辨率&#xff1a;单PORT高达4K30HZ&#xff0c;双PORT高达4K 60HZ 工作温度范围&#xff1a;−40C to 85C 产…

webgoat-client side客户端问题

client side Bypass front-end restrictions 用户对 Web 应用程序的前端有很大程度的控制权。 它们可以更改 HTML 代码&#xff0c;有时也可以更改脚本。这就是为什么 需要特定输入格式的应用也应在服务器端进行验证&#xff0c;而不是只在前端做限制。 0x02 先提交请求&am…

win10虚机扩容C盘

需求&#xff1a; 在虚机管理平台上&#xff0c;将win10虚机的C盘空间扩容至200G&#xff0c;当前空间为100G 操作步骤 1.在虚机平台上&#xff0c;将硬盘1的大小增加至200G 如下图 点击保存&#xff1b; 查看win10虚机&#xff0c;发现C盘空间还是100G&#xff0c;如下图…

使用Redis实现缓存及对应问题解决

一、为什么需要Redis作缓存&#xff1f; 在业务场景中&#xff0c;如果有些数据需要极高频的存取&#xff0c;每次都要在mysql中查询的话代价太大&#xff0c;假如有一个存在于客户端和mysql之间的存储空间&#xff0c;每次可以在这空间中进行存取操作&#xff0c;就会减轻mys…

go程序获取工作目录及可执行程序存放目录的方法-linux

简介 工作目录 通常就是指用户启动应用程序时&#xff0c;用户当时所在的文件夹的绝对路径。 如&#xff1a;root用户登录到linux系统后&#xff0c;一顿cd&#xff08;change directory&#xff09;后, 到了/tmp文件夹下。此时&#xff0c;用户要启动某个应用程序&#xff0…

mediapipe流水线分析 二

目标检测 Graph 一 流水线上游输入处理 1 TfLiteConverterCalculator 将输入的数据转换成tensorflow api 支持的Tensor TfLiteTensor 并初始化相关输入输出节点 &#xff0c;该类的业务主要通过 interpreter std::unique_ptrtflite::Interpreter interpreter_ nullptr; 实现…

利用大语言模型(LLM )提高工作效率

日常工作就是面向 google/ 百度编程&#xff0c;除了给变量命名是手动输入&#xff0c;大多时候就是通过搜索引擎拷贝别人的代码&#xff0c;或者找到旧项目一段代码拷贝过来使用。这无疑是开发人员的真实写照&#xff1b;然而&#xff0c;通过搜索引擎搜索答案&#xff0c;无疑…

Go 面向对象,多态

面向对象 工程结构 新建一个oop.go package _oop // Package _oop 引用名称import ("fmt""strconv" )// GIRL 常量 const (// GIRL 自增GIRL Gender iotaFIRSTSECONDTHIRD )type Gender uint8 // 无符号的8位整数类型// User 结构体 type User struct…

代码冲突解决

远程仓库修改 本地代码修改 接下来我们push一下 如果使用IDE 冲突内容如下&#xff1a; 我们可以使用自带的工具进行修改 我们选择接受自己改动的即可 如果使用git工具怎么去处理呢 远程分支是这样 本地是这样的 add和commit之后&#xff0c;再pull&#xff0c;最后pus…

关键词搜索亚马逊商品数据接口(标题|主图|SKU|价格|优惠价|掌柜昵称|店铺链接|店铺所在地)

亚马逊提供了API接口来获取商品数据。其中&#xff0c;关键词搜索亚马逊商品接口&#xff08;item_search-按关键字搜索亚马逊商品接口&#xff09;可以用于获取按关键字搜索到的商品数据。 通过该接口&#xff0c;您可以使用API Key和API Secret来认证身份&#xff0c;并使用…

BP神经网络的数据分类——语音特征信号分类

大家好&#xff0c;我是带我去滑雪&#xff01; BP神经网络&#xff0c;也称为反向传播神经网络&#xff0c;是一种常用于分类和回归任务的人工神经网络&#xff08;ANN&#xff09;类型。它是一种前馈神经网络&#xff0c;通常包括输入层、一个或多个隐藏层和输出层。BP神经网…

关于iOS:如何使用SwiftUI调整图片大小?

How to resize Image with SwiftUI? 我在Assets.xcassets中拥有很大的形象。 如何使用SwiftUI调整图像大小以缩小图像&#xff1f; 我试图设置框架&#xff0c;但不起作用&#xff1a; 1 2 Image(room.thumbnailImage) .frame(width: 32.0, height: 32.0) 在Image上应用…

浅析刚入门Python初学者的注意事项

文章目录 一、注意你的Python版本1.print()函数2.raw_input()与input()3.比较符号&#xff0c;使用!替换<>4.repr函数5.exec()函数 二、新手常遇到的问题1、如何写多行程序&#xff1f;2、如何执行.py文件&#xff1f;3、and&#xff0c;or&#xff0c;not4、True和False…

Unity项目转微信小游戏保姆教程,繁杂问题解决,及微信小游戏平台简单性能测试

前言 借着某人需求&#xff0c;做了一波简单的技术调研&#xff1a;将Unity项目转换为微信小游戏。 本文主要内容&#xff1a;Unity转换小游戏的步骤&#xff0c;遇到问题的解决方法&#xff0c;以及简单的性能测试对比 微信小游戏的限制 微信小游戏对程序包体大小有严格限制…

Manopt使用

本文记录一些黎曼流型的优化工具箱的使用 入手 安装 https://www.manopt.org/tutorial.html#gettingstarted

飞桨平台搭建PP-YOLOE模型

一、创建项目 此博客仅是运行PP-YOLOE源码&#xff0c;这里以变压器渗漏数据集为例COCO数据集太大了&#xff0c;跑不动&#xff0c;V100训练预估计得7天左右&#xff0c;即便是A100也得4天半&#xff0c;变压器渗漏油数据集跑一个小时左右&#xff0c;还可以接受&#xff0c;…

公司团建小游戏开发小程序游戏互动小游戏

在现代工作环境中&#xff0c;团队合作和员工士气是取得成功的关键因素。为了增强团队合作、提升员工士气&#xff0c;并促进员工之间的互动&#xff0c;公司团建小游戏成为了一种备受欢迎的方式。本文将探讨如何开发公司团建小游戏&#xff0c;以达到这些目标。 1. 游戏概念 …

京东数据分析(京东销量):2023年9月京东投影机行业品牌销售排行榜

鲸参谋监测的京东平台9月份投影机市场销售数据已出炉&#xff01; 根据鲸参谋电商数据分析平台的相关数据数据显示&#xff0c;9月份&#xff0c;京东平台投影机的销量为13万&#xff0c;环比下滑约17%&#xff0c;同比下滑约25%&#xff1b;销售额将近2.6亿&#xff0c;环比下…

JVM虚拟机:如何查看自己的JVM默认的垃圾回收器

只需要在程序运行的时候指定下面的参数就可以看到当前自己的JVM默认的垃圾回收器是什么&#xff1f;如下所示&#xff1a; 如上所示&#xff0c;默认使用的是G1回收器&#xff0c;这是我的电脑&#xff0c;因为我的电脑安装jdk的版本是1.9 如果你的jdk的版本是1.8&#xff0c;那…

HTML和CSS入门学习

目录 一.HTML 二.CSS 1.CSS作用&#xff1a;美化页面 2.CSS语法 【1】CSS语法规范 【2】如何插入样式表 3.CSS选择器 4.CSS设置样式属性--设置html各种标签的属性 【1】文本属性--设置整段文字的样式 【2】字体属性--设置单个字的样式 【3】链接属性--设置链接的样式…