import java.io.IOException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import com.alibaba.fastjson.JSONObject;
import sun.misc.BASE64Decoder;
public class SignatureUtils {
/**
* MD5withRSA 客户端调用使用签名认证-数字签名
* @param params 参数
* @param secret 客户端手里的私钥 (私钥 和 公钥 是一对,要么由客户端生成,要么由服务端生成)
* @return
*/
public static String getSign(Map params, String secret) throws Exception {
Set keySet = params.keySet();
// 使用treeset 排序
TreeSet sortSet = new TreeSet();
sortSet.addAll(keySet);
StringBuilder keyvalueStr = new StringBuilder();
Iterator it = sortSet.iterator();
while (it.hasNext()) {
String key = it.next();
String value = params.get(key);
keyvalueStr.append(key).append("=").append(value).append("&");
}
//获取私钥
PrivateKey privateKey = getPrivateKey(secret);
// 1.通过getInstance方法取得MD5withRSA实例 2.通过MD5进行数字摘要 ,3.使用RSA算法进行非对称加密
Signature signature = Signature.getInstance("MD5withRSA");
//使用私钥对signature初始化
signature.initSign(privateKey);
//keyvalueStr待摘要串
signature.update(keyvalueStr.substring(0,keyvalueStr.length()-1).getBytes());
//取得对应内容的数字签名串
String sign = BytesConvertUtils.bytesToHexString(signature.sign());
return sign;
}
/**
* PKCS8格式私钥 客户端使用私钥加密
* @return
*/
private static PrivateKey getPrivateKey(String key) throws Exception {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(key));
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyf.generatePrivate(priPKCS8);
return privateKey;
}
/**
* MD5withRSA 使用公钥验证客户端的数字签名是否正确 防止连接数据被篡改
* @param params 服务端的参数
* @param sign 服务端的签名串
* @param publicSecret 公钥 (私钥 和 公钥 是一对,要么由客户端生成,要么由服务端生成)
* @return
* @throws Exception
*/
public static boolean validate(Map params, String sign, String publicSecret) throws Exception {
Set keySet = params.keySet();
// 使用treeset排序(默认自然排序按照首字母顺序,一种自带Comparator自定义来排序)
TreeSet sortSet = new TreeSet();
sortSet.addAll(keySet);
Iterator it = sortSet.iterator();
StringBuilder keyvalueStr = new StringBuilder();
while (it.hasNext()) {
String key = it.next();
String value = params.get(key);
keyvalueStr.append(key).append("=").append(value).append("&");
}
//获取公钥
PublicKey publicKey = getPublicKey(publicSecret);
// 1.通过getInstance方法取得MD5withRSA实例 2.通过MD5进行数字摘要 ,3.使用RSA算法进行非对称加密
Signature signature = Signature.getInstance("MD5withRSA");
//使用公钥对signature初始化
signature.initVerify(publicKey);
//keyvalueStr待摘要串
signature.update(keyvalueStr.substring(0,keyvalueStr.length()-1).getBytes());
//对数字签名进行校验
return signature.verify(BytesConvertUtils.hexStringToBytes(sign));
}
/**
* 公钥 服务器端使用公钥解密
* @return
*/
private static PublicKey getPublicKey(String key) throws Exception {
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(key));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(bobPubKeySpec);
return publicKey;
}
/**
* 测试 MD5withRSA 加密 和 解密
* @param args
*/
public static void main(String[] args) {
/* 客户端生成签名 */
// String privateSecret = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAtFf8rpkRsy66BOwpS96VBNwxMj3ubwJd424YijNt/hVqChEZp537mhdO3hIJb4AyXXa7UmJKirw1aQ5wQ4KMiwIDAQABAkEAmNWIOp9WNLgAxXMufDGhsIC64I1Dp6gl0TvYaCa1rIElHUCAvT8jj3F0juYjNOKE6UOZ/mEMtNihmEfwezb34QIhANs78pVlgD46bUDu3i6tmvHZa5mhUHXbzTHrZduiv9WfAiEA0pZpv+BAlQ7i6iVuqMwVU+5eRKhomaz5H9fPJcmjaZUCIDeGCTwZBs+70Gu/k3nS8gkKfZvdJRmUX3WjHprvrxIVAiA30HnyyyMS3sExwf4UaxL0DeeaVvByWrAkynQy1cCkCQIgLb/ADZzVWkWkBE1tPAv2QwHOBrCJkRi5Lg9Qc8w/N5Q=";
String privateSecret ="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQCJnIoDN1LlWWolWy3vgSMfGuaMk2fwtoOjllKQNuehJiwF1wCtSIkzZuUMKz7a3tmmd8Rg8LYqMQ3oTp0dOsp09pl76iNvww45Sm+blJ0dvCOH4yLmBikZtMeXoyzIAx6Jn2foQFLQx/rbWMky8IllfPaFlHipwJhd7m+zP3/ROetNBnHRZEWBj5E4SV27O6kxPPERQw36Z8crc5sAzpAdmPDfUkumVUDUFeZ2AxfUBuvRi5E4S0Qgh87RVVZ8VMU/QYYaOmVWDlTe36iWOVIvuS5qwdvRU4LrLy0J4LRF2EfgawPtYc+nX3b7B+aXE5l+yyo2n/NQU1F8KoU9iFAgMBAAECggEADcukNxO4ldq+vkCBolz64moBduln/n8qSH0j9TtlCNx7f6T2DUoKkgwBHguo/bmTQSBuIGWc9cpSufmnosHRYa+uqnfvol43Fx7garpAuU57XkPUyoscxSyWudmMavX4wlaEbz4wjw9Ptt/UHEBebBlcNVrcIMxmUJNaFsMtzRISoSw9VwLXE8jKc3Kd21ygiJUAsUxGQkS2e5YIxztKaWZFma1U4NadF7VkDM8JvLUB0s89Sum2XAr6IBRjrLn39COGEfFbQWaKJuJBUzL/zVWxAKGRybrjCA8+2HfW4FFOJMsRfBF/UbdwcfBKlZMxx/HsUqq91bshMK/0EUM/wQKBgQD2Kmp3ZDeDyNJNY6vNEDsny6XEzPTm/Q67shNLteb2ei9lazYEtAcv3cNgR92wO42Lej6NWsvJ3JLhULmh6NnqBDwgHECaNYNqzDy4oHuTmEafsRZUPdBBIOj/2Piuoe4OdD+BwiDAP0WeOOcErTNj9D6gscnCDqQZCbKRelt9fQKBgQDYWDNYOxTO//LBUfSKSM8WZ/HT/1funRSdBMaZgPQ6FqkLLL39CSzo/Bd4lEtVf3518BKf+djp+xDsy3L285W1sa5gU9AmJEGNoobjRUzmO+su3CATWdFzqsDgANPZuawqLuvM9myVJYaIuxLy3T2hLCeu7bPPQp9b3Ro4h6nVqQKBgQCW9nYqV9RPxlVqTmiqrnyIsDQt17+M9L4XT2LT+G720wHEAZa0Fa+epDJQFmKW1D6+va4kw/zcA6Az+2yMJZ+WCxjQXGMl7xFz6mKGmyyN3EHbZx+Zc/sGJeeeF3KSrg2u4Y2cxu+M1oCcQqsyTAGLF9tccwuXLaFnFXJkExSOhQKBgAwJLCIWd1vkxV6X7qDa2BqJAd0ncX7m4kSyBVJ46B1rMVBb1t4C6qqzi6K7rU0+YIyzJHYd8kFiJFjoIKc4efW3wtm9D/FvRu5YQmTWcCbG4piiym4JKyviLgTIRwDv4XIfViL6+qN5iPiGX9F/GXSj/jkvJDe9nxv/Ovw2H+6xAoGBAIG8pAIhstGXZEGnpFQwZrhPmzfVf1KlwrLvSysxnYsDIm7+Xj9/u4s3O+GFvGbAEl6YannN4GOJv5vLHegiedhuMxIil3ydjow5J6wa4wO+OMXAhNQfMD5K22yXZslUdkNTeaqQqxwmwA7bCUf2vLATrDkjmFS1YXY4ORy2u1gF";
Map params = new HashMap();
List coins = new ArrayList<>();
coins.add("ADC");
coins.add("SDG");
params.put("coins", JSONObject.toJSONString(coins));
params.put("randomStr", "2E21EEGREG33");
params.put("timestamp", String.valueOf(1620375990));
String sing = null;
try {
sing = getSign(params,privateSecret);
System.out.println("客户端生成的数字签名为:"+ sing);
} catch (Exception e) {
e.printStackTrace();
}
/* 服务端验证数字签名 */
String publicSecret = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALRX/K6ZEbMuugTsKUvelQTcMTI97m8CXeNuGIozbf4VagoRGaed+5oXTt4SCW+AMl12u1JiSoq8NWkOcEOCjIsCAwEAAQ==";
try {
if(validate(params, sing, publicSecret)){
System.out.println("签名验证通过");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过MD5withRSA 公钥验签,私钥加签,通过秘钥AES解密
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
/* 获取认证信息
* 1.验签
* 2.验签失败,直接返回
* 3.验签成功,参数解密,调用接口
*
* @param vo
* @return
*/
@ApiOperation(value = "获取认证信息", notes = "获取认证信息")
@PostMapping("/getAuthenticationInfo")
public Map getAuthenticationInfo(@RequestBody @Valid AuthenticationInfoRequestVo vo) {
log.info("获取认证信息接口 请求参数为:{}", JSONObject.toJSONString(vo));
Map params = new HashMap(12);
params.put("ciphertext", vo.getCiphertext());
params.put("randomStr", vo.getRandomStr());
params.put("timestamp", String.valueOf(vo.getTimestamp()));
//验签
//查询公钥
Map publicKeyMap = this.puHuiYunService.getPuHuiYunPublicKey(PRIMARY_ID);
if (RemoteCallResultCheck.isFail(publicKeyMap)) {
log.error("查询公钥失败");
return ApiRes.getInstance(ResultEnum.PU_HUI_YUN_NOT_EXISTS_PUBLIC_KEY_FAIL);
}
String publicSecret = RemoteCallResultCheck.getKeyByDataByData(publicKeyMap, "puhuiyunPublicKey", String.class);
try {
boolean signResult = SignatureUtils.validate(params, vo.getSign(), publicSecret);
if (!signResult) {
log.error("获取认证信息接口 验签失败");
return ApiRes.getInstance(ResultEnum.PU_HUI_YUN_SIGN_FAIL);
}
} catch (Exception e) {
log.error("获取认证信息接口 验签失败,异常为:{}", e);
return ApiRes.getInstance(ResultEnum.PU_HUI_YUN_SIGN_FAIL);
}
//验签成功 解密
String requestBody = "";
//获取AES秘钥
String aesSecretKey = RemoteCallResultCheck.getKeyByDataByData(publicKeyMap, "aesSecretKey", String.class);
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, Base64.decode(aesSecretKey));
try {
requestBody = aes.decryptStr(vo.getCiphertext(), CharsetUtil.CHARSET_UTF_8);
log.info("获取认证信息接口 解密={}", vo.getCiphertext());
} catch (Exception e) {
log.error("获取认证信息接口 解密失败:", e);
}
if (StringUtils.isBlank(requestBody)) {
return ApiRes.getInstance(ResultEnum.PARAMETER_ERROR);
}
JSONObject json = JSONObject.parseObject(requestBody);
String phone = (String) json.get("phone");
//feign调用接口
String privateSecret = RemoteCallResultCheck.getKeyByDataByData(publicKeyMap, "seaPrivateKey", String.class);
if (StringUtils.isBlank(privateSecret)) {
log.error("查询私钥为空");
return ApiRes.getInstance(ResultEnum.PU_HUI_YUN_NOT_EXISTS_PRIVATE_KEY_FAIL);
}
Map userAuthInfo = userAuthService.getUserAuthInfoByMobile(phone);
if (RemoteCallResultCheck.isFail(userAuthInfo)) {
log.error("根据手机号获取认证信息未认证{}",userAuthInfo);
return userAuthInfo;
}
UserAuthInfoDto userAuthInfoDto = RemoteCallResultCheck.getData(userAuthInfo, UserAuthInfoDto.class);
//AES 加密
String ciphertext = aes.encryptHex(JSON.toJSONString(userAuthInfoDto));
Map result = new HashMap(12);
result.put("ciphertext", ciphertext);
result.put("randomStr", IDUtil.nextUUID());
result.put("timestamp", String.valueOf(System.currentTimeMillis()));
String sign = null;
try {
log.info("根据手机号获取认证信息生成数字签名,入参={}", result);
sign = SignatureUtils.getSign(result, privateSecret);
result.put("sign", sign);
log.info("根据手机号获取认证信息生成的数字签名为:" + sign);
} catch (Exception e) {
log.error("根据手机号获取认证信息接口 加签失败",e);
return ApiRes.getInstance(ResultEnum.PU_HUI_YUN_ADD_SIGN_FAIL);
}
return ApiRes.getInstance(ResultEnum.SUCCESS, result);
}