|
@@ -0,0 +1,284 @@
|
|
|
|
|
+package com.kxs.product.biz.util;
|
|
|
|
|
+
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
+
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.security.KeyFactory;
|
|
|
|
|
+import java.security.KeyPair;
|
|
|
|
|
+import java.security.KeyPairGenerator;
|
|
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
|
|
+import java.security.PrivateKey;
|
|
|
|
|
+import java.security.PublicKey;
|
|
|
|
|
+import java.security.Signature;
|
|
|
|
|
+import java.security.interfaces.RSAPrivateKey;
|
|
|
|
|
+import java.security.interfaces.RSAPublicKey;
|
|
|
|
|
+import java.security.spec.InvalidKeySpecException;
|
|
|
|
|
+import java.security.spec.PKCS8EncodedKeySpec;
|
|
|
|
|
+import java.security.spec.X509EncodedKeySpec;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * rsa验签工具。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author 没秃顶的码农
|
|
|
|
|
+ * @date 2024-04-23
|
|
|
|
|
+ */
|
|
|
|
|
+public class GdRsaUtils {
|
|
|
|
|
+
|
|
|
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(GdRsaUtils.class);
|
|
|
|
|
+
|
|
|
|
|
+ public static final String KEY_ALGORITHM = "RSA";
|
|
|
|
|
+ public static final String DEFAULT_CHARSET = "UTF-8";
|
|
|
|
|
+ // TODO: 配置私钥 (用于加密)
|
|
|
|
|
+ public static final String DEFAULT_PRIVATE_KEY = "";
|
|
|
|
|
+ // TODO: 配置公钥 (用于解密)
|
|
|
|
|
+ public static final String DEFAULT_PUBLIC_KEY = "";
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取公钥
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param publicKeyStr
|
|
|
|
|
+ * @return
|
|
|
|
|
+ * @throws Exception
|
|
|
|
|
+ */
|
|
|
|
|
+ public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
|
|
|
|
|
+ try {
|
|
|
|
|
+ byte[] buffer = Base64.getDecoder().decode(publicKeyStr);
|
|
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
|
|
|
|
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
|
|
|
|
|
+ RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
|
|
|
|
|
+ return restorePublicKey(publicKey.getEncoded());
|
|
|
|
|
+
|
|
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
|
|
+ throw new Exception("无此算法");
|
|
|
|
|
+ } catch (InvalidKeySpecException e) {
|
|
|
|
|
+ throw new Exception("公钥非法");
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new Exception("公钥数据内容读取错误");
|
|
|
|
|
+ } catch (NullPointerException e) {
|
|
|
|
|
+ throw new Exception("公钥数据为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 还原公钥
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param keyBytes
|
|
|
|
|
+ * @return
|
|
|
|
|
+ * @throws Exception
|
|
|
|
|
+ */
|
|
|
|
|
+ private static PublicKey restorePublicKey(byte[] keyBytes) throws Exception {
|
|
|
|
|
+ X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
|
|
|
|
|
+ try {
|
|
|
|
|
+ KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
|
|
+ return factory.generatePublic(x509EncodedKeySpec);
|
|
|
|
|
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
|
|
|
|
+ throw new Exception(e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 通过私钥KEY获取解密私钥
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param privateKeyStr
|
|
|
|
|
+ * @return
|
|
|
|
|
+ * @throws Exception
|
|
|
|
|
+ */
|
|
|
|
|
+ public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
|
|
|
|
|
+ try {
|
|
|
|
|
+ byte[] buffer = Base64.getDecoder().decode(privateKeyStr);
|
|
|
|
|
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
|
|
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
|
|
|
|
+ PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
|
|
|
|
|
+ return restorePrivateKey(privateKey.getEncoded());
|
|
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
|
|
+ throw new Exception("无此算法");
|
|
|
|
|
+ } catch (InvalidKeySpecException e) {
|
|
|
|
|
+ throw new Exception("公钥非法");
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new Exception("公钥数据内容读取错误");
|
|
|
|
|
+ } catch (NullPointerException e) {
|
|
|
|
|
+ throw new Exception("公钥数据为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 还原私钥
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param keyBytes
|
|
|
|
|
+ * @return
|
|
|
|
|
+ * @throws Exception
|
|
|
|
|
+ */
|
|
|
|
|
+ private static PrivateKey restorePrivateKey(byte[] keyBytes) throws Exception {
|
|
|
|
|
+ PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
|
|
|
|
+ try {
|
|
|
|
|
+ KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
|
|
+ return factory.generatePrivate(pkcs8EncodedKeySpec);
|
|
|
|
|
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
|
|
|
|
+ throw new Exception(e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据请求参数生成待签名的字符串
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param params
|
|
|
|
|
+ * @return
|
|
|
|
|
+ */
|
|
|
|
|
+ public static String getSignContent(Map<String, String> params) {
|
|
|
|
|
+ params.remove("sign");
|
|
|
|
|
+ Map<String, String> sortedParams = new TreeMap<>(params);
|
|
|
|
|
+
|
|
|
|
|
+ StringBuilder content = new StringBuilder();
|
|
|
|
|
+ List<String> keys = new ArrayList<>(sortedParams.keySet());
|
|
|
|
|
+ Collections.sort(keys);
|
|
|
|
|
+ int index = 0;
|
|
|
|
|
+ for (String key : keys) {
|
|
|
|
|
+ String value = sortedParams.get(key);
|
|
|
|
|
+ if (!StrUtil.isAllBlank(key, value)) {
|
|
|
|
|
+ content.append(index == 0 ? "" : "&").append(key).append("=").append(value);
|
|
|
|
|
+ index++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return content.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * sha256WithRsa 验签
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param content
|
|
|
|
|
+ * @param sign
|
|
|
|
|
+ * @param publicKey
|
|
|
|
|
+ * @param charset
|
|
|
|
|
+ * @return
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean rsa256CheckContent(String content, String sign, String publicKey, String charset) throws Exception {
|
|
|
|
|
+ try {
|
|
|
|
|
+ PublicKey pubKey = loadPublicKey(publicKey);
|
|
|
|
|
+ Signature signature = Signature.getInstance("SHA256WithRSA");
|
|
|
|
|
+ signature.initVerify(pubKey);
|
|
|
|
|
+ if (StrUtil.isEmpty(charset)) {
|
|
|
|
|
+ signature.update(content.getBytes());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ signature.update(content.getBytes(charset));
|
|
|
|
|
+ }
|
|
|
|
|
+ return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ throw new Exception("签名失败,RSAcontent=" + content + ";charset=" + charset + ";sign=" + sign, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * sha256WithRsa 签名
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param content
|
|
|
|
|
+ * @param privateKey
|
|
|
|
|
+ * @param charset
|
|
|
|
|
+ * @return
|
|
|
|
|
+ */
|
|
|
|
|
+ public static String rsa256Sign(String content, String privateKey, String charset) throws Exception {
|
|
|
|
|
+ try {
|
|
|
|
|
+ PrivateKey priKey = loadPrivateKey(privateKey);
|
|
|
|
|
+ Signature signature = Signature.getInstance("SHA256WithRSA");
|
|
|
|
|
+ signature.initSign(priKey);
|
|
|
|
|
+ if (StrUtil.isEmpty(charset)) {
|
|
|
|
|
+ signature.update(content.getBytes());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ signature.update(content.getBytes(charset));
|
|
|
|
|
+ }
|
|
|
|
|
+ byte[] signed = signature.sign();
|
|
|
|
|
+ return new String(Base64.getEncoder().encode(signed));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ throw new Exception("签名失败,RSAcontent=" + content + ";charset=" + charset, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成接口签名
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param reqParams
|
|
|
|
|
+ * @return
|
|
|
|
|
+ * @throws Exception
|
|
|
|
|
+ */
|
|
|
|
|
+ public static String rsa256Sign(Map<String, Object> reqParams, String privateKey) throws Exception {
|
|
|
|
|
+ Map<String, String> params = reqParams.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> String.valueOf(v.getValue())));
|
|
|
|
|
+ String checkContent = GdRsaUtils.getSignContent(params);
|
|
|
|
|
+ String sign = null;
|
|
|
|
|
+ try {
|
|
|
|
|
+ sign = GdRsaUtils.rsa256Sign(checkContent, privateKey, DEFAULT_CHARSET);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ throw new Exception(e);
|
|
|
|
|
+ }
|
|
|
|
|
+ return sign;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 校验接口请求签名
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param reqParams
|
|
|
|
|
+ * @return
|
|
|
|
|
+ * @throws Exception
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean checkSign(Map<String, Object> reqParams, String publicKey) throws Exception {
|
|
|
|
|
+ Map<String, String> params = reqParams.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> String.valueOf(v.getValue())));
|
|
|
|
|
+ String checkContent = GdRsaUtils.getSignContent(params);
|
|
|
|
|
+ String sign = (String) reqParams.get("sign");
|
|
|
|
|
+ boolean isSuccess;
|
|
|
|
|
+ try {
|
|
|
|
|
+ isSuccess = GdRsaUtils.rsa256CheckContent(checkContent, sign, publicKey, DEFAULT_CHARSET);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ throw new Exception(e);
|
|
|
|
|
+ }
|
|
|
|
|
+ return isSuccess;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成RSA密钥对
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void generateKeyPair() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 指定生成密钥的算法和密钥长度(密钥长度,常用值:1024、2048、4096)
|
|
|
|
|
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
|
|
|
|
|
+ kpg.initialize(1024);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取密钥对
|
|
|
|
|
+ KeyPair keyPair = kpg.genKeyPair();
|
|
|
|
|
+ RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
|
|
|
|
+ RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
|
|
|
|
+
|
|
|
|
|
+ // 获取公钥字符串
|
|
|
|
|
+ String publicKeyString = new String(Base64.getDecoder().decode(publicKey.getEncoded()));
|
|
|
|
|
+ // 得到私钥字符串
|
|
|
|
|
+ String privateKeyString = new String(Base64.getDecoder().decode((privateKey.getEncoded())));
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("公钥:" + publicKeyString);
|
|
|
|
|
+ System.out.println("私钥:" + privateKeyString);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ LOGGER.info("生成公钥私钥异常:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static void main(String[] args) throws Exception {
|
|
|
|
|
+ // 生成签名
|
|
|
|
|
+ Map<String, Object> reqParams = new HashMap<>(8);
|
|
|
|
|
+ reqParams.put("pageNo", 1);
|
|
|
|
|
+ reqParams.put("pageSize", 10);
|
|
|
|
|
+ reqParams.put("brhNo", "test");
|
|
|
|
|
+ reqParams.put("queryBeginTime", "2024-01-16");
|
|
|
|
|
+ reqParams.put("queryEndTime", "2024-01-16");
|
|
|
|
|
+ reqParams.put("timestamp", System.currentTimeMillis() / 1000);
|
|
|
|
|
+ String sign = GdRsaUtils.rsa256Sign(reqParams, DEFAULT_PRIVATE_KEY);
|
|
|
|
|
+ System.out.println("生成签名:" + sign);
|
|
|
|
|
+
|
|
|
|
|
+ // 验签
|
|
|
|
|
+ reqParams.put("sign", sign);
|
|
|
|
|
+ final boolean result = GdRsaUtils.checkSign(reqParams, DEFAULT_PUBLIC_KEY);
|
|
|
|
|
+ assert result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|