package cn.juhe;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
/**
* 身份证四要素 v8 - SHA512 版本客户端
*
* 接口文档:https://www.juhe.cn/docs/api/id/550
*
* 加密规范:
* - RSA密钥长度:2048位
* - RSA加密方式:RSA/ECB/OAEPWithSHA-512AndMGF1Padding
* - AES加密方式:AES/GCM/NoPadding
* - 签名方式:HmacSHA512
*/
public class IdCardFourFactorsSHA512Client {
private static final String API_URL = "https://apis.juhe.cn/id_card_four_factors/queryhwsha512";
private static final int AES_KEY_LENGTH = 16; // 16字节 = 128位
private static final int AES_IV_LENGTH = 12; // 12字节用于GCM模式
private static final int GCM_TAG_LENGTH = 128; // 128位标签
private String apiKey;
private String openId;
private PublicKey rsaPublicKey;
private byte[] aesKey;
private byte[] aesIv;
/**
* 构造函数
*
* @param apiKey 聚合数据分配的 API Key
* @param openId 聚合数据分配的 OpenId
* @param publicKeyPath RSA公钥文件路径(PEM格式)
*/
public IdCardFourFactorsSHA512Client(String apiKey, String openId, String publicKeyPath) throws Exception {
this.apiKey = apiKey;
this.openId = openId;
this.rsaPublicKey = loadPublicKey(publicKeyPath);
// 生成随机的AES密钥和IV
SecureRandom random = new SecureRandom();
this.aesKey = new byte[AES_KEY_LENGTH];
this.aesIv = new byte[AES_IV_LENGTH];
random.nextBytes(this.aesKey);
random.nextBytes(this.aesIv);
}
/**
* 加载RSA公钥
*/
private PublicKey loadPublicKey(String publicKeyPath) throws Exception {
String publicKeyPEM = new String(Files.readAllBytes(Paths.get(publicKeyPath)), StandardCharsets.UTF_8);
// 移除PEM头尾和换行符
publicKeyPEM = publicKeyPEM
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s", "");
byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
/**
* 使用RSA OAEP SHA-512加密
*/
private String rsaEncryptWithSHA512(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
// 配置OAEP参数:使用SHA-512作为摘要算法,MGF1使用SHA-512
OAEPParameterSpec oaepParams = new OAEPParameterSpec(
"SHA-512", // 主摘要算法
"MGF1", // 掩码生成函数
new MGF1ParameterSpec("SHA-512"), // MGF1使用的摘要算法
PSource.PSpecified.DEFAULT // P源(默认为空)
);
cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey, oaepParams);
byte[] encrypted = cipher.doFinal(data);
return Base64.getEncoder().encodeToString(encrypted);
}
/**
* 使用AES-GCM加密
*/
private String aesGcmEncrypt(String plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, aesIv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
}
/**
* 计算HmacSHA512签名
* 签名规则:HMAC-SHA512(aesKey + aesIv + apiKey, openId)
*/
private String calculateSignature() throws Exception {
// 拼接消息:aesKey + aesIv + apiKey
byte[] message = new byte[aesKey.length + aesIv.length + apiKey.getBytes(StandardCharsets.UTF_8).length];
System.arraycopy(aesKey, 0, message, 0, aesKey.length);
System.arraycopy(aesIv, 0, message, aesKey.length, aesIv.length);
System.arraycopy(apiKey.getBytes(StandardCharsets.UTF_8), 0, message, aesKey.length + aesIv.length,
apiKey.getBytes(StandardCharsets.UTF_8).length);
// 使用openId作为密钥进行HMAC-SHA512
Mac mac = Mac.getInstance("HmacSHA512");
SecretKeySpec secretKeySpec = new SecretKeySpec(openId.getBytes(StandardCharsets.UTF_8), "HmacSHA512");
mac.init(secretKeySpec);
byte[] hmacBytes = mac.doFinal(message);
// 转换为十六进制字符串(小写)
StringBuilder hexString = new StringBuilder();
for (byte b : hmacBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* 发起身份证四要素验证请求
*
* @param idcard 身份证号码
* @param realname 真实姓名
* @param startDate 证件有效期起始日期(格式:yyyyMMdd,如:20160731)
* @param endDate 证件有效期结束日期(格式:yyyyMMdd,如:20360730,长期填写:29991231)
* @return API响应结果
*/
public String verifyIdCard(String idcard, String realname, String startDate, String endDate) throws Exception {
// 1. 使用RSA OAEP SHA-512加密AES密钥和IV
String encryptedAesKey = rsaEncryptWithSHA512(aesKey);
String encryptedAesIv = rsaEncryptWithSHA512(aesIv);
// 2. 使用AES-GCM加密敏感参数
String encryptedIdcard = aesGcmEncrypt(idcard);
String encryptedRealname = aesGcmEncrypt(realname);
String encryptedStartDate = aesGcmEncrypt(startDate);
String encryptedEndDate = aesGcmEncrypt(endDate);
// 3. 计算签名
String signature = calculateSignature();
// 4. 构建请求参数
StringBuilder postData = new StringBuilder();
postData.append("key=").append(URLEncoder.encode(apiKey, "UTF-8"));
postData.append("&aesKey=").append(URLEncoder.encode(encryptedAesKey, "UTF-8"));
postData.append("&aesIv=").append(URLEncoder.encode(encryptedAesIv, "UTF-8"));
postData.append("&idcard=").append(URLEncoder.encode(encryptedIdcard, "UTF-8"));
postData.append("&realname=").append(URLEncoder.encode(encryptedRealname, "UTF-8"));
postData.append("&start_date=").append(URLEncoder.encode(encryptedStartDate, "UTF-8"));
postData.append("&end_date=").append(URLEncoder.encode(encryptedEndDate, "UTF-8"));
postData.append("&signature=").append(URLEncoder.encode(signature, "UTF-8"));
// 5. 发送HTTP POST请求
URL url = new URL(API_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
// 写入请求体
try (OutputStream os = conn.getOutputStream()) {
byte[] input = postData.toString().getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
// 6. 读取响应
int responseCode = conn.getResponseCode();
StringBuilder response = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
}
return response.toString();
}
/**
* 测试示例
*/
public static void main(String[] args) {
try {
// 配置参数(请替换为实际的值)
String apiKey = "bc87c549f724ae2d204596187xxxxxx"; // 您的API Key
String openId = "JH285250c81e065e255d449d49xxxxxxx"; // 您的OpenId
String publicKeyPath = "/Users/java0904/www/vv.juhe.cn/id_card_four_factors/common/rsa_key/rsa_public_key.pem";
// 创建客户端实例
IdCardFourFactorsSHA512Client client = new IdCardFourFactorsSHA512Client(apiKey, openId, publicKeyPath);
// 调用接口
String result = client.verifyIdCard(
"320382xxxxxxxxxx9", // 身份证号
"特朗普", // 姓名
"20160731", // 有效期开始日期
"20360730" // 有效期结束日期
);
// 输出结果
System.out.println("API响应结果:");
System.out.println(result);
} catch (Exception e) {
System.err.println("请求失败:" + e.getMessage());
e.printStackTrace();
}
}
}
登录