接口安全设计
需要使用国密算法,查阅网上资料,需引入Bouncy Castle依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.69</version>
</dependency>
并引入hutools-crypto来使用SM2,SM3,SM4 Hutool参考文档-国密算法工具-SmUtil
// 生成sm4的key
byte[] key = SecureUtil.generateKey("SM4").getEncoded();
String sm4KeyBase64 = Base64.getEncoder().encodeToString(key);
String sm4KeyBase64U = new String(sm4k.getBytes(), StandardCharsets.UTF_8);
// 生成sm2的key值对
KeyPair pair = SecureUtil.generateKeyPair("SM2");
byte[] privateKey = pair.getPrivate().getEncoded();
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey);
String privateKeyBase64U = new String(privateKeyS.getBytes(), StandardCharsets.UTF_8);
byte[] publicKey = pair.getPublic().getEncoded();
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey);
String publicKeyBase64U = new String(publicKeyS.getBytes(), StandardCharsets.UTF_8);
字符集,编码解码,加解密
字符集是bytes和string互相转换,如果字符集没选好,就容易出乱码。一般用UTF_8. 编码一般有Hex(Base16)和Base64,编码解码是为了避免有一些特殊字符,影响传输。 Base64UrlSafe(可以避免url中特殊字符出问题) 加解密是为了避免信息泄漏。
前后端编码方式选择
前端 后端hutools-crypto、Bouncy Castle,既可以base16,又可以base64. hex,base64,urlencode编码方案对比 SM4算法主要包含5种基本模式:ECB、CBC、CFB、OFB,CTR(后4种都是ECB算法模块衍生而来);与MAC结合还诞生了GCM,CCM等高级模式。 SM4分组密码算法介绍_浩雪峰的博客-CSDN博客_sm4分组密码算法
考虑性能和安全性,没有一种模式是绝对的最优选择,不同的模式适用于不同的应用场景。 分组加密模式 ECB、CBC、PCBC、CFB、OFB、CTR_fengwang0301的博客-CSDN博客_分组密码ecb模式
补码padding
SM4中的padding是指在加密前对明文进行填充,使其长度为16字节的整数倍,因为SM4是一种分组密码,每个分组的长度为16字节。12
不同的padding方式有不同的填充规则,常见的有PADDING_PKCS5、PADDING_PKCS7、PADDING_ISO10126、PADDING_ANSI_X923等。1
填充的目的是为了保证密文的完整性和一致性,同时也可以防止一些攻击,例如重放攻击和填充攻击。2
SM4用什么padding比较好,取决于你的加密模式和数据格式。12
一般来说,如果你使用的是ECB模式或者CBC模式,那么你需要对明文进行padding,因为这两种模式要求明文的长度是16字节的倍数。12
如果你使用的是CFB模式、OFB模式或者CTR模式,那么你不需要对明文进行padding,因为这三种模式可以将分组密码转化为流密码,可以对任意长度的明文进行加密。12
常见的padding方式有PADDING_PKCS5、PADDING_PKCS7、PADDING_ISO10126、PADDING_ANSI_X923等,它们的区别主要在于填充的字节的值和验证的方式。12
PADDING_PKCS5和PADDING_PKCS7的填充方式是在明文的末尾添加N个字节,每个字节的值都是N,其中N是从1到16的整数,表示需要填充的字节数。12
PADDING_ISO10126的填充方式是在明文的末尾添加N个字节,其中最后一个字节的值是N,其余的字节的值是随机的。12
PADDING_ANSI_X923的填充方式是在明文的末尾添加N个字节,其中最后一个字节的值是N,其余的字节的值都是0。12
在解密的时候,需要根据填充的字节的值来判断填充的字节数,然后去掉填充的字节,恢复原始的明文。12
如果你的数据格式是二进制的,那么你可以选择任意一种padding方式,只要加密和解密的时候保持一致即可。12
如果你的数据格式是文本的,那么你最好选择PADDING_PKCS5或者PADDING_PKCS7,因为这两种方式可以避免填充的字节和原始的明文的字符冲突,导致解密的时候出错。12
综上所述,SM4用什么padding比较好,没有一个确定的答案,你需要根据你的具体的需求和场景来选择合适的padding方式。12
加密
方式1:对每个json字段进行加密?
方式2:对整个请求报文进行加密(网金)请求报文本身是一个JSON,然后加密了之后以参数形式传递。
前端-请求加密
js SM4对称加密
后端-请求解密
java SM4对称解密
后端-响应加密
java SM4对称加密
前端-响应解密
js SM4对称解密
数据防重放
方案1:预请求
预请求,获取redis中的某个随机数;然后,根据随机数去访问接口,就会清掉redis的key-value. 通过这种方式进行防止数据重放。
没有redis,只能放在数据库中,并且需要设计两个请求,每个请求涉及DB的写操作,建议用方案2.
方案2:timestamp + nonce 方案
10 种保证接口数据安全的方案!
时间戳超时机制也是有漏洞的,如果是在时间差内,黑客进行的重放攻击,那就不好使了。可以使用timestamp + nonce
方案。
nonce
指唯一的随机字符串,用来标识每个被签名的请求。我们可以将每次请求的nonce
参数存储到一个“set 集合”中,或者可以 json 格式存储到数据库或缓存中。每次处理 HTTP 请求时,首先判断该请求的nonce
参数是否在该“集合”中,如果存在则认为是非法请求。
然而对服务器来说,永久保存nonce
的代价是非常大的。可以结合timestamp
来优化。因为timstamp
参数对于超过3min
的请求,都认为是非法请求,所以我们只需要存储3min
的nonce
参数的“集合”即可。
方案 | 优点 | 缺点 |
---|---|---|
数据库 | 完全防重放 | 每次需要查询DB,速率慢;需要写定时任务清理nonce;改造量大 |
本地缓存 | 速率快;缓存失效机制自动清理;改造少 | 假设有n台机器,重放攻击可以到达n次。 |
不同缓存框架比较
(很全面)SpringBoot 使用 Caffeine 本地缓存 SpringBoot: Implement caching with Caffeine. Spring Cache的使用教程:注解形式和api接口形式,以及调用内部方法注解失效的原因_不如敲代码的博客-CSDN博客
数据防篡改
数据报文加签验签,是保证数据传输安全的常用手段,它可以保证数据在传输过程中不被篡改。以前我做的企业转账系统,就用了加签验签。 签名/验签时,将参数名ASCII码从小到大排序_vamViolet的博客-CSDN博客_参数名ascii码从小到大排序
2.1 什么是加签验签呢?
- 数据加签:用 Hash算法(SM3)把原始请求参数生成报文摘要,然后用私钥对这个摘要进行加密,就得到这个报文对应的数字签名sign(这个过程就是加签)。通常来说,请求方会把数字签名和报文原文一并发送给接收方。
- 验签:接收方拿到原始报文和数字签名(sign)后,用**同一个 Hash 算法(**比如都用 SM3)从报文中生成摘要 A。另外,用对方提供的公钥对数字签名进行解密,得到摘要 B,对比 A 和 B 是否相同,就可以得知报文有没有被篡改过。
其实加签,按我的理解,就是把请求参数,按照一定规则,利用hash算法+加密算法生成一个唯一标签sign。验签的话,就是把请求参数按照相同的规则处理,再用相同的hash算法,和对应的密钥解密处理,以对比这个签名是否一致。
请求报文参考
{
"timestamp":"xxxx",
"nonce":"xxxx",
"sign":"xxxxxxxxxxxxxxx",
}
数据安全处理-最终方案
报文密文处理-SM4
- 前端对请求报文加密,后端对请求报文解密;
- 后端对响应报文加密,前端对响应报文解密
将整个报文体加密为一个密文字符串
分组加密模式 ECB、CBC、PCBC、CFB、OFB、CTR_fengwang0301的博客-CSDN博客_分组密码ecb模式
ECB
ECB模式加密过程
ECB模式解密过程
CBC
CBC模式加密过程
CBC模式解密过程
数据防重放
timestamp + nonce 方案
nonce
指唯一的随机字符串,用来标识每个被签名的请求。我们可以将每次请求的nonce
参数存储到一个“set 集合”中,或者可以 json 格式存储到数据库或缓存中。每次处理 HTTP 请求时,首先判断该请求的nonce
参数是否在该“集合”中,如果存在则认为是非法请求。
然而对服务器来说,永久保存nonce
的代价是非常大的。可以结合timestamp
来优化。因为timstamp
参数对于超过3min
的请求,都认为是非法请求,所以我们只需要存储3min
的nonce
参数的“集合”即可。
方案 | 优点 | 缺点 |
---|---|---|
nonce放到数据库 | 完全防重放 | 每次需要查询DB,速率慢;需要写定时任务清理nonce;改造量大 |
nonce放到本地缓存 | 速率快;缓存失效机制自动清理;改造少 | 假设有n台机器,重放攻击可以到达n次。 |
数据签名和验签
- 数据加签:原始报文按Key值排序(ASCALL码),处理成类似a=1&b=2的字符串,再用 Hash算法(SM3)把原始请求参数生成报文摘要,就得到这个报文对应的数字签名sign(这个过程就是加签)。通常来说,请求方会把数字签名和报文原文密文一并发送给接收方。
- 验签:接收方拿到原始报文和数字签名(sign)后,用**同一个 Hash 算法(**比如都用 SM3)从报文中生成摘要 A。对比 A 和 请求的数字签名 是否相同,就可以得知报文有没有被篡改过。