title: 实例-第三方接口签名加密
date: 2019-08-06 19:32:21
categories:
- 实战
- 安全
tags: - 安全
top:
总述
最近和第三方接口做对接,接口涉及到了签名和加密,因此在这里总结一下。签名和加密涉及对称加密、非对称加密、SSL等概念,可以在其他文章中参考。这里只做具体项目中实践的示例。
总体思路是用固定的key进行SHA1摘要签名,用对称加密的secret对数据进行aes加密。由于签名key和对称加密的secret是双方预先沟通好写死在代码里的,不像SSL需要在网络中传输,因此可以保证安全。
具体如下:
数据报文结构:
{
"appid":"1a6dfe4b3a7740d1a37fa1615a3f6bd9",
"nonce":"YSBuLCgvmdSdB3oS",
"timestamp":1552900602924,
"signature":"296a25e1d61b10d359b2893befc3a417d89b4708",
"data":"sdfjasIOISADFLASlsdaf12142rsakdfsafjsawKJL34343"
}
发送过程:
- 先加密:将String的data、秘钥、初始化向量转换为字节码,完成aes对称加密,再转换为Base64字符串。
- 再签名:将**“所有数据” + “签名key”**根据一定规则(比如根据askall码排序)拼装成字符串,然后接用SHA1或者MD5进行摘要用作签名就OK了。由于有timestamp和nonce,因此可以防止签名key被破解。
接收过程:
- 先验签:使用和签名同样的方式生成签名,然后对比数据的签名进行验签即可。
- 再解密:将Base64字符串的data转换为字节码,再进行aes解密,再转换为String即可(Json字符串)。
由于appid在报文里是明文的,因此很容易根据appid获得验签和解密所需的签名key,以及秘钥和初始化向量。
下面进行具体解释。
签名算法
签名规则
签名规则如下:
- 将appid、timestamp、nonce、data及key不为null的字段值按ASCII码从小到大排序(字典序);
- 将排序后的字段值拼接成字符串;
- 对拼接后的字符串计算SHA1摘要,作为签名。
其中签名key由平台提供。
签名示例
签名key:Odgs!^36M$aAXrpp
请求体字段 | 值 |
---|---|
appid | “1a6dfe4b3a7740d1a37fa1615a3f6bd9” |
nonce | “YSBuLCgvmdSdB3oS” |
timestamp | 1552900602924 |
data | null |
-
将appid、timestamp、nonce、key及data不为null的字段值按ASCII码从小到大排序(字典序),因为data为null不加入签名计算,结果如下:
请求体字段值 1552900602924 1a6dfe4b3a7740d1a37fa1615a3f6bd9 Odgs!^36M$aAXrpp YSBuLCgvmdSdB3oS -
将排序后的字段值拼接成字符串,结果如下:
15529006029241a6dfe4b3a7740d1a37fa1615a3f6bd9Odgs!^36M$aAXrppYSBuLCgvmdSdB3oS
-
对拼接后的字符串计算SHA1摘要,签名如下:
296a25e1d61b10d359b2893befc3a417d89b4708
签名后的请求体:
{
"appid":"1a6dfe4b3a7740d1a37fa1615a3f6bd9",
"nonce":"YSBuLCgvmdSdB3oS",
"timestamp":1552900602924,
"signature":"296a25e1d61b10d359b2893befc3a417d89b4708"
}
业务数据加/解密算法
加密规则
- 将业务数据序列化为JSON格式字符串;
- 通过AES加密算法和密钥、初始化向量对JSON格式字符串进行加密;
- 将加密结果进行Base64编码。
解密规则
- 将加密业务数据进行Base64解码;
- 通过AES解密算法和密钥、初始化向量进行解密;
- 获得JSON格式的业务数据。
其中加*/解密的密钥和初始化向量由平台提供,字符串均采用UTF-8*编码格式
代码示例
项目实际使用的签名、加密/解密代码如下:
import java.security.MessageDigest;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;
import java.util.stream.Collectors;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import lombok.SneakyThrows;
public class SecurityUtil {
public static