建行专线银企直连签到及查询余额开发过程记录
接上篇《建行专线银企直连秘钥交互开发过程记录》,本篇文章主要是记录:
1、如何对建设银行的des对称秘钥和rsa公钥进行解密;
2、如何对签到的请求报文进行加密和对签到的响应报文进行解密;
3、如何对查询余额的请求报文进行加密和对查询余额的响应报文进行解密;
1 package x; 2 3 import java.awt.*; 4 import java.io.*; 5 import java.math.BigInteger; 6 import java.security.*; 7 import java.security.interfaces.RSAPrivateKey; 8 import java.security.interfaces.RSAPublicKey; 9 import java.security.spec.PKCS8EncodedKeySpec; 10 import java.security.spec.X509EncodedKeySpec; 11 import java.text.SimpleDateFormat; 12 import java.util.Date; 13 14 import javax.crypto.Cipher; 15 import javax.crypto.SecretKey; 16 import javax.crypto.SecretKeyFactory; 17 import javax.crypto.spec.DESKeySpec; 18 import javax.crypto.spec.DESedeKeySpec; 19 20 import com.sun.org.apache.xpath.internal.objects.XString; 21 import org.apache.commons.codec.binary.Base64; 22 import sun.misc.BASE64Decoder; 23 24 public class PostDemo { 25 26 private static final String SIGNATURE_ALGORITHM = "MD5withRSA"; 27 28 public static void main(String[] args) throws Exception { 29 //解密建行des对称秘钥和rsa公钥 30 dectyptCcbKey(); 31 32 //生成签到请求 33 requestSignIn(); 34 //解析签到响应 35 responseSignIn(); 36 37 //生成查询余额请求 38 requestBalance(); 39 //解析查询余额响应 40 responseBalance(); 41 } 42 43 /** 44 * 解密建行des对称秘钥和rsa公钥 45 * @throws Exception 46 */ 47 public static void dectyptCcbKey() throws Exception { 48 // 电子银行合约编号(取后10位,不足10位的,前面补0)+交换当日日期(YYMMDD 6位) 49 String desKey = getDESKey(); 50 // 建行提供方法处理后8位byte[] 51 byte[] keyByte = asc2bin(desKey); 52 53 //ccb_des和ccb_pub是通过/interlink/KeyTransfer获取的文件 54 // 获取建行des对称秘钥,并转换成byte[] 55 byte[] ccb_des=getContent("D:/CCBKEY/ccb_des.txt"); 56 // 获取建行rsa公钥,并转换成byte[] 57 byte[] ccb_pub=getContent("D:/CCBKEY/ccb_pub.txt"); 58 59 //减去前面000000 60 byte[] ccb_des_real = new byte[ccb_des.length - 6]; 61 System.arraycopy(ccb_des,6, ccb_des_real,0, ccb_des.length - 6); 62 //des对称秘钥解密后的base64 63 byte[] ccbDesDecrypt = decryptKey(ccb_des_real,keyByte); 64 //保存des对称秘钥解密后的二进制成文件 65 saveFile(ccbDesDecrypt, "D:/CCBKEY/ccb_des_byte.txt"); 66 //保存des对称秘钥解密后的base64成文件 67 saveFile(Base64.encodeBase64String(ccbDesDecrypt), "D:/CCBKEY/ccb_des_base64.txt"); 68 69 //减去前面000000 70 byte[] ccb_pub_real = new byte[ccb_pub.length - 6]; 71 System.arraycopy(ccb_pub,6, ccb_pub_real,0, ccb_pub.length - 6); 72 //建行rsa公钥解密后的base64 73 byte[] ccbPubDecrypt = decryptKey(ccb_pub_real,keyByte); 74 //保存建行rsa公钥解密后的二进制成文件 75 saveFile(ccbPubDecrypt, "D:/CCBKEY/ccb_rsa_pub_byte.txt"); 76 //保存建行rsa公钥解密后的base64成文件 77 saveFile(Base64.encodeBase64String(ccbPubDecrypt), "D:/CCBKEY/ccb_rsa_pub_base64.txt"); 78 } 79 80 /** 81 * 生成签到请求 82 * @throws Exception 83 */ 84 public static void requestSignIn() throws Exception { 85 //des对称秘钥解密后的base64 86 byte[] ccbDesDecrypt = getCcbDes(); 87 String gouxinPriKey = getGouxinPriKey(); 88 89 //取的request的xml的base64字符串 90 String xml = readFile("D:/CCBKEY/签到_request.txt");//原报文xml明文 91 byte[] xml_encrypt_byte = encrypt(xml.getBytes(), ccbDesDecrypt); 92 //保存签到request的xml的二进制成文件 93 saveFile(xml_encrypt_byte,"D:/CCBKEY/签到request的xml的二进制.txt"); 94 String xml_encrypt_string = Base64.encodeBase64String(xml_encrypt_byte); 95 //保存签到request的xml的base64成文件 96 saveFile(xml_encrypt_string,"D:/CCBKEY/签到request的xml的base64.txt"); 97 98 //取的request的signature的base64字符串 99 byte[] xml_signature_byte = sign(xml.getBytes(), gouxinPriKey); 100 //保存签到request的signature的二进制成文件 101 saveFile(xml_signature_byte,"D:/CCBKEY/签到request的signature的二进制.txt"); 102 String xml_signature_string = Base64.encodeBase64String(xml_signature_byte); 103 //保存签到request的signature的base64成文件 104 saveFile(xml_signature_string,"D:/CCBKEY/签到request的signature的base64.txt"); 105 } 106 107 /** 108 * 解析签到响应 109 * @throws Exception 110 */ 111 public static void responseSignIn() throws Exception{ 112 byte[] response_byte = getContent("D:/CCBKEY/签到response.txt"); 113 byte[] len_byte = new byte[10]; 114 System.arraycopy(response_byte,0, len_byte,0, 10); 115 String sign_len_s = new String(len_byte); 116 //数字签名的长度 117 int sign_len= Integer.parseInt(sign_len_s); 118 119 //获取数字签名 120 byte[] response_sign_byte = new byte[sign_len]; 121 System.arraycopy(response_byte,10, response_sign_byte,0, sign_len); 122 //保存建行签到响应的数字签名的二进制成文件 123 saveFile(response_sign_byte, "D:/CCBKEY/签到response的数字签名的二进制.txt"); 124 //保存建行签到响应的数字签名的base64成文件 125 saveFile(Base64.encodeBase64String(response_sign_byte), "D:/CCBKEY/签到response的数字签名的base64.txt"); 126 127 //获取报文密文 128 int response_xml_byte_length = response_byte.length - 10 - sign_len; 129 byte[] response_xml_byte = new byte[response_xml_byte_length]; 130 System.arraycopy(response_byte,10 + sign_len, response_xml_byte,0, response_xml_byte_length); 131 //保存建行签到响应的报文密文的二进制成文件 132 saveFile(response_xml_byte, "D:/CCBKEY/签到response的报文密文的二进制.txt"); 133 //保存建行签到响应的报文密文的base64成文件 134 saveFile(Base64.encodeBase64String(response_xml_byte), "D:/CCBKEY/签到response的报文密文的base64.txt"); 135 136 //对响应报文解密 137 // 获取建行des对称秘钥,并转换成byte[] 138 byte[] ccb_des = getCcbDes(); 139 // 获取建行rsa公钥 140 String ccbPubKey = getCcbPubKey(); 141 142 //建行提供的DES对称密钥对响应报文进行解密,获得响应报文明文 143 byte[] response_decrypt = decrypt(response_xml_byte, ccb_des); 144 boolean isvalid = isValid(response_decrypt, response_sign_byte, ccbPubKey); 145 146 if(isvalid){ 147 String response_decrypt_string = new String(response_decrypt); 148 saveFile(response_decrypt_string, "D:/CCBKEY/签到response的报文明文.txt"); 149 } 150 else{ 151 saveFile("验证签名失败", "D:/CCBKEY/签到response的报文明文.txt"); 152 } 153 } 154 155 /** 156 * 解析查询余额响应 157 * @throws Exception 158 */ 159 public static void requestBalance() throws Exception { 160 //des对称秘钥解密后的base64 161 byte[] ccbDesDecrypt = getCcbDes(); 162 String gouxinPriKey = getGouxinPriKey(); 163 164 //取的request的xml的base64字符串 165 String xml = readFile("D:/CCBKEY/查询余额_request.txt");//原报文xml明文 166 byte[] xml_encrypt_byte = encrypt(xml.getBytes(), ccbDesDecrypt); 167 //保存签到request的xml的二进制成文件 168 saveFile(xml_encrypt_byte,"D:/CCBKEY/查询余额request的xml的二进制.txt"); 169 String xml_encrypt_string = Base64.encodeBase64String(xml_encrypt_byte); 170 //保存签到request的xml的base64成文件 171 saveFile(xml_encrypt_string,"D:/CCBKEY/查询余额request的xml的base64.txt"); 172 173 //取的request的signature的base64字符串 174 byte[] xml_signature_byte = sign(xml.getBytes(), gouxinPriKey); 175 //保存签到request的signature的二进制成文件 176 saveFile(xml_signature_byte,"D:/CCBKEY/查询余额request的signature的二进制.txt"); 177 String xml_signature_string = Base64.encodeBase64String(xml_signature_byte); 178 //保存签到request的signature的base64成文件 179 saveFile(xml_signature_string,"D:/CCBKEY/查询余额request的signature的base64.txt"); 180 } 181 182 /** 183 * 解析查询余额响应 184 * @throws Exception 185 */ 186 public static void responseBalance() throws Exception{ 187 byte[] response_byte = getContent("D:/CCBKEY/查询余额response.txt"); 188 byte[] len_byte = new byte[10]; 189 System.arraycopy(response_byte,0, len_byte,0, 10); 190 String sign_len_s = new String(len_byte); 191 //数字签名的长度 192 int sign_len= Integer.parseInt(sign_len_s); 193 194 //获取数字签名 195 byte[] response_sign_byte = new byte[sign_len]; 196 System.arraycopy(response_byte,10, response_sign_byte,0, sign_len); 197 //保存建行签到响应的数字签名的二进制成文件 198 saveFile(response_sign_byte, "D:/CCBKEY/查询余额response的数字签名的二进制.txt"); 199 //保存建行签到响应的数字签名的base64成文件 200 saveFile(Base64.encodeBase64String(response_sign_byte), "D:/CCBKEY/查询余额response的数字签名的base64.txt"); 201 202 //获取报文密文 203 int response_xml_byte_length = response_byte.length - 10 - sign_len; 204 byte[] response_xml_byte = new byte[response_xml_byte_length]; 205 System.arraycopy(response_byte,10 + sign_len, response_xml_byte,0, response_xml_byte_length); 206 //保存建行签到响应的报文密文的二进制成文件 207 saveFile(response_xml_byte, "D:/CCBKEY/查询余额response的报文密文的二进制.txt"); 208 //保存建行签到响应的报文密文的base64成文件 209 saveFile(Base64.encodeBase64String(response_xml_byte), "D:/CCBKEY/查询余额response的报文密文的base64.txt"); 210 211 //对响应报文解密 212 // 获取建行des对称秘钥,并转换成byte[] 213 byte[] ccb_des = getCcbDes(); 214 // 获取建行rsa公钥 215 String ccbPubKey = getCcbPubKey(); 216 217 //建行提供的DES对称密钥对响应报文进行解密,获得响应报文明文 218 byte[] response_decrypt = decrypt(response_xml_byte, ccb_des); 219 boolean isvalid = isValid(response_decrypt, response_sign_byte, ccbPubKey); 220 221 if(isvalid){ 222 String response_decrypt_string = new String(response_decrypt); 223 saveFile(response_decrypt_string, "D:/CCBKEY/查询余额response的报文明文.txt"); 224 } 225 else{ 226 saveFile("验证签名失败", "D:/CCBKEY/查询余额response的报文明文.txt"); 227 } 228 } 229 230 /** 231 * 从文件中获取我方rsa私钥 232 * @return 233 */ 234 public static String getGouxinPriKey(){ 235 return readFile("D:/CCBKEY/gouxin_rsa_pri_base64.txt"); 236 } 237 238 /** 239 * 从文件中获取我方rsa公钥 240 * @return 241 */ 242 public static String getGouxinPubKey(){ 243 return readFile("D:/CCBKEY/gouxin_rsa_pub_base64.txt"); 244 } 245 246 /** 247 * 从文件中获取建行rsa公钥 248 * @return 249 */ 250 public static String getCcbPubKey(){ 251 return readFile("D:/CCBKEY/ccb_rsa_pub_base64.txt"); 252 } 253 254 /** 255 * 从文件中获取建行des对称秘钥 256 * @return 257 */ 258 public static byte[] getCcbDes() throws IOException { 259 return getContent("D:/CCBKEY/ccb_des_byte.txt"); 260 } 261 262 /** 263 * 签名 264 * @param data 265 * @param privateKey 266 * @return 267 * @throws Exception 268 */ 269 public static byte[] sign(byte[] data, String privateKey) throws Exception { 270 // 取私钥匙对象 271 PrivateKey priKey = getPrivateKey(privateKey); 272 // 用私钥对信息生成数字签名 273 Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); 274 signature.initSign(priKey); 275 signature.update(data); 276 return signature.sign(); 277 } 278 279 /** 280 * RSA验签名检查 281 * @param in 282 * @param signdata 283 * @param publicKey 建行rsa公钥 284 * @return 285 */ 286 public static boolean isValid(byte[] in, byte[] signdata, String publicKey) throws Exception { 287 try 288 { 289 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 290 //获取建行公钥 291 PublicKey pubKey = getPublicKey(publicKey); 292 Signature signature = java.security.Signature.getInstance(SIGNATURE_ALGORITHM); 293 signature.initVerify(pubKey); 294 signature.update(in); 295 return signature.verify(signdata); 296 } 297 catch (Exception e) 298 { 299 e.printStackTrace(); 300 } 301 return false; 302 } 303 304 /** 305 * String转私钥PrivateKey 306 * @param key 307 * @return 308 * @throws Exception 309 */ 310 public static PrivateKey getPrivateKey(String key) throws Exception { 311 byte[] keyBytes; 312 keyBytes = (new BASE64Decoder()).decodeBuffer(key); 313 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 314 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 315 PrivateKey privateKey = keyFactory.generatePrivate(keySpec); 316 return privateKey; 317 } 318 319 /** 320 * String转公钥PublicKey 321 * @param key 322 * @return 323 * @throws Exception 324 */ 325 public static PublicKey getPublicKey(String key) throws Exception { 326 byte[] keyBytes; 327 keyBytes = (new BASE64Decoder()).decodeBuffer(key); 328 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); 329 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 330 PublicKey publicKey = keyFactory.generatePublic(keySpec); 331 return publicKey; 332 } 333 334 /** 335 * 336 * @return 337 */ 338 private static String getDESKey() { 339 String customerId = "SZ44200009022220201"; 340 // 电子银行合约编号取后10位,不足10位,前面补0 341 if (customerId.length() >= 10) { 342 customerId = customerId.substring(customerId.length() - 10, customerId.length()); 343 } else { 344 customerId = "0000000000".substring(customerId.length()) + customerId; 345 } 346 // 交换当日日期(YYMMDD) 347 SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd"); 348 String date = sdf.format(new Date()); 349 // DES密钥 350 String DESKey = customerId + date; 351 return DESKey; 352 } 353 354 /** 355 * 356 * @param hexString 357 * @return 358 */ 359 private static byte[] asc2bin(String hexString) { 360 byte[] hexbyte = hexString.getBytes(); 361 byte[] bitmap = new byte[hexbyte.length / 2]; 362 for (int i = 0; i < bitmap.length; i++) { 363 hexbyte[i * 2] -= hexbyte[i * 2] > '9' ? 7 : 0; 364 hexbyte[i * 2 + 1] -= hexbyte[i * 2 + 1] > '9' ? 7 : 0; 365 bitmap[i] = (byte) ((hexbyte[i * 2] << 4 & 0xf0) | (hexbyte[i * 2 + 1] & 0x0f)); 366 } 367 368 return bitmap; 369 } 370 371 /** 372 * 373 * @param in 374 * @param key_byte 375 * @return 376 * @throws Exception 377 */ 378 public static byte[] encryptKey(String in, byte[] key_byte) throws Exception { 379 byte[] enc_data = null; 380 SecretKeyFactory skf = SecretKeyFactory.getInstance("DES"); 381 SecretKey secretkey = skf.generateSecret(new DESKeySpec(key_byte)); 382 byte[] inByte = Base64.decodeBase64(in); 383 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 384 cipher.init(Cipher.ENCRYPT_MODE, secretkey, new SecureRandom()); 385 enc_data = cipher.doFinal(inByte); 386 return enc_data; 387 } 388 389 /** 390 * 391 * @param in 392 * @param key_byte 393 * @return 394 * @throws Exception 395 */ 396 public static byte[] decryptKey(byte[] in, byte[] key_byte) throws Exception { 397 byte[] dec_data = null; 398 SecretKeyFactory skf = SecretKeyFactory.getInstance("DES"); 399 SecretKey secretkey = skf.generateSecret(new DESKeySpec(key_byte)); 400 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 401 cipher.init(Cipher.DECRYPT_MODE, secretkey); 402 dec_data = cipher.doFinal(in); 403 return dec_data; 404 } 405 406 /** 407 * DESede加密 408 * @param in 409 * @param key_byte 410 * @return 411 * @throws Exception 412 */ 413 public static byte[] encrypt(byte[] in, byte[] key_byte) throws Exception{ 414 byte[] enc_data = null; 415 SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede"); 416 SecretKey secretKey = skf.generateSecret(new DESedeKeySpec(key_byte)); 417 Cipher cipher = Cipher.getInstance("DESede"); 418 cipher.init(Cipher.ENCRYPT_MODE, secretKey, new SecureRandom()); 419 enc_data = cipher.doFinal(in); 420 return enc_data; 421 } 422 423 /** 424 * DESede解密 425 * @param in 426 * @param key_byte 427 * @return 428 * @throws Exception 429 */ 430 public static byte[] decrypt(byte[] in, byte[] key_byte) throws Exception{ 431 byte[] enc_data = null; 432 SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede"); 433 SecretKey secretKey = skf.generateSecret(new DESedeKeySpec(key_byte)); 434 Cipher cipher = Cipher.getInstance("DESede"); 435 cipher.init(Cipher.DECRYPT_MODE, secretKey, new SecureRandom()); 436 enc_data = cipher.doFinal(in); 437 return enc_data; 438 } 439 440 /** 441 * 保存文件 442 * @param fileString 文件内容 443 * @param filepath 文件绝对路径 444 */ 445 public static void saveFile(String fileString, String filepath){ 446 File file = new File(filepath); 447 if (file.exists()) { 448 file.delete(); 449 } 450 FileWriter writer; 451 try { 452 writer = new FileWriter(filepath); 453 writer.write(fileString); 454 writer.flush(); 455 writer.close(); 456 } catch (IOException e) { 457 e.printStackTrace(); 458 } 459 } 460 461 /** 462 * 将字节流转换成文件 463 * @param data 二进制数据 464 * @param filepath 文件绝对路径 465 * @throws Exception 466 */ 467 public static void saveFile(byte[] data,String filepath)throws Exception { 468 if (data != null) { 469 File file = new File(filepath); 470 if (file.exists()) { 471 file.delete(); 472 } 473 FileOutputStream fos = new FileOutputStream(file); 474 fos.write(data, 0, data.length); 475 fos.flush(); 476 fos.close(); 477 } 478 } 479 480 /** 481 * 根据文件路径读取文件转出byte[] 482 * @param filePath 483 * @return 484 * @throws IOException 485 */ 486 public static byte[] getContent(String filePath) throws IOException { 487 File file = new File(filePath); 488 long fileSize = file.length(); 489 if (fileSize > Integer.MAX_VALUE) { 490 System.out.println("file too big..."); 491 return null; 492 } 493 FileInputStream fi = new FileInputStream(file); 494 byte[] buffer = new byte[(int) fileSize]; 495 int offset = 0; 496 int numRead = 0; 497 while (offset < buffer.length && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) { 498 offset += numRead; 499 } 500 // 确保所有数据均被读取 501 if (offset != buffer.length) { 502 throw new IOException("Could not completely read file " 503 + file.getName()); 504 } 505 fi.close(); 506 return buffer; 507 } 508 509 /** 510 * 读取文件内容 511 * @param fileName 512 * @return 513 */ 514 public static String readFile(String fileName) { 515 File file = new File(fileName); 516 BufferedReader reader = null; 517 StringBuffer sbf = new StringBuffer(); 518 try { 519 reader = new BufferedReader(new FileReader(file)); 520 String tempStr; 521 while ((tempStr = reader.readLine()) != null) { 522 sbf.append(tempStr); 523 } 524 reader.close(); 525 return sbf.toString(); 526 } catch (IOException e) { 527 e.printStackTrace(); 528 } finally { 529 if (reader != null) { 530 try { 531 reader.close(); 532 } catch (IOException e1) { 533 e1.printStackTrace(); 534 } 535 } 536 } 537 return sbf.toString(); 538 } 539 }
(注:所有请求建设银行的接口都是使用postman手动调用的)