fix:自动火试金接口签名测试
This commit is contained in:
@@ -0,0 +1,213 @@
|
|||||||
|
package com.zt.plat.module.qms;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用的签名、加解密工具类
|
||||||
|
*/
|
||||||
|
public final class CryptoSignatureUtils {
|
||||||
|
|
||||||
|
public static final String ENCRYPT_TYPE_AES = "AES";
|
||||||
|
public static final String ENCRYPT_TYPE_DES = "DES";
|
||||||
|
public static final String SIGNATURE_TYPE_MD5 = "MD5";
|
||||||
|
public static final String SIGNATURE_TYPE_SHA256 = "SHA256";
|
||||||
|
|
||||||
|
private static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding";
|
||||||
|
public static final String SIGNATURE_FIELD = "signature";
|
||||||
|
|
||||||
|
private CryptoSignatureUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 AES 密钥(SecretKeySpec)
|
||||||
|
*
|
||||||
|
* @param password 密钥字符串
|
||||||
|
* @return SecretKeySpec
|
||||||
|
*/
|
||||||
|
public static SecretKeySpec getSecretKey(String password) {
|
||||||
|
try {
|
||||||
|
KeyGenerator kg = KeyGenerator.getInstance("AES");
|
||||||
|
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
|
||||||
|
random.setSeed(password.getBytes(StandardCharsets.UTF_8));
|
||||||
|
kg.init(128, random);
|
||||||
|
SecretKey secretKey = kg.generateKey();
|
||||||
|
return new SecretKeySpec(secretKey.getEncoded(), "AES");
|
||||||
|
} catch (NoSuchAlgorithmException ex) {
|
||||||
|
throw new IllegalStateException("Failed to generate AES secret key", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对称加密(Base64 格式输出)
|
||||||
|
*
|
||||||
|
* @param plaintext 明文内容
|
||||||
|
* @param key 密钥
|
||||||
|
* @param type 加密类型,支持 AES、DES
|
||||||
|
* @return 密文(Base64 格式)
|
||||||
|
*/
|
||||||
|
public static String encrypt(String plaintext, String key, String type) {
|
||||||
|
if (ENCRYPT_TYPE_AES.equalsIgnoreCase(type)) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));
|
||||||
|
byte[] result = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Base64.getEncoder().encodeToString(result);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalStateException("Failed to encrypt using AES", ex);
|
||||||
|
}
|
||||||
|
} else if (ENCRYPT_TYPE_DES.equalsIgnoreCase(type)) {
|
||||||
|
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] desKey = new byte[8];
|
||||||
|
System.arraycopy(keyBytes, 0, desKey, 0, Math.min(keyBytes.length, desKey.length));
|
||||||
|
byte[] encrypted = SecureUtil.des(desKey).encrypt(plaintext.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Base64.getEncoder().encodeToString(encrypted);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported encryption type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对称解密(输入为 Base64 格式密文)
|
||||||
|
*
|
||||||
|
* @param ciphertext 密文内容(Base64 格式)
|
||||||
|
* @param key 密钥
|
||||||
|
* @param type 加密类型,支持 AES、DES
|
||||||
|
* @return 明文内容
|
||||||
|
*/
|
||||||
|
public static String decrypt(String ciphertext, String key, String type) {
|
||||||
|
if (ciphertext == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (ENCRYPT_TYPE_AES.equalsIgnoreCase(type)) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));
|
||||||
|
byte[] decoded = decodeBase64Ciphertext(ciphertext);
|
||||||
|
byte[] result = cipher.doFinal(decoded);
|
||||||
|
return new String(result, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalStateException("Failed to decrypt using AES", ex);
|
||||||
|
}
|
||||||
|
} else if (ENCRYPT_TYPE_DES.equalsIgnoreCase(type)) {
|
||||||
|
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] desKey = new byte[8];
|
||||||
|
System.arraycopy(keyBytes, 0, desKey, 0, Math.min(keyBytes.length, desKey.length));
|
||||||
|
byte[] decoded = decodeBase64Ciphertext(ciphertext);
|
||||||
|
byte[] decrypted = SecureUtil.des(desKey).decrypt(decoded);
|
||||||
|
return new String(decrypted, StandardCharsets.UTF_8);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported encryption type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证请求签名
|
||||||
|
*
|
||||||
|
* @param reqMap 请求参数 Map
|
||||||
|
* @param type 签名算法类型,支持 MD5、SHA256
|
||||||
|
* @return 签名是否有效
|
||||||
|
*/
|
||||||
|
public static boolean verifySignature(Map<String, Object> reqMap, String type) {
|
||||||
|
Map<String, Object> sortedMap = new TreeMap<>(reqMap);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Map.Entry<String, Object> entry : sortedMap.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
if (SIGNATURE_FIELD.equals(key) || value == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sb.append(key).append('=');
|
||||||
|
sb.append(value);
|
||||||
|
sb.append('&');
|
||||||
|
}
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
|
}
|
||||||
|
String provided = (String) reqMap.get(SIGNATURE_FIELD);
|
||||||
|
if (provided == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String computed;
|
||||||
|
if (SIGNATURE_TYPE_MD5.equalsIgnoreCase(type)) {
|
||||||
|
computed = SecureUtil.md5(sb.toString());
|
||||||
|
} else if (SIGNATURE_TYPE_SHA256.equalsIgnoreCase(type)) {
|
||||||
|
computed = SecureUtil.sha256(sb.toString());
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported signature type: " + type);
|
||||||
|
}
|
||||||
|
return provided.equalsIgnoreCase(computed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] decodeBase64Ciphertext(String ciphertext) {
|
||||||
|
IllegalArgumentException last = null;
|
||||||
|
for (String candidate : buildBase64Candidates(ciphertext)) {
|
||||||
|
if (candidate == null || candidate.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Base64.getDecoder().decode(candidate);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
last = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw last != null ? last : new IllegalArgumentException("Invalid Base64 content");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> buildBase64Candidates(String ciphertext) {
|
||||||
|
Set<String> candidates = new LinkedHashSet<>();
|
||||||
|
if (ciphertext == null) {
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
String trimmed = ciphertext.trim();
|
||||||
|
candidates.add(trimmed);
|
||||||
|
|
||||||
|
String withoutWhitespace = stripWhitespace(trimmed);
|
||||||
|
candidates.add(withoutWhitespace);
|
||||||
|
|
||||||
|
if (trimmed.indexOf(' ') >= 0) {
|
||||||
|
String restoredPlus = trimmed.replace(' ', '+');
|
||||||
|
candidates.add(restoredPlus);
|
||||||
|
candidates.add(stripWhitespace(restoredPlus));
|
||||||
|
}
|
||||||
|
|
||||||
|
String urlNormalised = withoutWhitespace
|
||||||
|
.replace('-', '+')
|
||||||
|
.replace('_', '/');
|
||||||
|
candidates.add(urlNormalised);
|
||||||
|
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stripWhitespace(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
boolean hasWhitespace = false;
|
||||||
|
for (int i = 0; i < value.length(); i++) {
|
||||||
|
if (Character.isWhitespace(value.charAt(i))) {
|
||||||
|
hasWhitespace = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasWhitespace) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder(value.length());
|
||||||
|
for (int i = 0; i < value.length(); i++) {
|
||||||
|
char ch = value.charAt(i);
|
||||||
|
if (!Character.isWhitespace(ch)) {
|
||||||
|
sb.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,6 @@ import com.alibaba.fastjson.JSONObject;
|
|||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.zt.plat.framework.common.util.security.CryptoSignatureUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@@ -17,9 +13,6 @@ import java.net.http.HttpRequest;
|
|||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -31,9 +24,7 @@ public class DatabusApiInvocationExample {
|
|||||||
private static final String APP_SECRET = "tjDKCUGNEDR9yNgbxIsvtXsRMuQK+tj1HNEMpgjJOPU=";
|
private static final String APP_SECRET = "tjDKCUGNEDR9yNgbxIsvtXsRMuQK+tj1HNEMpgjJOPU=";
|
||||||
private static final String ENCRYPTION_TYPE = CryptoSignatureUtils.ENCRYPT_TYPE_AES;
|
private static final String ENCRYPTION_TYPE = CryptoSignatureUtils.ENCRYPT_TYPE_AES;
|
||||||
private static final String TARGET_API = "http://172.16.46.62:30081/admin-api/databus/api/portal";
|
private static final String TARGET_API = "http://172.16.46.62:30081/admin-api/databus/api/portal";
|
||||||
private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
|
private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
|
||||||
.connectTimeout(Duration.ofSeconds(5))
|
|
||||||
.build();
|
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
private static final PrintStream OUT = buildConsolePrintStream();
|
private static final PrintStream OUT = buildConsolePrintStream();
|
||||||
public static final String ZT_APP_ID = "ZT-App-Id";
|
public static final String ZT_APP_ID = "ZT-App-Id";
|
||||||
@@ -47,10 +38,10 @@ public class DatabusApiInvocationExample {
|
|||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
OUT.println("=== GET 请求示例 ===");
|
OUT.println("=== GET 请求示例 ===");
|
||||||
executeGetExample();
|
// executeGetExample();
|
||||||
// OUT.println();
|
// OUT.println();
|
||||||
// OUT.println("=== POST 请求示例 ===");
|
// OUT.println("=== POST 请求示例 ===");
|
||||||
// executePostExample();
|
executePostExample();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void executeGetExample() throws Exception {
|
private static void executeGetExample() throws Exception {
|
||||||
@@ -72,16 +63,12 @@ public class DatabusApiInvocationExample {
|
|||||||
// .header("ZT-Auth-Token", "a75c0ea94c7f4a88b86b60bbc0b432c3")
|
// .header("ZT-Auth-Token", "a75c0ea94c7f4a88b86b60bbc0b432c3")
|
||||||
.GET()
|
.GET()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||||
printResponse(response);
|
printResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void executePostExample() throws Exception {
|
private static void executePostExample() throws Exception {
|
||||||
Map<String, Object> queryParams = new LinkedHashMap<>();
|
Map<String, Object> queryParams = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
String jsonStr = "{\n" +
|
String jsonStr = "{\n" +
|
||||||
" \"taskId\": \"1994323064365080578\",\n" +
|
" \"taskId\": \"1994323064365080578\",\n" +
|
||||||
" \"sampleList\": [\n" +
|
" \"sampleList\": [\n" +
|
||||||
|
|||||||
Reference in New Issue
Block a user