package com.fuiou.demo.api; import cn.hutool.core.codec.Base64; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.http.HttpUtil; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Date; import java.util.HashMap; import java.util.Map; /* * 本代码仅供测试环境使用,不保证其完整性、正确性或适用于任何特定目的。 * 对于因使用本代码而造成的任何直接或间接损失,我们不承担任何责任。 * 建议您在生产环境中使用经过充分测试和验证的代码。 */ public class Ap01DemoTest { public static final String KEY_ALGORITHM = "RSA"; public static final int KEY_SIZE = 2048; // 建议使用 2048 位或更长的密钥长度 public static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; public static final String yourPrivateKey ="MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCleZ/WWlI+MwaENt1OK1ykGMdP6hdca/14BfMnXxTo/8RelqDSkPuccYsn//v87YBejNYmL6CvsamLipXZNgpxvLjy3doJguFaN9L8HOe1E0YXNMgrSFiXMRyeE9lmSWt4Fe8c9TfhaGL+NqA8SKT/C3k/9J/L/PNQzEqzrJBpm3cVC+/EnNzMnVzVsfOmJWHgVB5YaBL8wnQjVTcuQiRXLP/AnFnIunx1SOvRyYBxWOFgu8GFspWgZqjAcCLKT9TS0uD10q7o11ekTkYRk7PfEPgKJ+Xa175dIpHcOZTdQO5ZMGT/6rXD+WSeYisPGGOW2cTa109Te0y44lCK7fJFAgMBAAECggEBAIxFfKQVbrBBSt3bMGCqS17jjlmFBAaZmIUc7hFK/YvB/LF+GJhGxLPKYH8o9XBj2DTOSF6YcytcfG/Iq9w0fkgKBfIC9GippOR4fAaxbg3GZ90WJjTioA6SWEL8aobV6B8k4Mx4ZsVSWtBKeCyCHDQDguYfNTKTm6K7evuyZbzO5HhKq0kP5+HQF7YphArBykRsS2pFjG+LgGeFpXVsdUljnLqa/go2gqLEkqF/BcL2fROA7VkS3QcG0WkgslugqDp9tS668+vdFFA4IXtuvPd6Skp6IQ2A9dv4EWS21wV8JOggY8UJ4YtDywE66fUNjrQBwKJm3bEL5r8AYppsfKUCgYEA+bPT4WdqR3OkLCZ+kZxW62bm+r/GLB22/ibaBJNSubtpQkhrEEEpMqJDyVpkqCjrNPDPhggGZOUJ/xURmcfEkiePKg0IsuRgqZCeE9QPfOnt/U9zg2U+fm+ESKPY6nl5ABqBjPR1pOBe5h3IOfWOA9ub1FE/+LRueJi5Ok7JDHsCgYEAqaX+UIvJ5ebwLdSw060TL+RTZ2MejjGPs+xCvEGmbSOT+PoP99P1ChktNX9OusqffIN4WGbbcgYXY1PtLypVmuFRMvqcv7VK8beP6eVfbBUuB9OSg1W5hwRQMkcbwz+pj9MiagjvBkH0z4QsRie9enI+aUk25pdDrwpfgB/FoD8CgYEAo8X2ahhR9Js8SljVGtvXhn3vcPbnG3hB1V/WDroxv+/Tkc29quOSCcuzehT3f/OWkRqAggAxcWtnqw4+hQYpP1MC6ymxUuPHIm/fvlGP9vXXShUaRkvZOUQbFymf0+noGtFHtxN/NayTkYpnENylUJJxGkhQFOcCrcY9dqjF5JECgYEAgsHnd4uXDTVnr9t8g1qmLEavJkPWnECFA2e5tEJhlUNT3RZYUmszNhpbpx09wGlGbgEjM/frckJRqoRYjv7xRlQecs2JHZYNcqtKKDxbxQG6Hdwr1ECxo+hmK6p1MpOSDMHuh43lNYyGtZ+pRFWDDKqbgiklQKwcRgEXxLg4aZ8CgYEA40iB/BgTHiesRcjj+nj1gog+/FwzL254lf+j1tfa7Ms2tf4L4hSo2rcDymSXnsxZFdZn3L8lKJg8jpxoNYihs9Jlr70FJDrSgOJ9WRq84ARwU/pnBR4H+9Y/u3Z5tKV8Mx5Xj4Rv0bdygltgaD8qlKbkJ7FfhmgmIAMYFNP7qAk="; public static final String fyPublicKey ="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzj8g+wK2VhIMAMlSKSXh651qAMJnT1E9F6s/se+N9081whOotcFog3sdVF5SYTpfaKIeZ7gC53bWZFOtcz7WoXTaAG6xkCswp+CeKnX2wcj2yM2wjaQ8+tisy5Kj828jzTUHlvSVyDWCU+0XzpM0CvNRe5xw/1Wu4ET4frHxP0h041cfTlegfQPB0RjrP+qn2yTDaCSzuZ67qisJOswJ/OplSNeCPPNVXF9JTQaObB37Cc9NuONa3b5JXKBHy3lOgxNGvfrvFkKE+oiR+LaCq3a0vUMwLeIP10hgBm78GTGqF0QNoEL1liOT5KJG4GrwkdlMCRRq5FlTG5ocnSDSQwIDAQAB"; static String mchntSsn= DateUtil.currentSeconds() + RandomUtil.randomNumbers(3); /** * 生成rsa秘钥对 * @return * @throws NoSuchAlgorithmException */ public static KeyPair generateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGenerator.initialize(KEY_SIZE); return keyPairGenerator.generateKeyPair(); } /** * rsa 签名 * @param data * @param privateKey * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { byte[] keyBytes = Base64.decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(privateK); signature.update(data); return Base64.encode(signature.sign()); } /** * rsa 验签 * @param publicKeyStr * @param data * @param signatureStr * @return */ public static boolean verifySignature(String publicKeyStr, String data, String signatureStr) { try { byte[] publicKeyBytes = Base64.decode(publicKeyStr); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PublicKey publicKey = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(publicKey); signature.update(data.getBytes("UTF-8")); byte[] signatureBytes = Base64.decode(signatureStr); return signature.verify(signatureBytes); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 获取xml对应节点的内容 * @param xmlStr * @param nodeName * @return */ public static String extractNodeValue(String xmlStr, String nodeName) { if(!xmlStr.contains("</"+nodeName+">")){ return null; } return xmlStr.substring(xmlStr.indexOf("<"+nodeName+">")+nodeName.length()+2, xmlStr.indexOf("</"+nodeName+">")); } /** * 获取xml对应节点的结构字符串 * @param xmlStr * @param nodeName * @return */ public static String extractNodeFull(String xmlStr, String nodeName) { if(!xmlStr.contains("</"+nodeName+">")){ return null; } return xmlStr.substring(xmlStr.indexOf("<"+nodeName+">"), xmlStr.lastIndexOf("</"+nodeName+">")+nodeName.length()+3); } /** * 4.1 付款(单笔) * @throws Exception */ public static void payforreq() throws Exception { String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?><payforreq><ver>4.0</ver><merdt>"+ DateUtil.format(new Date(),"yyyyMMdd") +"</merdt>" + "<orderno>"+mchntSsn+"</orderno><accntno>6228480868777777777</accntno><accntnm>蒂花秀</accntnm>" + "<amt>100</amt><mobile>18888888888</mobile><addDesc>1</addDesc><sceneType>00</sceneType><memo>高温补贴</memo></payforreq>"; String macSource ="0002900F0345178|"+"payforreq"+"|"+xml; final byte[] bytes = macSource.getBytes("UTF-8"); Map<String,Object> postParams=new HashMap<>(); postParams.put("merid", "0002900F0345178"); postParams.put("reqtype", "payforreq"); postParams.put("xml", xml); postParams.put("mac", sign(bytes, yourPrivateKey)); System.out.println(xml); String result = HttpUtil.post("https://fht-test-api.fuioupay.com/fuMer_api/req.do", postParams); System.out.println(result); String ret = extractNodeValue(result,"ret"); String memo =extractNodeValue(result,"memo"); String mac = extractNodeValue(result,"mac"); macSource="0002900F0345178|"+mchntSsn+"|"+ret+"|"+memo; System.out.println("验签: "+verifySignature(fyPublicKey,macSource,mac)); } /** * 5.2 账户余额查 * @throws Exception */ public static void qryacnt() throws Exception { String macSource ="4.0|0002900F0345178"; final byte[] bytes = macSource.getBytes("UTF-8"); String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><qryacnt>\n" + " <ver>4.0</ver>\n" + " <merid>0002900F0345178</merid>\n" + " <mac>"+sign(bytes, yourPrivateKey)+"</mac>\n" + "</qryacnt>"; Map<String,Object> postParams=new HashMap<>(); postParams.put("merid", "0002900F0345178"); postParams.put("xml", xml); String result = HttpUtil.post("https://fht-test-api.fuioupay.com/fuMer_api/t0zj_qryAcnt.do", postParams); System.out.println(result); String ret = extractNodeValue(result,"ret"); String memo = extractNodeValue(result,"memo"); String ctamt = extractNodeValue(result,"ctamt"); String caamt = extractNodeValue(result,"caamt"); String cuamt = extractNodeValue(result,"cuamt"); String cfamt = extractNodeValue(result,"cfamt"); String mac = extractNodeValue(result,"mac"); macSource="0002900F0345178|"+ret+"|"+memo+"|"+ctamt+"|"+caamt+"|"+cuamt+"|"+cfamt; System.out.println("验签: "+verifySignature(fyPublicKey,macSource,mac)); } /** * 5.1 交易查询(条件可组合) * @throws Exception */ public static void qry() throws Exception { String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><qrytransreq>\n" + " <ver>4.0</ver>\n" + " <busicd>AP01</busicd>\n" + " <orderno></orderno>\n" + " <startdt>20240924</startdt>\n" + " <enddt>20240924</enddt>\n" + "</qrytransreq>"; String macSource ="0002900F0345178|"+xml; final byte[] bytes = macSource.getBytes("UTF-8"); Map<String,Object> postParams=new HashMap<>(); postParams.put("merid", "0002900F0345178"); postParams.put("xml", xml); postParams.put("mac", sign(bytes, yourPrivateKey)); String result = HttpUtil.post("https://fht-test-api.fuioupay.com/fuMer_api/qry.do", postParams); System.out.println(result); String mac = extractNodeValue(result,"mac"); String ret = extractNodeValue(result,"ret"); String memo = extractNodeValue(result,"memo"); if("000000".equals(ret)){ macSource="0002900F0345178|"+ret+"|"+memo+"|"+ extractNodeFull(result,"trans"); }else{ macSource="0002900F0345178|"+ret+"|"+memo; } System.out.println("验签: "+verifySignature(fyPublicKey,macSource,mac)); } /** * 生成 Base64 编码的字符串形式的rsa秘钥 * @throws Exception */ public static void genRsaKey() throws Exception { KeyPair keyPair = generateKeyPair(); // 获取公钥和私钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 将密钥转换为 Base64 编码的字符串,方便存储和传输 String publicKeyString = Base64.encode(publicKey.getEncoded()); String privateKeyString = Base64.encode(privateKey.getEncoded()); System.out.println("公钥 (Base64 编码):\n" + publicKeyString); System.out.println("私钥 (Base64 编码):\n" + privateKeyString); String message = "Hello, RSA!"; String sign = sign(message.getBytes("UTF-8"), privateKeyString); System.out.println("检验密钥对是否匹配:"+verifySignature(publicKeyString,message,sign)); } public static void main(String[] args) throws Exception { qryacnt(); } }