• 微信支付分创建支付分订单+签名+验签


    签名工具类:

    package app.action.signUtil;
    
    import app.action.wx.Sign;
    import com.alibaba.druid.util.StringUtils;
    import okhttp3.HttpUrl;
    import org.apache.commons.codec.binary.Base64;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.security.KeyFactory;
    import java.security.NoSuchAlgorithmException;
    import java.security.PrivateKey;
    import java.security.Signature;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.util.Random;
    
    /**
     * 微信支付分签名工具类
     */
    public class SignUtil {
    
        private static final Log LOGGER = LogFactory.getLog(Sign.class);
    
        //method(请求类型GET、POST url(请求url) body(请求body,GET请求时body传"",POST请求时body为请求参数的json串)  merchantId(商户号) certSerialNo(API证书序列号) keyPath(API证书路径)
        public static String getToken(String method, String url, String body, String merchantId, String certSerialNo, String keyPath) throws Exception {
            String signStr = "";
            HttpUrl httpurl = HttpUrl.parse(url);
            String nonceStr = create_nonce_str();
            long timestamp = System.currentTimeMillis() / 1000;
            if (StringUtils.isEmpty(body)) {
                body = "";
            }
            String message = buildMessage(method, httpurl, timestamp, nonceStr, body);
            String signature = sign(message.getBytes("utf-8"), keyPath);
            signStr = "mchid="" + merchantId
                    + "",nonce_str="" + nonceStr
                    + "",timestamp="" + timestamp
                    + "",serial_no="" + certSerialNo
                    + "",signature="" + signature + """;
            LOGGER.info("Authorization Token:" + signStr);
            return signStr;
        }
    
    
        public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
            String canonicalUrl = url.encodedPath();
            if (url.encodedQuery() != null) {
                canonicalUrl += "?" + url.encodedQuery();
            }
            return method + "
    "
                    + canonicalUrl + "
    "
                    + timestamp + "
    "
                    + nonceStr + "
    "
                    + body + "
    ";
        }
    
        public static String sign(byte[] message, String keyPath) throws Exception {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(getPrivateKey(keyPath));
            sign.update(message);
            return Base64.encodeBase64String(sign.sign());
        }
    
        /**
         * 获取私钥。
         *
         * @param filename 私钥文件路径  (required)
         * @return 私钥对象
         */
        public static PrivateKey getPrivateKey(String filename) throws IOException {
    
            String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
            LOGGER.info("File content:" + content);
            try {
                String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                        .replace("-----END PRIVATE KEY-----", "")
                        .replaceAll("\s+", "");
                LOGGER.info("privateKey:" + privateKey);
                KeyFactory kf = KeyFactory.getInstance("RSA");
                return kf.generatePrivate(
                        new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("当前Java环境不支持RSA", e);
            } catch (InvalidKeySpecException e) {
                LOGGER.info("异常:" + e);
                throw new RuntimeException("无效的密钥格式");
            }
        }
    
        /**
         * 获取32位随机数
         * @return
         */
        public static String create_nonce_str() {
            StringBuilder sb = new StringBuilder();
            Random rand = new Random();
            for (int i = 0; i < 32; i++) {
                sb.append(STR_ARR[rand.nextInt(STR_ARR.length)]);
            }
            return sb.toString();
        }
    
        private static String[] STR_ARR = new String[] { "a", "b", "c", "d", "e",
                "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
                "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E",
                "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
                "S", "T", "U", "V", "W", "X", "Y", "Z", "1", "2", "3", "4", "5",
                "6", "7", "8", "9", "0" };
    }
    

      创建支付分订单:

    package app.action.signUtil;
    
    import cn.hutool.http.Header;
    import cn.hutool.http.HttpRequest;
    
    /**
     * 签名并创建支付分订单
     */
    public class CreateOrderInfoAction {
        public static void main(String[] args) throws Exception {
            // 微信支付分验签+创建支付分订单
            // 版本号:V1.3(版本时间---版本更新时间:2020.03.05)
    
            String reqdata = "{  "out_order_no": "456123JKHDFE125476962"," + // 随机数
                    "  "appid": "wx74654qweqwe46545"," +          // 公众账号ID,(这个一定要确保正确,不然能坑死)
                    "  "service_id": "0000000000000000251514515151512"," +
                    "  "service_introduction": "小米手机"," +
                    "  "time_range": {" +
                    "    "start_time": "OnAccept"" +
                    "  }," +
                    "	"post_payments": [" +
                    "	{" +
                    "		"name": "支付分换手机"," +
                    "		"amount":10," +
                    "		"description": "小米手机"," +
                    "		"count": 1" +
                    "	}" +
                    "]," +
                    "  "risk_fund": {" +
                    "    "name": "ESTIMATE_ORDER_COST"," +
                    "    "amount": 10" +
                    "  }," +
                    "  "notify_url": "https://test.com"," +     // 设置的微信处理完回调你的地址
                    "  "openid": "aqweqweqweqweqweqweqweUY"," + // 用户标识,每个用户是唯一的
                    "  "need_user_confirm": false}"; // 我这商户是免确认模式,顾设置为false
    
            // 创建支付分订单API的请求URL:https://api.mch.weixin.qq.com/v3/payscore/serviceorder
            String URL = "https://api.mch.weixin.qq.com/v3/payscore/serviceorder";
    
            // 商户号(具体以自己的为准)
            String WX_MCHID = "123456789";
    
            // 商户API证书序列号serial_no(具体以自己的为准)
            String WX_SERIAL_NO = "16ASDASDASDASD561564651321ASDASDASD";
    
            // 签名私钥路径(最好不要有中文)
            String keyPath = "F:\wxpert\apiclient_key.pem";
    
            // 下面的post方法是Hutool工具类里的一个方法
    		/*
    			引入以下jar包
    			<dependency>
    				<groupId>cn.hutool</groupId>
    				<artifactId>hutool-all</artifactId>
    				<version>5.2.3</version>
    			</dependency>
    		*/
            String data = HttpRequest.post(URL)
                    .header(Header.CONTENT_TYPE, "application/json")
                    .header(Header.ACCEPT, "application/json")
                    .header("Authorization", "WECHATPAY2-SHA256-RSA2048" + " "
                            + SignUtil.getToken("POST", URL, reqdata, WX_MCHID, WX_SERIAL_NO, keyPath))//头信息,多个头信息多次调用此方法即可
                    .body(reqdata)
                    .execute().body();
            System.out.println("data = " + data);
        }
    }
    

      微信支付分验签公钥获取:

    package app.action.signUtil;
    
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.http.HttpEntity;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    import java.io.*;
    import java.nio.charset.StandardCharsets;
    import java.security.GeneralSecurityException;
    import java.security.cert.X509Certificate;
    import java.util.ArrayList;
    import java.util.List;
    
    import static app.action.wx.Sign.getToken;
    
    /**
     * 获取公钥的方式,并把获取的公钥保存在F:key
     * F:key(自己设置的地址)
     */
    public class PublicKeyUtil {
        private static final Log LOGGER = LogFactory.getLog(PublicKeyUtil.class);
    
        public static void main(String[] args) throws Exception {
            getCertByAPI("123456789","https://api.mch.weixin.qq.com/v3/certificates",2,null,"16ASDASDASDASD561564651321ASDASDASD","F:\wxpert\apiclient_key.pem");
        }
        public static List<X509Certificate> getCertByAPI(String merchantId, String url, int timeout, String body, String certSerialNo, String keyPath) throws UnsupportedEncodingException, Exception{
            String result = "";
            //创建http请求
            HttpGet httpGet = new HttpGet(url);
            httpGet.addHeader("Content-Type", "application/json");
            httpGet.addHeader("Accept", "application/json");
    
            //设置认证信息
            httpGet.setHeader("Authorization", "WECHATPAY2-SHA256-RSA2048"+" "+getToken("GET",url,null,merchantId,certSerialNo,keyPath));
    
            //设置请求器配置:如超时限制等
            RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).build();
            httpGet.setConfig(config);
            List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
            try {
                CloseableHttpClient httpClient = HttpClients.createDefault();
                CloseableHttpResponse response = httpClient.execute(httpGet);
                int statusCode = response.getStatusLine().getStatusCode();
                HttpEntity httpEntity = response.getEntity();
                result = EntityUtils.toString(httpEntity, "UTF-8");
                if(statusCode == 200){
                    LOGGER.info("下载平台证书返回结果:"+result);
                    List<CertificateItem> certList = new ArrayList<CertificateItem>();
                    JSONObject json = JSONObject.parseObject(result);
                    LOGGER.info("查询结果json字符串转证书List:"+json.get("data"));
                    JSONArray jsonArray = (JSONArray)json.get("data");
                    for(int i=0;i<jsonArray.size();i++){
                        CertificateItem certificateItem = new CertificateItem();
                        EncryptedCertificateItem encryptCertificate = new EncryptedCertificateItem();
                        JSONObject bo = JSONObject.parseObject(jsonArray.get(i).toString());
                        certificateItem.setSerial_no(bo.get("serial_no").toString());
                        certificateItem.setEffective_time(bo.get("effective_time").toString());
                        certificateItem.setExpire_time(bo.get("expire_time").toString());
                        JSONObject encryptBo = JSONObject.parseObject(bo.get("encrypt_certificate").toString());
                        encryptCertificate.setAlgorithm(encryptBo.get("algorithm").toString());
                        encryptCertificate.setNonce(encryptBo.get("nonce").toString());
                        encryptCertificate.setAssociated_data(encryptBo.get("associated_data").toString());
                        encryptCertificate.setCiphertext(encryptBo.get("ciphertext").toString());
                        certificateItem.setEncrypt_certificate(encryptCertificate);
                        certList.add(certificateItem);
                    }
                    LOGGER.info("证书List:"+certList);
    
                    List<PlainCertificateItem> plainList = decrypt(certList,response);
                    if(CollectionUtils.isNotEmpty(plainList)){
                        LOGGER.info("平台证书开始保存");
                        x509Certs = saveCertificate(plainList);
                    }
                }
                response.close();
                httpClient.close(); //throw
                return x509Certs;
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.error("下载平台证书返回结果:"+e);
            }
            return x509Certs;
        }
    
        private static List<PlainCertificateItem> decrypt(List<CertificateItem> certList,CloseableHttpResponse response) throws GeneralSecurityException, IOException {
            List<PlainCertificateItem> plainCertificateList = new ArrayList<PlainCertificateItem>();
    //        qweqweqweqweqweqweqweqwApi3为自己的apiv3秘药
            AesUtil aesUtil = new AesUtil(("qweqweqweqweqweqweqweqwApi3").getBytes(StandardCharsets.UTF_8));
            for(CertificateItem item:certList){
                PlainCertificateItem bo = new PlainCertificateItem();
                bo.setSerialNo(item.getSerial_no());
                bo.setEffectiveTime(item.getEffective_time());
                bo.setExpireTime(item.getExpire_time());
                LOGGER.info("平台证书密文解密");
                bo.setPlainCertificate(aesUtil.decryptToString(item.getEncrypt_certificate().getAssociated_data().getBytes(StandardCharsets.UTF_8),
                        item.getEncrypt_certificate().getNonce().getBytes(StandardCharsets.UTF_8), item.getEncrypt_certificate().getCiphertext()));
                LOGGER.info("平台证书公钥明文:"+bo.getPlainCertificate());
                plainCertificateList.add(bo);
            }
            return plainCertificateList;
        }
    
        //证书保存地址
        private static List<X509Certificate> saveCertificate(List<PlainCertificateItem> cert) throws IOException {
            List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
            // 公钥保存在F:key文件夹下名字为publicKey.pem
            File file = new File("F:\key");
            file.mkdirs();
            for (PlainCertificateItem item : cert) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(item.getPlainCertificate().getBytes(StandardCharsets.UTF_8));
                X509Certificate x509Cert = PemUtil.loadCertificate(inputStream);
                x509Certs.add(x509Cert);
                String outputAbsoluteFilename = file.getAbsolutePath() + File.separator + "publicKey.pem";
                try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputAbsoluteFilename), StandardCharsets.UTF_8))) {
                    writer.write(item.getPlainCertificate());
                }
                LOGGER.info("输出证书文件目录:"+outputAbsoluteFilename);
            }
            return x509Certs;
        }
    
    }
    

      微信支付分验签工具类:

    package app.action.signUtil;
    
    import org.apache.http.Header;
    import org.apache.http.HttpEntity;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.security.PublicKey;
    import java.security.Signature;
    import java.security.cert.CertificateException;
    import java.security.cert.CertificateFactory;
    import java.security.cert.X509Certificate;
    import java.util.Base64;
    import java.util.HashMap;
    import java.util.Map;
    
    import static app.action.signUtil.SignUtil.getToken;
    
    
    /**
     * 微信支付分验签工具类
     */
    public class CheckSignUtil {
        private static String CHARSET_ENCODING = "UTF-8";
        private static String ALGORITHM = "SHA256withRSA";
    
        public static void main(String[] args) throws Exception {
            getCheckSign("123456789","https://api.mch.weixin.qq.com/v3/certificates",2,null,"16ASDASDASDASD561564651321ASDASDASD","F:\wxpert\apiclient_key.pem");
        }
    
        public static String getCheckSign(String merchantId, String url, int timeout, String body, String certSerialNo, String keyPath) throws UnsupportedEncodingException, Exception{
            String result = "";
            //创建http请求
            HttpGet httpGet = new HttpGet(url);
            httpGet.addHeader("Content-Type", "application/json");
            httpGet.addHeader("Accept", "application/json");
    
            //设置认证信息
            httpGet.setHeader("Authorization", "WECHATPAY2-SHA256-RSA2048"+" "+getToken("GET",url,null,merchantId,certSerialNo,keyPath));
    
            //设置请求器配置:如超时限制等
            RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).build();
            httpGet.setConfig(config);
            try {
                CloseableHttpClient httpClient = HttpClients.createDefault();
                CloseableHttpResponse response = httpClient.execute(httpGet);
                HttpEntity httpEntity = response.getEntity();
                result = EntityUtils.toString(httpEntity, "UTF-8");
                Header[] allHeaders = response.getAllHeaders();
                Map<String, String> headers = new HashMap<String, String>();
                for(int i=0;i<allHeaders.length;i++){
                    String key = allHeaders[i].getName();
                    String value = allHeaders[i].getValue();
                    headers.put(key, value);
                }
                String Nonce = headers.get("Wechatpay-Nonce");
                String Signature = headers.get("Wechatpay-Signature");
                String Timestamp = headers.get("Wechatpay-Timestamp");
                String srcData = Timestamp+"
    "
                        + Nonce+"
    "
                        + result+"
    ";
                // publicKeyPath 为自己公钥保存的地址如:F:keypublicKey.pem
                String publicKeyPath = "F:\key\publicKey.pem";
                boolean verify = verify(srcData, Signature, publicKeyPath);
                System.out.println("验签结果 = " + verify);
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 验签
         * @param srcData
         * @param signedData
         * @param publicKeyPath
         * @return
         */
        public static boolean verify(String srcData, String signedData, String publicKeyPath){
            if(srcData==null || signedData==null || publicKeyPath==null){
                return false;
            }
            try {
                PublicKey publicKey = readPublic(publicKeyPath);
                Signature sign = Signature.getInstance(ALGORITHM);
                sign.initVerify(publicKey);
                sign.update(srcData.getBytes(CHARSET_ENCODING));
                return sign.verify(Base64.getDecoder().decode(signedData));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    
    
        /**
         * 读取公钥
         * @param publicKeyPath
         * @return
         */
        private static PublicKey readPublic(String publicKeyPath){
            if(publicKeyPath==null){
                return null;
            }
            PublicKey pk = null;
            FileInputStream bais = null;
            try {
                CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
                bais = new FileInputStream(publicKeyPath);
                X509Certificate cert = (X509Certificate)certificatefactory.generateCertificate(bais);
                pk = cert.getPublicKey();
            } catch (CertificateException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally{
                if(bais != null){
                    try {
                        bais.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return pk;
        }
    }
    

      以下是需要的工具及实体类:

    package app.action.signUtil;
    
    import javax.crypto.Cipher;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.spec.GCMParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.IOException;
    import java.security.GeneralSecurityException;
    import java.security.InvalidAlgorithmParameterException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.util.Base64;
    
    public class AesUtil {
    
        static final int KEY_LENGTH_BYTE = 32;
        static final int TAG_LENGTH_BIT = 128;
        private final byte[] aesKey;
    
        public AesUtil(byte[] key) {
            if (key.length != KEY_LENGTH_BYTE) {
                throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
            }
            this.aesKey = key;
        }
    
        public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
                throws GeneralSecurityException, IOException {
            try {
                Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    
                SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
                GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
    
                cipher.init(Cipher.DECRYPT_MODE, key, spec);
                cipher.updateAAD(associatedData);
    
                return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
            } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
                throw new IllegalStateException(e);
            } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
                throw new IllegalArgumentException(e);
            }
        }
    }
    

      

    package app.action.signUtil;
    
    
    //平台证书item
    public class CertificateItem {
    
        //加密的平台证书序列号
        private String serial_no;
    
        //加密的平台证书序列号
        private String effective_time;
    
        //证书弃用时间
        private String expire_time;
    
        //证书加密信息
        private EncryptedCertificateItem encrypt_certificate;
    
        public String getSerial_no() {
            return serial_no;
        }
    
        public void setSerial_no(String serial_no) {
            this.serial_no = serial_no;
        }
    
        public String getEffective_time() {
            return effective_time;
        }
    
        public void setEffective_time(String effective_time) {
            this.effective_time = effective_time;
        }
    
        public String getExpire_time() {
            return expire_time;
        }
    
        public void setExpire_time(String expire_time) {
            this.expire_time = expire_time;
        }
    
        public EncryptedCertificateItem getEncrypt_certificate() {
            return encrypt_certificate;
        }
    
        public void setEncrypt_certificate(EncryptedCertificateItem encrypt_certificate) {
            this.encrypt_certificate = encrypt_certificate;
        }
    }
    

      

    package app.action.signUtil;
    
    public class EncryptedCertificateItem {
    
        //加密的平台证书序列号
        private String algorithm;
    
        //加密的平台证书序列号
        private String nonce;
    
        //证书弃用时间
        private String associated_data;
    
        //证书弃用时间
        private String ciphertext;
    
        public String getAlgorithm() {
            return algorithm;
        }
    
        public void setAlgorithm(String algorithm) {
            this.algorithm = algorithm;
        }
    
        public String getNonce() {
            return nonce;
        }
    
        public void setNonce(String nonce) {
            this.nonce = nonce;
        }
    
        public String getAssociated_data() {
            return associated_data;
        }
    
        public void setAssociated_data(String associated_data) {
            this.associated_data = associated_data;
        }
    
        public String getCiphertext() {
            return ciphertext;
        }
    
        public void setCiphertext(String ciphertext) {
            this.ciphertext = ciphertext;
        }
    }
    

      

    package app.action.signUtil;
    
    //证书明文item
    public class PlainCertificateItem {
    
        private String serialNo;
    
        private String effectiveTime;
    
        private String expireTime;
    
        private String plainCertificate;
    
        public String getSerialNo() {
            return serialNo;
        }
    
        public void setSerialNo(String serialNo) {
            this.serialNo = serialNo;
        }
    
        public String getEffectiveTime() {
            return effectiveTime;
        }
    
        public void setEffectiveTime(String effectiveTime) {
            this.effectiveTime = effectiveTime;
        }
    
        public String getExpireTime() {
            return expireTime;
        }
    
        public void setExpireTime(String expireTime) {
            this.expireTime = expireTime;
        }
    
        public String getPlainCertificate() {
            return plainCertificate;
        }
    
        public void setPlainCertificate(String plainCertificate) {
            this.plainCertificate = plainCertificate;
        }
    }
    

      

  • 相关阅读:
    技术汇总:第十五章:MyBatisGenerator数据层代码生成
    java程序员怎么创建自己的网站:第四章:做个网站引入广告赚点小钱
    Spring Cloud:第二章:eureka服务发现
    How to Install and Use Wireshark on Ubuntu
    inotify_add_watch使用注意
    Consul 注册中心
    Spring Security 实现用户认证(Authentication)和用户授权(Authorization)
    数据备份还原
    Ribbon 服务消费
    Spring Boot Admin系统服务监控
  • 原文地址:https://www.cnblogs.com/nginxTest/p/12697136.html
Copyright © 2020-2023  润新知