微信实名认证接口

微信实名认证接口和微信图片上传

本文章为原创 如有需要转载 请说明转载处链接: https://blog.csdn.net/qq_25496167/article/details/100869289 . 谢谢

关于微信的实名认证接口,微信官方的文档,估计又不少人会看着很懵逼,根本无从下手,小编也是第一次接触这个微信的接口,踩了很多坑,花了很多的时间,才整理出的代码,希望能帮助各位,早点完成这个接口的对接。

注意:在实现微信的实名认证之前要把该上传到微信的图片给上传好,下面我会一起跟大家说怎么做上传图片到微信

首先根据官方的文档来 先做好准备的工作, 把该弄的证书,文件先整理好,不然实现不了接口对接
1:商户的私钥和证书和公钥**
(关于商户证书和私钥到这个连接了解获取链接: https://kf.qq.com/faq/161222NneAJf161222U7fARv.html
以下是通过微信申请的商户的公私钥和证书
在这里插入图片描述
注意:前面的是商户号用来封装请求头部的 authorization 用的
apiclient_cert.p12 商户证书,包含商户的商户号、公司名称、公钥信息的证书
apiclient_cert.pem 是商户的公钥,可以通过这个公钥获取商户证书的序列号
apiclient_key.pem 是商户的私钥,微信返回给我们的字段是通过商户的公钥加密的,我们要用这个私钥来解密,

商户签名使用商户私钥,证书序列号包含在请求HTTP头部的Authorization的serial_no

微信支付签名使用微信支付平台私钥,证书序列号包含在应答HTTP头部的Wechatpay-Serial

商户上送敏感信息时使用微信支付平台公钥加密,证书序列号包含在请求HTTP头部的Wechatpay-Serial

至于商户的序列号怎么获取的呢? 我们可以通过这个链接: https://myssl.com/cert_decode.html 把我们的apiclient_cert.pem 放进去就可以获取我们的商户序列号了,然后再把获取到的商户号放在封装签名的serial_no

那么我们就先来封装签名串

HTTP请求方法\n
URL\n
请求时间戳\n
请求随机串\n
请求报文主体\n

请求的HTTP方法是根据请求接口路径的请求方式(POST,GET,PUT )
在这里插入图片描述
签名中的URL是根据微信的请求接口路径后面的也就是截图出来的部分

请求时间戳的时间戳 可通过这个方法生成

	Long.toString(System.currentTimeMillis() / 1000)

请求的随机串我们可以通过RandomStringUtils来生成

	// 位数,是否纯数字  是否有字母
	RandomStringUtils.random(25, true, true)

签名串一共有五行,每一行为一个参数。行尾以\n结束
注意:GET的请求方式中是没有查询参数(也就是没有请求的报文,但是也要加个\n不然签名会过不了的)

以下就是拼接好的签名串

 //Post的请求的内容拼接组成签名
    public static String getPostSig(String urlSuffix, JSONObject jsonObject){
        //拼签名串
        StringBuffer sb = new StringBuffer();
        sb.append("POST").append("\n");
        sb.append(urlSuffix).append("\n");
        sb.append(timestamp).append("\n");
        sb.append(nonce_str).append("\n");
        sb.append(jsonObject).append("\n"); //请求的内容
        System.out.println("签名原串:" + sb.toString());
        return  sb.toString();
    }
 //Get的请求的内容拼接组成签名
    public static String getGetSig(String urlSuffix){
        //拼签名串
        StringBuffer sb = new StringBuffer();
        sb.append("GET").append("\n");
        sb.append(urlSuffix).append("\n");
        sb.append(timestamp).append("\n");
        sb.append(nonce_str).append("\n");
        sb.append("\n"); //请求的内容
        System.out.println("签名原串:" + sb.toString());
        return  sb.toString();
    }

我们把拼接好的签名串通过Signature这个类进行SHA-256 with RSA作为数字签名的算法和Base64加密

	
    /**
     * @param data 加密的签名串
     * @param priKey 用来加密的签名的商户私钥路径
     * @return
     */
    public static String signRSA(String data, String priKey) {
        try {
            InputStream inputStream = new FileInputStream(priKey);
            //加载商户私钥
            PrivateKey privateKey = loadPrivateKey(inputStream);
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(data.getBytes("UTF-8"));
            byte[] sign = signature.sign();
            return Base64.getEncoder().encodeToString(sign);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (SignatureException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取私钥
     * @param inputStream
     * @return
     */
    public static PrivateKey loadPrivateKey(InputStream inputStream) {
        try {
            ByteArrayOutputStream array = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                array.write(buffer, 0, length);
            }
            String privateKey = array.toString("utf-8")
                    .replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(java.util.Base64.getMimeDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        } catch (IOException e) {
            throw new RuntimeException("无效的密钥");
        }
    }

然后我们在构造请求头部信息的Authorization的值
在这里插入图片描述

   /**
     * @param mchid 发起请求的商户号
     * @param nonce_str 请求随机串
     * @param sign 加密后的签名串
     * @param timestamp  时间戳
     * @param serial_no 商户API证书序列号
     * @return
     */
    public String getAuthorization(String mchid,String nonce_str,String sign,String timestamp,String serial_no) {
    	String method = "WECHATPAY2-SHA256-RSA2048" ;
    	return method+"\n"
    		 + serial_no+"\n"
    	     + timestamp+"\n"
    	     + nonce_str+"\n"
    	     + sign+"\n";
    }

我们已经把签名串并且把要请求的签名串给加密好了也拼接完成了请求头部所需要的Authorization的值了接下来我们要设置请求的头部信息了但是再此之前我要获取微信平台证书,因为我们要使用平台证书的序列号

	

    /**
     * 获取平台证书
     * @return
     * @throws IOException
     */
    public static  String terrace() throws IOException {
        //请求路径
        String urlSuffix = "/v3/certificates";

        //获取拼接的签名
        String sig = getGetSig(urlSuffix);

        //签名加密
        String sign = signRSA(sig, rsaPrivateKeyFile);

        //拼装http头的Authorization内容
        String authorization =  getAuthorization(mchid, nonce_str,timestamp,serial_no);
        System.out.println("authorization值:" + authorization);

        HttpURLConnection conn = getHttpConnection("GET","https://api.mch.weixin.qq.com/v3/certificates",authorization,"application/json");
        conn.connect();

        System.out.println("接口返回头信息:");
        Map<String, List<String>> responseHeader = conn.getHeaderFields();
        for (Map.Entry<String, List<String>> entry : responseHeader.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        //打印返回内容
        int responseCode = conn.getResponseCode();
        String rescontent ="";
        if(responseCode == HttpURLConnection.HTTP_OK){
            //成功
            rescontent =getResult(conn.getInputStream());
            System.out.println(rescontent);
        }else{
            //失败
            rescontent =getResult(conn.getErrorStream());
            System.out.println(rescontent);
        }
        return  rescontent;
    }

以下就是获取的证书内容
在这里插入图片描述
然后把获取的ciphertext字段给解密



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);
        }
    }
}

然后把证书给解密了


    /**
     * 解密证书
     */
    public static void certificateDecode() throws IOException, GeneralSecurityException {
        String decode  =terrace();
        JSONObject json  = JSON.parseObject(decode);
        JSONArray data = (JSONArray) json.get("data");
        JSONObject resStr = (JSONObject) data.get(0);
        JSONObject encrypt_certificate = resStr.getJSONObject("encrypt_certificate");

        String  ciphertext = encrypt_certificate.get("ciphertext").toString();
        String  nonce = encrypt_certificate.getString("nonce");
        String  associated_data = encrypt_certificate.getString("associated_data");

        AesUtil aesUtil = new AesUtil("apiv3密钥");
        System.out.println(aesUtil.decryptToString(associated_data.getBytes(),nonce.getBytes(),ciphertext));
    }

该字段解密后会是公钥也是证书,关于如何把这个转成证书?我们把获取的这个公钥放在一个新建的记事本文件然后把后缀名给改成.pem,这个这个文件就是微信的平台证书了,把这个证书通过链接: https://myssl.com/cert_decode.html 给解密出来,获取到我们需要的平台证书的序列号,然后我还要把这个文件保存好,因为我们等等还需要这个平台证书里面的公钥来验签

然后把获取到的平台证书的序列号放在下面请求的Wechatpay-Serial

	/**
	 * @param type 请求方式
	 * @param httpurl 请求的路径
	 * @param authorization 封装好的签名
	 * @param requestType  请求的类型(application/json,multipart/form-data")
	 * @return
	 * @throws IOException
	 */
	public static HttpURLConnection getHttpConnection(String type,String httpurl, String authorization,String requestType) throws IOException {
		URL url = new URL(httpurl);
		//打开连接
		HttpURLConnection conn = (HttpURLConnection)url.openConnection();
		//设置为(POSt,GET)
		conn.setRequestMethod(type);
		//设置输出流
		conn.setDoOutput(true);
		//设置输入流
		conn.setDoInput(true);
		//是否缓存
		conn.setUseCaches(false);
		//请求请求参数
		conn.setRequestProperty("Charsert", "UTF-8");
		conn.setRequestProperty("Accept", "application/json");
		conn.setRequestProperty("Content-Type", requestType);
		conn.setRequestProperty("Authorization", authorization);
		//微信平台证书的序列号
        conn.setRequestProperty("Wechatpay-Serial", "");
		return conn;
	}
	

以上步骤都做完了接下来我们要做的是把微信实名认证所需要的图片给上传

我们先把文件路径放进来 然后在通过对图片文件的二进制内容进行sha256计算得到图片文件的文件摘要

      
        JSONObject jsonObject = new JSONObject();

        File file = new File(filePath);
        String filename = file.getName();//文件名
        String fileSha256 = DigestUtils.sha256Hex(new FileInputStream(file));//文件sha256值
        jsonObject.put("filename",filename);
        jsonObject.put("sha256",fileSha256);
        //获取签名
        String sig = getPostSig(imgUp,jsonObject);
        //计算签名
        String sign = signRSA(sig, rsaPrivateKeyFile);

然后获取http头的Authorization内容

      
	String authorization = getAuthorization(mchid, nonce_str,timestamp,serial_no);

获取https连接把请求的路径还有方式放进去

	 // 获取连接
      HttpURLConnection conn = getHttpConnection("POST","https://api.mch.weixin.qq.com/v3/merchant/media/upload",authorization,"multipart/form-data");   

通过DataOutputStream读取Http的流

          DataOutputStream dos = new DataOutputStream(conn.getOutputStream());

然后拼接body的内容
注意:此处的拼接是有顺序的千万不能弄乱了

  //拼装请求内容第一部分
        StringBuilder strSb =new StringBuilder();
        strSb.append(PREFIX).append(BOUNDARY).append(LINE_END)
                .append("Content-Disposition: form-data; name=\"meta\";" + LINE_END)
                .append("Content-Type: application/json; " + LINE_END)
                .append(LINE_END)// 空行
                .append(jsonObject)
                .append(LINE_END);
            System.out.println(strSb);
        
         	dos.write(strSb.toString().getBytes());
      	    dos.flush();

接下来是第二部分的body

 		StringBuilder fileSbStart =new StringBuilder();
        fileSbStart.append(PREFIX).append(BOUNDARY).append(LINE_END)
                .append("Content-Disposition: form-data; name=\"file\"; filename=\""+ filename+"\";" + LINE_END)
                .append("Content-Type: image/jpeg" + LINE_END)
                .append(LINE_END);// 空行
        System.out.println(fileSbStart);
        dos.write(fileSbStart.toString().getBytes());
      	dos.flush();

然后就是放图片的二进制内容了

	 	InputStream is = new FileInputStream(file);
	        byte[] buffer = new byte[1024];
	        int len = 0;
	        while ((len = is.read(buffer)) != -1) {
	            dos.write(buffer, 0, len);
        	}
           dos.flush();

剩下的就是最后一部分的拼接了

        StringBuilder fileSbEnd = new StringBuilder();
        fileSbEnd.append(LINE_END)
                .append(PREFIX).append(BOUNDARY).append(PREFIX)
                .append(LINE_END);
        dos.write(fileSbEnd.toString().getBytes());
        
        dos.flush();
        dos.close();

打印出返回的头部信息

	//接收返回
        //打印返回头信息
        System.out.println("接口返回头信息:");
        Map<String, List<String>> responseHeader = conn.getHeaderFields();
        for (Map.Entry<String, List<String>> entry : responseHeader.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

通过返回的状态判断是否成功上传


        //打印返回内容
        int responseCode = conn.getResponseCode();
        String rescontent = "";
        String resSrt = null;
        if (responseCode == HttpURLConnection.HTTP_OK) {
            rescontent = getResult(conn.getInputStream());
            System.out.println("图片上传成功:" + rescontent);
            JSONObject json = JSON.parseObject(rescontent);
            resSrt = (String) json.get("media_id");
        } else {
            rescontent = getResult(conn.getInputStream());
            System.out.println("图片上传失败:" + rescontent);
        }

这些操作弄完后我们就开始验签了


 		//验证微信支付返回签名
        String Wtimestamp = responseHeader.get("Wechatpay-Timestamp").get(0);
        String Wnonce = responseHeader.get("Wechatpay-Nonce").get(0);
        String Wsign = responseHeader.get("Wechatpay-Signature").get(0);
        //拼装待签名串
        StringBuffer ss = new StringBuffer();
        ss.append(Wtimestamp).append("\n");
        ss.append(Wnonce).append("\n");
        ss.append(rescontent).append("\n");
        //验证签名
        if (!verify(getCertificate("微信平台证书的路径"), ss.toString().getBytes(), Wsign)) {
            throw new Exception("签名验证失败");
        }

    /**
     * 获取证书
     * @param filename 证书的路径
     * @return
     */
    public static X509Certificate getCertificate(String filename) throws IOException {
        InputStream fis = new FileInputStream(filename);
        BufferedInputStream bis = new BufferedInputStream(fis);

        try {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        } finally {
            bis.close();
        }
    }
    
     //验签名
    private static boolean verify(X509Certificate certificate, byte[] message, String signature) {
        try {
            Signature sig = Signature.getInstance("SHA256withRSA");
            sig.initVerify(certificate);
            sig.update(message);
            return sig.verify(Base64.getDecoder().decode(signature));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("无效的证书", e);
        } catch (SignatureException e) {
            throw new RuntimeException("签名验证过程发生了错误", e);
        }
    }

这样我们一个完整的图片上传就完成了!

接下来就是我们的重点微信实名认证了

准备的内容也是跟我们之前的一样先把实名的信息构造在签名串里,然后加密签名,在获取头部的authorization值
再通过获取http的连接请求路径 先看看代码把



    /**
     * 微信的实名认证
     * @return
     */
    public static  String autonymAttestation(JSONObject jsonObject) throws Exception {
        //获取签名
        String sig = getPostSig("实名认证的路径",jsonObject);

        //计算签名
        String sign = signRSA(sig, rsaPrivateKeyFile);

        System.out.println("签名sign值:" + sign);
        System.out.println("签名的长度:"+sig.length());

        //拼装http头的Authorization内容
        String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + mchid + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + sign + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + serial_no + "\"";
        System.out.println("authorization值:" + authorization);


        HttpURLConnection conn = getHttpConnection("POST","实名认证的路径",authorization,"application/json");

        DataOutputStream dos = new DataOutputStream(conn.getOutputStream());

        dos.write(jsonObject.toString().getBytes());

        dos.flush();

        dos.close();

        System.out.println("接口返回头信息:");
        Map<String, List<String>> responseHeader = conn.getHeaderFields();
        for (Map.Entry<String, List<String>> entry : responseHeader.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

        //打印返回内容
        int responseCode = conn.getResponseCode();
        String rescontent ="";
        if(responseCode == HttpURLConnection.HTTP_OK){
            //成功
            rescontent =getResult(conn.getInputStream());
            System.out.println(rescontent);
        }else{
            //失败
            rescontent =getResult(conn.getErrorStream());
            System.out.println(rescontent);
        }
        return  rescontent;
    }


该使用的代码方法都弄好了 剩下的就是你们自己所需的参数放进去

如果请求成功就会返回一个字段也就是注册成功的商户号

在此之前我们还需要通过接口上传实名认证所需要的一些图片,

	{ "applyment_id": 20000000011111}

如何返回签名失败的话 可以根据官方给出的以下原因来一个个的排除

在这里插入图片描述
在这里插入图片描述

如果请求返回的也是401的话 官方也给出的原因可以看看原因排除
在这里插入图片描述
以上就是小编会的内容了 亲测过没问题 如有问题欢迎评论,小编是第一次写博客写不喜勿喷 谢谢

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值