diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/OpenSignatureUtil.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/OpenSignatureUtil.java index 2fd0cabd1..a6e6db642 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/utils/OpenSignatureUtil.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/OpenSignatureUtil.java @@ -5,9 +5,10 @@ import lombok.extern.slf4j.Slf4j; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; import java.util.stream.Collectors; @@ -110,4 +111,86 @@ public class OpenSignatureUtil { } return result.toString(); } + + /** + * 构造签名串 + * @param params 参数Map + * @return 构造好的签名串 + */ + public static String buildSignString(Map params) { + return params.entrySet().stream() + // 过滤空值字段 + .filter(entry -> entry.getValue() != null && !entry.getValue().trim().isEmpty()) + // 按参数名 ASCII 码升序排序 + .sorted(Map.Entry.comparingByKey()) + // 格式化为 key=value,值进行URL编码 + .map(entry -> { + try { + String encodedValue = URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name()); + return entry.getKey() + "=" + encodedValue; + } catch (Exception e) { + throw new RuntimeException("URL编码失败", e); + } + }) + // 用 & 连接 + .collect(Collectors.joining("&")); + } + + /** + * 使用私钥对签名串进行 RSA-SHA256 签名 + * @param signString 待签名的字符串 + * @param privateKeyStr Base64编码的私钥 + * @return Base64编码的签名 + */ + public static String signWithRsaSha256(String signString, String privateKeyStr) { + try { + // 1. 解码Base64私钥 + byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr); + + // 2. 创建PKCS8编码密钥规范 + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + + // 3. 获取RSA KeyFactory + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + + // 4. 生成私钥对象 + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + + // 5. 创建Signature实例,使用SHA256withRSA算法 + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(privateKey); + + // 6. 更新要签名的数据 + signature.update(signString.getBytes(StandardCharsets.UTF_8)); + + // 7. 执行签名 + byte[] digitalSignature = signature.sign(); + + // 8. 对签名结果进行Base64编码 + return Base64.getEncoder().encodeToString(digitalSignature); + + } catch (Exception e) { + throw new RuntimeException("RSA-SHA256签名失败", e); + } + } + + /** + * 完整的签名生成方法 + * @param params 参数Map + * @param privateKeyStr 私钥字符串 + * @return 签名结果 + */ + public static String generateSignature(Map params, String privateKeyStr) { + // 1. 构造签名串 + String signString = buildSignString(params); + log.info("待签名串: {}", signString); + + // 2. 使用私钥进行RSA-SHA256签名 + String signature = signWithRsaSha256(signString, privateKeyStr); + log.info("生成签名: {}", signature); + + return signature; + } + + } \ No newline at end of file diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/VerifySignatureUtils.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/VerifySignatureUtils.java new file mode 100644 index 000000000..615968bff --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/VerifySignatureUtils.java @@ -0,0 +1,104 @@ +package com.cool.store.utils; + +import lombok.extern.slf4j.Slf4j; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.X509EncodedKeySpec; +import java.util.*; +import java.util.stream.Collectors; +import java.net.URLEncoder; +import java.util.Base64; + +/** + *RSA-SHA256 验签工具类 + * @Author suzhuhong + * @Date 2025/11/12 15:15 + * @Version 1.0 + */ +@Slf4j +public class VerifySignatureUtils { + + /** + * 验证签名 + * @param params 参数Map(包含sign字段) + * @param publicKeyStr Base64编码的公钥 + * @return 验签结果 + */ + public static boolean verifySignature(Map params, String publicKeyStr) { + try { + // 1. 从参数中提取签名(并移除,不参与签名串构造) + String receivedSignature = params.get("sign"); + if (receivedSignature == null || receivedSignature.trim().isEmpty()) { + throw new IllegalArgumentException("签名参数sign不能为空"); + } + + // 2. 创建参数的副本,移除sign字段 + Map paramsForSign = new HashMap<>(params); + paramsForSign.remove("sign"); + + // 3. 构造签名串(与签名方相同的规则) + String signString = buildSignString(paramsForSign); + log.info("验签待签名串: {}", signString); + + // 4. 验证签名 + return verifyRsaSha256(signString, receivedSignature, publicKeyStr); + + } catch (Exception e) { + log.error("验签过程异常: ", e.getMessage()); + return false; + } + } + + /** + * 构造签名串(与签名方相同的逻辑) + */ + private static String buildSignString(Map params) { + return params.entrySet().stream() + .filter(entry -> entry.getValue() != null && !entry.getValue().trim().isEmpty()) + .sorted(Map.Entry.comparingByKey()) + .map(entry -> { + try { + String encodedValue = URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name()); + return entry.getKey() + "=" + encodedValue; + } catch (Exception e) { + throw new RuntimeException("URL编码失败", e); + } + }) + .collect(Collectors.joining("&")); + } + + /** + * RSA-SHA256 验签 + */ + private static boolean verifyRsaSha256(String data, String signature, String publicKeyStr) { + try { + // 1. 解码Base64公钥 + byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr); + + // 2. 创建X509编码密钥规范 + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); + + // 3. 获取RSA KeyFactory + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + + // 4. 生成公钥对象 + PublicKey publicKey = keyFactory.generatePublic(keySpec); + + // 5. 创建Signature实例 + Signature sig = Signature.getInstance("SHA256withRSA"); + sig.initVerify(publicKey); + + // 6. 更新要验证的数据 + sig.update(data.getBytes(StandardCharsets.UTF_8)); + + // 7. 解码收到的签名 + byte[] signatureBytes = Base64.getDecoder().decode(signature); + + // 8. 执行验证 + return sig.verify(signatureBytes); + + } catch (Exception e) { + throw new RuntimeException("RSA-SHA256验签失败", e); + } + } +} \ No newline at end of file