Commit 846058a5 authored by 胡建龙's avatar 胡建龙

20220415 项目搭建

parent e074b984
package com.cmb.service;
import com.alibaba.fastjson.JSONObject;
import com.hand.hap.core.IRequest;
public interface HclcCmbPolyService {
JSONObject getQrcode(IRequest iRequest, JSONObject params);
}
package com.cmb.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.cmb.service.HclcCmbPolyService;
import com.cmb.util.MD5Utils;
import com.cmb.util.SM2Util;
import com.cmb.util.SignatureUtil;
import com.cmb.util.Utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hand.hap.core.IRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@Service
public class HclcCmbPolyServiceImpl implements HclcCmbPolyService {
private static final String url = "https://api.cmburl.cn:8065/polypay/v1.0/mchorders/qrcodeapply"; //UAT
//uat环境商户国密私钥
public static final String privateKey = "D5F2AFA24E6BA9071B54A8C9AD735F9A1DE9C4657FA386C09B592694BC118B38";
//uat环境招行国密公钥
public static final String publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE6Q+fktsnY9OFP+LpSR5Udbxf5zHCFO0PmOKlFNTxDIGl8jsPbbB/9ET23NV+acSz4FEkzD74sW2iiNVHRLiKHg==";
public static final String appId = "8ab74856-8772-45c9-96db-54cb30ab9f74";
public static final String appSecret = "5b96f20a-011f-4254-8be8-9a5ceb2f317f";
@Value("${cmb.version}")
private String version;
@Value("${cmb.encoding}")
private String encoding;
@Value("${cmb.signMethod}")
private String signMethod;
/*币种,非必传,默认156人民币*/
@Value("${cmb.currencyCode}")
private String currencyCode;
@Override
public JSONObject getQrcode(IRequest iRequest, JSONObject params) {
JSONObject result = new JSONObject();
Map<String, String> requestPublicParams = JSONObject.parseObject(params.toJSONString(),Map.class);
// 组装requestBody并加签
String signResult= signMethod(requestPublicParams);
try {
ObjectMapper mapper = new ObjectMapper();
Map<String,String> signResultMap = mapper.readValue(signResult, Map.class);
long currentTimeMills = System.currentTimeMillis() / 1000;
// 组apiSign加密Map
Map<String,String> apiSign = new TreeMap<>();
apiSign.put("appid", appId);
apiSign.put("secret", appSecret);
apiSign.put("sign", signResultMap.get("sign"));
apiSign.put("timestamp", "" + currentTimeMills);
// MD5加密
String MD5Content = SignatureUtil.getSignContent(apiSign);
String apiSignString = MD5Utils.getMD5Content(MD5Content).toLowerCase();
// 组request头部Map
Map<String, String> apiHeader = new HashMap<>();
apiHeader.put("appid", appId);
apiHeader.put("timestamp", "" + currentTimeMills);
apiHeader.put("apisign", apiSignString);
// 发送HTTP post请求
Map<String,String> response = Utils.postForEntity(url, signResult, apiHeader);
/*
版本号 version String(5) 是 固定为0.0.1
编码方式 encoding String(20) 是 固定为UTF-8
签名 sign String(1024) 是
签名方法 signMethod String(2) 是 02:SM2
返回码 returnCode String(7) 是 SUCCESS/FAIL,此字段是通信标识,非交易标识,交易是否成功需要查看respCode来判断。SUCCESS表示商户上送的报文符合规范,FAIL表示报文内的字段不符合规范,包括长度超长、非法字符、签名错误等
以下字段仅在returnCode为SUCCESS时返回
商户号 merId String(32) 是 请求报文中的商户号
商户订单号 orderId String(32) 是 请求报文中的商户订单号
响应码 respCode String(10) 是 业务错误码,成功为SUCCESS,失败为FAIL
以下字段仅在returnCode和respCode为SUCCESS时返回
二维码 qrCode String(300) 是
平台订单号 cmbOrderId String(32) 是 招行生成的订单号
订单发送时间 txnTime String(14) 是 订单生成时间,格式为yyyyMMddHHmmss
以下字段仅在returnCode或respCode为FAIL时返回
错误码 errCode String(32) 是 请求处理失败的错误码信息
应答信息 respMsg String(256) 是 请求处理失败的详细描述信息
*/
System.out.println(mapper.writeValueAsString(response));
// 返回结果验签,验证数据的正确性(安全性)
Boolean checkResult1 = checkSign(mapper.writeValueAsString(response));
if(!checkResult1){
//数据验签失败的处理,重发或者直接告诉前端发送失败
result.put("errCode",response.get("errCode"));
}
//获取响应结果内容
String success = response.get("returnCode");
System.out.println("返回结果:" + success);
result.put("qrcode",response.get("qrcode"));
result.put("respMsg",response.get("respMsg"));
//业务数据的封装。。。
return result;
} catch (Exception e) {
e.printStackTrace();
result.put("errCode","服务器问题");
return result;
}
}
private String signMethod(Map paramsMap){
Map<String, String> requestPublicParams = new TreeMap<>();
String requestStr = null;
try {
//公共请求参数
requestPublicParams.put("version", version); //版本号,固定为0.0.1(必传字段)
requestPublicParams.put("encoding", encoding); //编码方式,固定为UTF-8(必传)
requestPublicParams.put("signMethod", signMethod); //签名方法,固定为02,表示签名方式为国密(必传)
// //业务要素
// Map<String, String> requestTransactionParams = new HashMap<>();
// requestTransactionParams.put("body", "聚合支付测试"); //商户号(必传)
// requestTransactionParams.put("currencyCode", "156"); //交易币种,默认156,目前只支持人民币(156)
// requestTransactionParams.put("merId", "3089991074200KV"); //商户号(必传)
// requestTransactionParams.put("notifyUrl", notifyUrl); //交易通知地址(必传)
// requestTransactionParams.put("orderId", "" + System.currentTimeMillis()); //商户订单号(必传)
// requestTransactionParams.put("payValidTime", "1200"); //支付有效时间
// requestTransactionParams.put("termId", "00774411"); //终端号
// requestTransactionParams.put("txnAmt", "1405"); //交易金额,单位为分(必传)
// requestTransactionParams.put("userId", "N003363806"); //收银员
// requestTransactionParams.put("tradeScene", "OFFLINE"); //交易场景
ObjectMapper mapper = new ObjectMapper();
requestPublicParams.put("biz_content", mapper.writeValueAsString(paramsMap));
System.out.println("加签前的报文内容:" + mapper.writeValueAsString(requestPublicParams));
//对待加签内容进行排序拼接
String signContent= SignatureUtil.getSignContent(requestPublicParams);
//加签
requestPublicParams.put("sign", SM2Util.sm2Sign(signContent, privateKey));
requestStr = mapper.writeValueAsString(requestPublicParams);
System.out.println("加签后的报文内容:" + requestStr);
return requestStr;
}catch (Exception e){
System.out.println("加签发生异常!");
e.printStackTrace();
return requestStr;
}
}
private Boolean checkSign(String string){
System.out.println("要验签的报文内容:" + string);
try {
//验签
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> responseBodyMap = objectMapper.readValue(string, Map.class);
String sign = responseBodyMap.remove("sign");
String contentStr = SignatureUtil.getSignContent(responseBodyMap);
boolean result = SM2Util.sm2Check(contentStr,sign, publicKey);
if (result) {
System.out.println("报文验签成功!");
} else {
System.out.println("报文验签失败!");
}
return result;
}catch (Exception e){
System.out.println("验签发生异常!");
e.printStackTrace();
return false;
}
}
}
package com.cmb.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Utils {
/**
* MD5加密
*
* @param input
* @return
*/
public static String getMD5Content(String input){
try {
// 拿到MD转换器
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] inputByteArray = input.getBytes();
messageDigest.update(inputByteArray);
byte[] resultByteArray = messageDigest.digest();
return byteArrayToHex(resultByteArray);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
/**
* 将字节数组转换成16进组的字符串
*
* @param byteArray
* @return
*/
public static String byteArrayToHex(byte[] byteArray){
// 初始化一个字符数组,用来存放每个16进制字符
char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char[] resultCharArray = new char[byteArray.length * 2];
int index = 0;
// 遍历字节数组,通过位运算,转换成字符放到字符数据中
for (byte b : byteArray){
resultCharArray[index++] = hexDigits[b>>> 4 & 0xf];
resultCharArray[index++] = hexDigits[b & 0xf];
}
return new String(resultCharArray);
}
}
package com.cmb.util;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Base64;
public class SM2Util {
private static final String SM2_KEY_TITLE = "3059301306072a8648ce3d020106082a811ccf5501822d03420004";
public static final String USER_ID = "1234567812345678";
public static String sm2Sign( String content,String privateKey ){
try {
//init privateKey
BCECPrivateKey bcecPrivateKey = BCUtil.getPrivatekeyFromD(new BigInteger(privateKey,16));
byte[] sign = BCUtil.signSm3WithSm2(content.getBytes(),USER_ID.getBytes(),bcecPrivateKey);
return encodeBase64(signRawToAsn1(sign));
}catch (Exception ex){
ex.printStackTrace();
return null;
}
}
/**
*
* @param content
* @param rawSign
* @param publicKey
* @return
*/
public static boolean sm2Check( String content,String rawSign ,String publicKey ){
try {
//init PublicKey
Sm2Vo sm2Vo = parseBase64TRawKey(publicKey);
if( null == sm2Vo ){
return false;
}
BCECPublicKey bcecPublicKey = BCUtil.getPublickeyFromXY(new BigInteger(sm2Vo.getSm2_x(),16),new BigInteger(sm2Vo.getSm2_y(),16));
byte[] sign = signAsn12Raw(decodeBase64(rawSign));
return BCUtil.verifySm3WithSm2(content.getBytes(),USER_ID.getBytes(),sign,bcecPublicKey);
}catch (Exception ex){
ex.printStackTrace();
return false;
}
}
/**
* BASE64格式公钥转换为裸公钥
* @param sm2Key
* @return
*/
private static Sm2Vo parseBase64TRawKey(String sm2Key){
if( null == sm2Key ){
return null;
}
String sm2_asn1 = Hex.toHexString(decodeBase64(sm2Key));
if( !sm2_asn1.startsWith(SM2_KEY_TITLE) ){
return null;
}
String sm2_xy = sm2_asn1.substring(SM2_KEY_TITLE.length(),sm2_asn1.length());
String sm2_x = sm2_xy.substring(0,sm2_xy.length()/2 );
String sm2_y = sm2_xy.substring(sm2_xy.length()/2 ,sm2_xy.length());
return new Sm2Vo(SM2_KEY_TITLE,sm2_x,sm2_y);
}
public static void main(String[] args) {
String sm2_asn1 = "3059301306072A8648CE3D020106082A811CCF5501822D03420004bf552c3a487a87912302ee2dffc6362a9743efb6fe71233efecf174d27c247e089d77bc81de9a1b99577700d667093733339d053840a3fe9f3dbb9b5d26e2f24";
System.out.println(encodeBase64(Hex.decode(sm2_asn1)));
}
/**
* 将字节数组转换为Base64格式字符串
* @param data
* @return
*/
public static String encodeBase64(byte[] data){
return Base64.getEncoder().encodeToString(data);
}
/**
* 将Base64格式字符串转为字节数组
* @param data
* @return
*/
public static byte[] decodeBase64(String data){
return Base64.getDecoder().decode(data);
}
/**
* 将BC SM2 RAW签名值转化为ASN1格式签名值
* @param bcCipTxt
* @return
* @throws Exception
*/
private static byte[] signRawToAsn1(byte[] bcCipTxt) throws Exception {
byte[] netSignCipTxt = new byte[73];
byte[] signR = new byte[32];
byte[] signS = new byte[32];
System.arraycopy(bcCipTxt, 0, signR, 0, 32);
System.arraycopy(bcCipTxt, 32, signS, 0, 32);
//signR补位
int wPos = 4;
netSignCipTxt[0] = 0x30;
netSignCipTxt[2] = 0x02;
if( (signR[0] & 0xFF) >= 128 )
{
netSignCipTxt[wPos - 1] = 0x21;
netSignCipTxt[wPos] = 0x00;
wPos += 1;
}
else
{
netSignCipTxt[wPos - 1] = 0x20;
}
System.arraycopy(signR, 0, netSignCipTxt, wPos, 32);
wPos += 32;
//signS补位
netSignCipTxt[wPos] = 0x02;
wPos += 1;
if( (signS[0] & 0xFF) >= 128 )
{
netSignCipTxt[wPos] = 0x21;
wPos += 1;
netSignCipTxt[wPos] = 0x00;
wPos += 1;
}
else
{
netSignCipTxt[wPos] = 0x20;
wPos += 1;
}
System.arraycopy(signS, 0, netSignCipTxt, wPos, 32);
wPos += 32;
if(70 == wPos)
{
netSignCipTxt[1] = 0x44;
}
else if(71 == wPos)
{
netSignCipTxt[1] = 0x45;
}
else if(72== wPos)
{
netSignCipTxt[1] = 0x46;
}
else
{
throw new Exception("signRawToAsn1 Error!");
}
byte[] resultBytes = new byte[wPos];
System.arraycopy(netSignCipTxt, 0, resultBytes, 0, wPos);
return resultBytes;
}
/**
* 将ASN1格式签名值转化为BC SM2 RAW 签名值
*
* @param signature Asn1格式签名值
* @return byte[] Raw签名值
*/
private static byte[] signAsn12Raw(byte[] signature) throws Exception {
byte[] resultBytes = new byte[64];
//截取signR
int wPos = 3;
if( (signature[wPos] & 0xFF) == 32 )
{
wPos += 1;
}
else if( (signature[wPos] & 0xFF) == 33 )
{
wPos += 2;
}
else
{
throw new Exception("signR length Error!");
}
System.arraycopy(signature, wPos, resultBytes, 0, 32);
wPos += 32;
//截取signS
wPos += 1;
if( (signature[wPos] & 0xFF) == 32 )
{
wPos += 1;
}
else if( (signature[wPos] & 0xFF) == 33 )
{
wPos += 2;
}
else
{
throw new Exception("signS length Error!");
}
System.arraycopy(signature, wPos, resultBytes, 32, 32);
//System.out.println("\nhhh:\n" + ByteToHex(resultBytes));
return resultBytes;
}
}
package com.cmb.util;
import org.apache.commons.codec.binary.Base64;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class SignatureUtil {
public SignatureUtil() {
}
public static String rsaSign(String content, String privateKey, String charset) throws Exception {
try {
PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", new ByteArrayInputStream(privateKey.getBytes()));
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initSign(priKey);
if (null==charset||charset.length()==0) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
byte[] signed = signature.sign();
return new String(Base64.encodeBase64(signed));
} catch (Exception var6) {
throw new RuntimeException("RSAcontent = " + content + "; charset = " + charset, var6);
}
}
public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
if (ins != null && null!=algorithm&&algorithm.length()!=0) {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
byte[] encodedKey= Base64.decodeBase64( copyToString(ins, Charset.forName("UTF-8")));
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
} else {
return null;
}
}
public static String copyToString(InputStream in, Charset charset) throws IOException {
if (in == null) {
return "";
} else {
StringBuilder out = new StringBuilder();
InputStreamReader reader = new InputStreamReader(in, charset);
char[] buffer = new char[4096];
int bytesRead;
while((bytesRead = reader.read(buffer)) != -1) {
out.append(buffer, 0, bytesRead);
}
return out.toString();
}
}
public static String getSignContent(Map<String, String> sortedParams) {
StringBuffer content = new StringBuffer();
List<String> keys = new ArrayList<String>(sortedParams.keySet());
Collections.sort(keys);
int index = 0;
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = sortedParams.get(key);
if (null!=key&&null!=value) {
content.append((index == 0 ? "" : "&") + key + "=" + value);
index++;
}
}
return content.toString();
}
public static boolean rsaCheck(String content, String sign, String publicKey, String charset) throws Exception {
try {
PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(pubKey);
if (null==charset||charset.length()==0) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
return signature.verify(Base64.decodeBase64(sign.getBytes()));
} catch (Exception var6) {
throw new RuntimeException("RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, var6);
}
}
public static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
byte[] encodedKey = Base64.decodeBase64(copyToString(ins, Charset.forName("UTF-8")));
return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
}
}
package com.cmb.util;
public class Sm2Vo {
//标准公钥头
private String sm2_h;
//裸公钥X
private String sm2_x;
//裸公钥Y
private String sm2_y;
public Sm2Vo(String sm2_h, String sm2_x, String sm2_y){
this.sm2_h = sm2_h;
this.sm2_x = sm2_x;
this.sm2_y = sm2_y;
}
public String getSm2_h() {
return sm2_h;
}
public void setSm2_h(String sm2_h) {
this.sm2_h = sm2_h;
}
public String getSm2_x() {
return sm2_x;
}
public void setSm2_x(String sm2_x) {
this.sm2_x = sm2_x;
}
public String getSm2_y() {
return sm2_y;
}
public void setSm2_y(String sm2_y) {
this.sm2_y = sm2_y;
}
}
package com.cmb.util;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.*;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Map;
public class Utils {
static {
disableSslVerification();
}
private static void disableSslVerification() {
try
{
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
public static Map<String, String> postForEntity(String url,String requestBody, Map<String, String> apiHeader){
RestTemplate client = getRestTemplate();
client.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
HttpHeaders headers = getHttpHeaders();
// 以json的方式提交
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("appid", apiHeader.get("appid"));
headers.add("timestamp", apiHeader.get("timestamp"));
headers.add("apisign", apiHeader.get("apisign"));
// 将请求头部和参数合成一个请求
HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
// 执行HTTP请求
Map<String,String> response = client.postForEntity(url,requestEntity,Map.class).getBody();
return response;
}
public static RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static HttpHeaders getHttpHeaders(){
return new HttpHeaders();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment