java-微信銀行卡提現(xiàn)和零錢提現(xiàn)記錄


背景

商城項目客戶需要提現(xiàn)功能,之前沒有對接過,也遇到了一些小問題,防止以后出問題,這里做下簡單記錄。第一次寫可能不是特別好,希望大家能夠指出,一起進步。


準備

  • 獲取微信商戶證書

獲取方式:微信商戶后臺-》賬戶中心-》API安全-》申請證書

  • 微信證書轉16進制字符串存儲(感謝 CS*N-代碼風)
       try {
           FileInputStream file = new FileInputStream("E://xxx.p12");
           String s1 = Hex.encodeHexString(StreamUtils.copyToByteArray(file));
           System.out.println(s1);
       } catch (Exception e) {
           e.printStackTrace();
       }

   }
image.png
  • 商戶APIkey

微信商戶后臺-》賬戶中心-》API安全-》API秘鑰(沒有的話生成一個)

使用條件
  • 商戶號(或同主體其他非服務商商戶號)已入駐90日
  • 截止今日回推30天,商戶號(或同主體其他非服務商商戶號)連續(xù)不間斷保持有交易
  • 登錄微信支付商戶平臺-產(chǎn)品中心,開通企業(yè)付款。
    (否則接口會返回沒有權限,而且需要注意的是,未達到條件的微信商戶,無法在產(chǎn)品中心看到該功能)

1.企業(yè)付款到零錢

官方文檔

  • API調用類
 /**
     * 企業(yè)付款到零錢
     * @param vo
     * @param mchApiKey
     * 必傳參數(shù)
     *    vo => mch_appid           申請商戶號的appid或商戶號綁定的appid 微信appid  小程序appid
     *          mchid               商戶號id
     *          partner_trade_no    商戶訂單號
     *          openid              用戶openid
     *          amount              金額 單位 分
     *          desc                備注
     * @return
     */
    public static Map<String,Object> cashTransfers(TransfresParamsVo vo, String mchApiKey){

        Map<String,Object> returnMap = null; //返回結果
        try {
            //計算簽名
            vo.setNonce_str(RandomUtil.randomString(32)); //隨機字符串
            //實體類轉map 工具包 hutool
            Map<String,Object> paramMap = BeanUtil.beanToMap(vo);
            //獲取簽名
            String sign = WechatSginUtils.getWechatPaySignStr(paramMap , mchApiKey); 
            vo.setSign(sign); //設置簽名
            //對象轉xml ture:是否去掉默認報文頭 true去掉
            String apiParamXml = XmlUtils.beanToXml(vo, TransfresParamsVo.class, true);
            logger.info("transfresParams:{}", apiParamXml);
            //提交請求
            String reponseStr = HttpRequest.sendP12PostRequest(WECHAT_TRANSFRES_API, WECHAT_CRET_P12, apiParamXml, vo.getMchid());
            //xml轉map
            returnMap = XmlUtils.xml2map(reponseStr, false);
        } catch (JAXBException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return returnMap;
    }
  • 參數(shù)實體類
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * 微信提現(xiàn)到零錢參數(shù)
 */
@XmlRootElement(name = "xml") //實體轉xml時的根節(jié)點
public class TransfresParamsVo {
    private String mch_appid; //申請商戶號的appid或商戶號綁定的appid 微信appid  小程序appid
    private String mchid; //商戶號id
    private String nonce_str; //隨機字符串
    private String sign; //簽名
    private String partner_trade_no; //商戶訂單號
    private String openid; //用戶openid
    private String check_name = "NO_CHECK"; //校驗用戶姓名選項 NO_CHECK 不校驗真實姓名  FORCE_CHECK:強校驗真實姓名
    private int amount; //金額 單位 分
    private String desc; //備注

    @XmlElement(name="mch_appid")
    public String getMch_appid() {
        return mch_appid;
    }

    public void setMch_appid(String mch_appid) {
        this.mch_appid = mch_appid;
    }

    @XmlElement(name="mchid")
    public String getMchid() {
        return mchid;
    }

    public void setMchid(String mchid) {
        this.mchid = mchid;
    }
    @XmlElement(name="nonce_str")
    public String getNonce_str() {
        return nonce_str;
    }

    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }

    @XmlElement(name="sign")
    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }
    @XmlElement(name="partner_trade_no")
    public String getPartner_trade_no() {
        return partner_trade_no;
    }

    public void setPartner_trade_no(String partner_trade_no) {
        this.partner_trade_no = partner_trade_no;
    }

    @XmlElement(name="openid")
    public String getOpenid() {
        return openid;
    }

    public void setOpenid(String openid) {
        this.openid = openid;
    }
    @XmlElement(name="check_name")
    public String getCheck_name() {
        return check_name;
    }

    public void setCheck_name(String check_name) {
        this.check_name = check_name;
    }
    @XmlElement(name="amount")
    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }
    @XmlElement(name="desc")
    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

2.企業(yè)付款到銀行卡

官方文檔

接口: https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank
是否需要證書:需要

按照常用慣例,得吐槽一下,微信文檔,真的有點折磨人,本來倆個小時搞定的事情,偏偏因為一個小問題多花了一個小時,微信文檔真不適合小白看。
與付款到零錢不同,付款到銀行卡需要提供 銀行卡號收款方用戶名(廢話),且在微信傳參時 RSA加密,而這個RSA加密,需要調用 接口 獲取 微信方的RSA公鑰;然后按照下面的方式,對字符進行處理:

  • 調用獲取RSA公鑰API獲取RSA公鑰,落地成本地文件,假設為public.pem
  • 確定public.pem文件的存放路徑,同時修改代碼中文件的輸入路徑,加載RSA公鑰
  • 用標準的RSA加密庫對敏感信息進行加密,選擇RSA_PKCS1_OAEP_PADDING填充模式
    (eg:Java的填充方式要選 " RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING")
  • 得到進行rsa加密并轉base64之后的密文
  • 將密文傳給微信側相應字段,如付款接口(enc_bank_no/enc_true_name)
    (我星星特m的星星)
  • 獲取微信RSA公鑰
public static void main(String[] args) throws Exception {
       Map<String, Object> map = new HashMap<>();
        map.put("mch_id","***");
        map.put("nonce_str",RandomUtil.randomString(32));
        map.put("sign_type","MD5");
        String sign =WechatSginUtils.getWechatPaySignStr(map, "*****");
        map.put("sign", sign);
        Map<String, Object> map2 = map;
        String apiParamXml = XmlUtils.map2XmlString(map2); //對象轉xml
        //帶證書請求獲取RSA公鑰
        String reponseStr = HttpRequest.sendP12PostRequest("https://fraud.mch.weixin.qq.com/risk/getpublickey", WECHAT_CRET_P12, apiParamXml, "1584991451");
        System.out.println(reponseStr);
    }
  • 獲取到的RSA公鑰轉一下 PKCS#8 這里我用的網(wǎng)頁轉的:傳送門
  • 轉換前:
    -----BEGIN RSA PRIVATE KEY-----
    MIICWwIBAAKBgQC9XrJWcWbj0LhDBzN4uwEOLA/UJKmCkkbvlVgN/qei3e/jVFpx
    R6D3fzshnv5QNB4+BJ/rjRWbbxCJ0djzPxsLS1dJ+bDwagZWZ9hNXARTq4K0uxw6
    Ol5jGD9Od6w5n5uxyaEk9/edvYwMhthIxC/uADRp2pNSutwyLX3bUJnHZwIDAQAB
    AoGANN3S+7788my6hDvmarYKPWKfqKHzkLg1hX0z7/Q/6H/9EIHkHevZTD8AywoQ
    BWQHbVjtLF1ewt3myBMFdiMP8UOx0WVErcyuVRh8AUcRZIEwz73jmLmpRd8fVAzy
    8uoijKvExt/fdu9aIfVmV4nXvL5dDpsoL/mVRDgNCZ+9mMECQQDzWLnqty25mgEs
    73rJ8mhehifwblg44uO+9xpmKZhG3NFZW+beG1iPZklBVlaQ6m53e77VbVotC+LF
    efsaOtU7AkEAxzd3q0REhF/FaFcq9TV3Eu3C4B/aqARKgkpJKiaCC4tnAqny7Rvd
    /anxLBf8DFPYjPMkPrNqXoDA8rAC9TwDxQJBAPF6mHOMdvl5E7WNp6GCxYMXScbT
    GQTKUgoMl8vNdujK84vjIMRDCqyyaftGO/zuRdSXnZWZQCT3aH9iPoWW4EUCQB1r
    NYLXK/8YXYCRDsjzQkhLUDHkwld5er9O1QsicKXfyjB8hGE7ckbZZ8IJMLFpWFtI
    NJwFxrl57gRotacdW7kCP2r3MkJqtHdrjUbaCJJCnHmX9BhYcBhaYS2yGFW9uyNT
    5TGOrrzjz+CXBNrif3JkDbDYv2z/cCgd7kqV1kPl/g==
    -----END RSA PRIVATE KEY-----
  • 轉換后
    -----BEGIN PRIVATE KEY-----
    MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL1eslZxZuPQuEMH
    M3i7AQ4sD9QkqYKSRu+VWA3+p6Ld7+NUWnFHoPd/OyGe/lA0Hj4En+uNFZtvEInR
    2PM/GwtLV0n5sPBqBlZn2E1cBFOrgrS7HDo6XmMYP053rDmfm7HJoST39529jAyG
    2EjEL+4ANGnak1K63DItfdtQmcdnAgMBAAECgYA03dL7vvzybLqEO+Zqtgo9Yp+o
    ofOQuDWFfTPv9D/of/0QgeQd69lMPwDLChAFZAdtWO0sXV7C3ebIEwV2Iw/xQ7HR
    ZUStzK5VGHwBRxFkgTDPveOYualF3x9UDPLy6iKMq8TG399271oh9WZXide8vl0O
    mygv+ZVEOA0Jn72YwQJBAPNYueq3LbmaASzvesnyaF6GJ/BuWDji4773GmYpmEbc
    0Vlb5t4bWI9mSUFWVpDqbnd7vtVtWi0L4sV5+xo61TsCQQDHN3erRESEX8VoVyr1
    NXcS7cLgH9qoBEqCSkkqJoILi2cCqfLtG939qfEsF/wMU9iM8yQ+s2pegMDysAL1
    PAPFAkEA8XqYc4x2+XkTtY2noYLFgxdJxtMZBMpSCgyXy8126Mrzi+MgxEMKrLJp
    +0Y7/O5F1JedlZlAJPdof2I+hZbgRQJAHWs1gtcr/xhdgJEOyPNCSEtQMeTCV3l6
    v07VCyJwpd/KMHyEYTtyRtlnwgkwsWlYW0g0nAXGuXnuBGi1px1buQI/avcyQmq0
    d2uNRtoIkkKceZf0GFhwGFphLbIYVb27I1PlMY6uvOPP4JcE2uJ/cmQNsNi/bP9w
    KB3uSpXWQ+X+
    -----END PRIVATE KEY-----
  • 獲取到pks8私鑰后你可以轉存文件xxx.pem,或者用靜態(tài)常量存儲調用,常量存儲需要去掉-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY-----,只留下中間的字符


    image.png
  • API調用

/**
     * 企業(yè)付款到銀行卡
     * @param vo
     * @param mchApiKey
     * @return
     */
    public static Map<String, Object> cashPayBank(CashPayBankVo vo, String mchApiKey){
        Map<String, Object> returnMap = null; //返回結果
        try {
            //銀行卡用戶名稱 RSA加密
            String enctrueName = WechatRsaUtils.encrypt(vo.getEnc_true_name().getBytes("UTF-8"), WechatRsaUtils.loadPublicKey(WECHAT_PUB_KEY));//rsa公鑰加密 用戶名稱
            String encBankNo = WechatRsaUtils.encrypt(vo.getEnc_bank_no().getBytes("UTF-8"), WechatRsaUtils.loadPublicKey(WECHAT_PUB_KEY)); //rsa公鑰加密 用戶銀行卡
            vo.setEnc_true_name(Base64.encode(enctrueName)); //用戶名稱 base64
            vo.setEnc_bank_no(Base64.encode(encBankNo)); //用戶銀行卡 base64
            vo.setNonce_str(RandomUtil.randomString(32)); //隨機字符串
            //獲取簽名
            vo.setSign(WechatSginUtils.getWechatPaySignStr(BeanUtil.beanToMap(vo), mchApiKey)); //簽名
            //對象轉xml
            String apiParamXml = XmlUtils.beanToXml(vo, CashPayBankVo.class, true); //對象轉xml
            logger.info("cashPayBank:{}", apiParamXml);
            //提交請求
            String reponseStr = HttpRequest.sendP12PostRequest(WECHAT_PAYBANK_API, WECHAT_CRET_P12, apiParamXml, vo.getMch_id());
            //xml轉map
            returnMap = XmlUtils.xml2map(reponseStr, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return returnMap;
    }
  • 參數(shù)實體類
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * 企業(yè)付款到銀行卡 參數(shù)對象
 */
@XmlRootElement(name = "xml")
public class CashPayBankVo {
    private String mch_id; //商戶號
    private String partner_trade_no; //商戶企業(yè)付款單號
    private String nonce_str; //隨機字符串
    private String sign; //簽名
    private String enc_bank_no; //收款方銀行卡號
    private String enc_true_name; //收款方用戶名
    private String bank_code; //收款方開戶行
    private int amount; //付款金額 單位 分
    private String desc; //付款說明

    @XmlElement(name="sign")
    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }

    @XmlElement(name="amount")
    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }

    @XmlElement(name="desc")
    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @XmlElement(name="mch_id")
    public String getMch_id() {
        return mch_id;
    }

    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }

    @XmlElement(name="partner_trade_no")
    public String getPartner_trade_no() {
        return partner_trade_no;
    }

    public void setPartner_trade_no(String partner_trade_no) {
        this.partner_trade_no = partner_trade_no;
    }

    @XmlElement(name="nonce_str")
    public String getNonce_str() {
        return nonce_str;
    }

    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }

    @XmlElement(name="enc_bank_no")
    public String getEnc_bank_no() {
        return enc_bank_no;
    }


    public void setEnc_bank_no(String enc_bank_no) {
        this.enc_bank_no = enc_bank_no;
    }

    @XmlElement(name="enc_true_name")
    public String getEnc_true_name() {
        return enc_true_name;
    }

    public void setEnc_true_name(String enc_true_name) {
        this.enc_true_name = enc_true_name;
    }

    @XmlElement(name="bank_code")
    public String getBank_code() {
        return bank_code;
    }

    public void setBank_code(String bank_code) {
        this.bank_code = bank_code;
    }
}

3.工具類

工具包:Hutool
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.9</version>
</dependency>
  • xml工具類
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.*;

/**
 * xml工具類
 */
public class XmlUtils {

    /**
     * 實體轉xml
     * @param obj
     * @param load
     * @param is_fragment 是否去掉默認報文頭
     * @return
     * @throws JAXBException
     */
    public static String beanToXml(Object obj, Class<?> load, boolean is_fragment) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(load);
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, is_fragment);
        StringWriter writer = new StringWriter();
        marshaller.marshal(obj,writer);
        return writer.toString();
    }

    /**
     * xml字符轉對象
     * @param xmlStr
     * @param load
     * @return
     * @throws JAXBException
     */
    public static Object xmlToBean(String xmlStr, Class<?> load) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(load);
        // 進行將Xml轉成對象的核心接口
        Unmarshaller unmarshaller = context.createUnmarshaller();
        StringReader sr = new StringReader(xmlStr);
        return unmarshaller.unmarshal(sr);
    }

    /**
     * map 轉xml
     * @param map
     * @return
     */
    public static String map2XmlString(Map<String, Object> map) {
        String xmlResult = "";
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        for (String key : map.keySet()) {
            String value = "<![CDATA[" + map.get(key) + "]]>";
            sb.append("<" + key + ">" + value + "</" + key + ">");
        }
        sb.append("</xml>");
        xmlResult = sb.toString();

        return xmlResult;
    }

    /**
     * xml轉map 不帶屬性
     * @param xmlStr
     * @param needRootKey 是否需要在返回的map里加根節(jié)點鍵
     * @return

     */
    public static Map xml2map(String xmlStr, boolean needRootKey) {
        Map<String, Object> map = null;

        try {
            Document doc = DocumentHelper.parseText(xmlStr);
            Element root = doc.getRootElement();
            map = (Map<String, Object>) xml2map(root);
            if(root.elements().size()==0 && root.attributes().size()==0){
                return map;
            }
            if(needRootKey){
                //在返回的map里加根節(jié)點鍵(如果需要)
                Map<String, Object> rootMap = new HashMap<String, Object>();
                rootMap.put(root.getName(), map);
                return rootMap;
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }

        return map;
    }

    /**
     * xml轉map 不帶屬性
     * @param e
     * @return
     */
    private static Map xml2map(Element e) {
        Map map = new LinkedHashMap();
        List list = e.elements();
        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                Element iter = (Element) list.get(i);
                List mapList = new ArrayList();

                if (iter.elements().size() > 0) {
                    Map m = xml2map(iter);
                    if (map.get(iter.getName()) != null) {
                        Object obj = map.get(iter.getName());
                        if (!(obj instanceof List)) {
                            mapList = new ArrayList();
                            mapList.add(obj);
                            mapList.add(m);
                        }
                        if (obj instanceof List) {
                            mapList = (List) obj;
                            mapList.add(m);
                        }
                        map.put(iter.getName(), mapList);
                    } else
                        map.put(iter.getName(), m);
                } else {
                    if (map.get(iter.getName()) != null) {
                        Object obj = map.get(iter.getName());
                        if (!(obj instanceof List)) {
                            mapList = new ArrayList();
                            mapList.add(obj);
                            mapList.add(iter.getText());
                        }
                        if (obj instanceof List) {
                            mapList = (List) obj;
                            mapList.add(iter.getText());
                        }
                        map.put(iter.getName(), mapList);
                    } else
                        map.put(iter.getName(), iter.getText());
                }
            }
        } else
            map.put(e.getName(), e.getText());
        return map;
    }

}
  • 微信簽名工具類
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.jeespring.common.security.MD5Tools;
import com.jeespring.common.weike.wechat.config.WechatApiContext;
import com.jeespring.common.weike.wechat.vo.pay.WechatSignVo;

import java.util.*;

public class WechatSginUtils {

    /**
     * 獲取微信支付簽名
     * @param paramMap
     * @param mchApiKey
     * @return
     * @throws Exception
     */
    public static String getWechatPaySignStr(Map<String,Object> paramMap ,String mchApiKey) throws Exception{
        StringBuffer sbuff = new StringBuffer();
        //字典排序
        List<String> paramsList = new ArrayList<>(paramMap.keySet());
        Collections.sort(paramsList);
        for (int i = 0; i < paramsList.size(); i++){
            String key = paramsList.get(i);
            Object val = paramMap.get(key);
            if (val != null){
                sbuff.append(key);
                sbuff.append("=");
                sbuff.append(val);
                sbuff.append("&");
            }
        }
        //拼接商戶APIkey
        sbuff.append("key=".concat(mchApiKey));
        //簽名MD5轉大寫加密
        String sign = MD5Tools.MD5(sbuff.toString()).toUpperCase();
        return sign;
    }

    /**
     * 預支付返回簽名
     * @param vo
     * @param mchApiKey
     * @throws Exception
     */
    public static void getWechatReturnSgin(WechatSignVo vo , String mchApiKey) throws Exception{
        StringBuffer sbuff = new StringBuffer();
        //隨機字符串
        vo.setNonceStr(RandomUtil.randomString(32));
        //字典排序
        Map<String,Object> paramMap = BeanUtil.beanToMap(vo);
        List<String> paramsList = new ArrayList<>(paramMap.keySet());
        Collections.sort(paramsList);
        for (int i = 0; i < paramsList.size(); i++){
            String key = paramsList.get(i);
            Object val = paramMap.get(key);
            if (val != null){
                if ((WechatApiContext.WECHAT_PAY_SGIN_PACKAGE_SGINSTR.concat("Str")).equals(key)){
                    key = WechatApiContext.WECHAT_PAY_SGIN_PACKAGE_SGINSTR;
                }
                sbuff.append(key);
                sbuff.append("=");
                sbuff.append(val);
                sbuff.append("&");
            }
        }

        //拼接商戶APIkey
        sbuff.append("key=".concat(mchApiKey));
        //簽名MD5轉大寫加密
        String sign = MD5Tools.MD5(sbuff.toString()).toUpperCase();
        vo.setPaySign(sign);
    }



}
  • RSA工具類

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

import static com.jeespring.common.utils.Encodes.decodeBase64;

public class WechatRsaUtils {

    private static String RSA = "RSA";
    private static final int KEYLENGTH = 2048;
    private static final int RESERVESIZE  = 11;

    /**
     * 指定填充模式
     */
    private static final String CIPHERALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING";

    /**
     * 用公鑰加密 <br>
     * 每次加密的字節(jié)數(shù),不能超過密鑰的長度值減去11
     *
     * @param plainBytes 需加密數(shù)據(jù)的byte數(shù)據(jù)
     * @param publicKey  公鑰
     * @return 加密后的byte型數(shù)據(jù)
     */
    public static String encrypt(byte[] plainBytes, PublicKey publicKey) throws Exception {
        int keyByteSize = KEYLENGTH / 8;
        int encryptBlockSize = keyByteSize - RESERVESIZE;
        int nBlock = plainBytes.length / encryptBlockSize;
        if ((plainBytes.length % encryptBlockSize) != 0) {
            nBlock += 1;
        }
        ByteArrayOutputStream outbuf = null;
        try {
            Cipher cipher = Cipher.getInstance(CIPHERALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);

            outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);
            for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
                int inputLen = plainBytes.length - offset;
                if (inputLen > encryptBlockSize) {
                    inputLen = encryptBlockSize;
                }
                byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
                outbuf.write(encryptedBlock);
            }
            outbuf.flush();
            byte[] encryptedData = outbuf.toByteArray();
            return Base64.encodeBase64String(encryptedData);
        } catch (Exception e) {
            throw new Exception("ENCRYPT ERROR:", e);
        } finally {
            try {
                if (outbuf != null) {
                    outbuf.close();
                }
            } catch (Exception e) {
                throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);
            }
        }
    }

    /**
     * 從字符串中加載公鑰
     *
     * @param publicKeyStr 公鑰數(shù)據(jù)字符串
     * @throws Exception 加載公鑰時產(chǎn)生的異常
     */
    public static PublicKey loadPublicKey(String publicKeyStr){
        PublicKey publicKey = null;
        try {
            byte[] buffer = decodeBase64(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            publicKey = keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
        return publicKey;
    }

}

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;

public class HttpRequest {
    /**
     * 向指定URL發(fā)送GET方法的請求
     *
     * @param url
     *            發(fā)送請求的URL
     * @param param
     *            請求參數(shù),請求參數(shù)應該是 name1=value1&name2=value2 的形式。
     * @return URL 所代表遠程資源的響應結果
     */
    public static String sendGet(String url, String param) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            URL realUrl = new URL(urlNameString);
            // 打開和URL之間的連接
            URLConnection connection = realUrl.openConnection();
            // 設置通用的請求屬性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立實際的連接
            connection.connect();
            // 獲取所有響應頭字段
            //Map<String, List<String>> map = connection.getHeaderFields();
            // 遍歷所有的響應頭字段
            //for (String key : map.keySet()) {
               // System.out.println(key + "--->" + map.get(key));
            //}
            // 定義 BufferedReader輸入流來讀取URL的響應
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("發(fā)送GET請求出現(xiàn)異常!" + e.toString());
            result="Down";
            //e.printStackTrace();
        }
        // 使用finally塊來關閉輸入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 向指定 URL 發(fā)送POST方法的請求
     *
     * @param url
     *            發(fā)送請求的 URL
     * @param param
     *            請求參數(shù),請求參數(shù)應該是 name1=value1&name2=value2 的形式。
     * @return 所代表遠程資源的響應結果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打開和URL之間的連接
            URLConnection conn = realUrl.openConnection();
            // 設置通用的請求屬性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 發(fā)送POST請求必須設置如下兩行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 獲取URLConnection對象對應的輸出流
            out = new PrintWriter(conn.getOutputStream());
            // 發(fā)送請求參數(shù)
            out.print(param);
            // flush輸出流的緩沖
            out.flush();
            // 定義BufferedReader輸入流來讀取URL的響應
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("發(fā)送 POST 請求出現(xiàn)異常!"+e.toString());
            //e.printStackTrace();
        }
        //使用finally塊來關閉輸出流、輸入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }

    public static String sendP12PostRequest(String url, String cretCode, String param, String cretkey){
        CloseableHttpClient client = null;
        HttpPost httpPost = null;
        try {
            //解析證書
            byte[] bytes = Hex.decodeHex(cretCode.toCharArray()); //解密出16進制原證書文件內容為字節(jié)數(shù)組
            ByteArrayInputStream input = new ByteArrayInputStream(bytes); //讀取字節(jié)數(shù)組
            KeyStore clientTrustKeyStore = KeyStore.getInstance("PKCS12"); //獲取PKCS12秘鑰庫實例
            clientTrustKeyStore.load(input, cretkey.toCharArray()); //秘鑰解析
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(clientTrustKeyStore, cretkey.toCharArray());

            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("TLSv1");
            sslContext.init(kmf.getKeyManagers(), tm, new java.security.SecureRandom());
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
            client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            httpPost = new HttpPost(url);
            httpPost.setEntity(new StringEntity(param, "utf-8"));
            CloseableHttpResponse response = client.execute(httpPost);
            StatusLine statusLine = response.getStatusLine();
            HttpEntity entity = response.getEntity();
            if (statusLine.getStatusCode() == 200) {
                return EntityUtils.toString(entity, "utf-8");
            }
        } catch (DecoderException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String getParameterMapString(HttpServletRequest request){
        if(request.getQueryString()!=null){
            if(request.getQueryString().length()>0){
                return request.getQueryString();
            }
        }
        Map map = request.getParameterMap();
        java.util.Enumeration enumx = request.getParameterNames();
        String result="";
        while(enumx.hasMoreElements()){
            String  paramName=(String)enumx.nextElement();
            String[]  values=request.getParameterValues(paramName);
            for(int  i=0;i<values.length;i++){
                result+=paramName+"="+values[i]+"&";
            }
        }
        return result;
    }


    private static class MyX509TrustManager implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374