master
parent
7e80e1be55
commit
b990c30ca0
@ -0,0 +1,59 @@
|
||||
package com.bsd.say.util.wechat;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class AesException extends Exception {
|
||||
|
||||
public final static int OK = 0;
|
||||
public final static int ValidateSignatureError = -40001;
|
||||
public final static int ParseXmlError = -40002;
|
||||
public final static int ComputeSignatureError = -40003;
|
||||
public final static int IllegalAesKey = -40004;
|
||||
public final static int ValidateAppidError = -40005;
|
||||
public final static int EncryptAESError = -40006;
|
||||
public final static int DecryptAESError = -40007;
|
||||
public final static int IllegalBuffer = -40008;
|
||||
//public final static int EncodeBase64Error = -40009;
|
||||
//public final static int DecodeBase64Error = -40010;
|
||||
//public final static int GenReturnXmlError = -40011;
|
||||
|
||||
private int code;
|
||||
|
||||
private static String getMessage(int code) {
|
||||
switch (code) {
|
||||
case ValidateSignatureError:
|
||||
return "签名验证错误";
|
||||
case ParseXmlError:
|
||||
return "xml解析失败";
|
||||
case ComputeSignatureError:
|
||||
return "sha加密生成签名失败";
|
||||
case IllegalAesKey:
|
||||
return "SymmetricKey非法";
|
||||
case ValidateAppidError:
|
||||
return "appid校验失败";
|
||||
case EncryptAESError:
|
||||
return "aes加密失败";
|
||||
case DecryptAESError:
|
||||
return "aes解密失败";
|
||||
case IllegalBuffer:
|
||||
return "解密后得到的buffer非法";
|
||||
// case EncodeBase64Error:
|
||||
// return "base64加密错误";
|
||||
// case DecodeBase64Error:
|
||||
// return "base64解密错误";
|
||||
// case GenReturnXmlError:
|
||||
// return "xml生成失败";
|
||||
default:
|
||||
return null; // cannot be
|
||||
}
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
AesException(int code) {
|
||||
super(getMessage(code));
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.bsd.say.util.wechat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class ByteGroup {
|
||||
ArrayList<Byte> byteContainer = new ArrayList<Byte>();
|
||||
|
||||
public byte[] toBytes() {
|
||||
byte[] bytes = new byte[byteContainer.size()];
|
||||
for (int i = 0; i < byteContainer.size(); i++) {
|
||||
bytes[i] = byteContainer.get(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public ByteGroup addBytes(byte[] bytes) {
|
||||
for (byte b : bytes) {
|
||||
byteContainer.add(b);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return byteContainer.size();
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 对公众平台发送给公众账号的消息加解密示例代码.
|
||||
*
|
||||
* @copyright Copyright (c) 1998-2014 Tencent Inc.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
package com.bsd.say.util.wechat;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* SHA1 class
|
||||
*
|
||||
* 计算公众平台的消息签名接口.
|
||||
*/
|
||||
class SHA1 {
|
||||
|
||||
/**
|
||||
* 用SHA1算法生成安全签名
|
||||
* @param token 票据
|
||||
* @param timestamp 时间戳
|
||||
* @param nonce 随机字符串
|
||||
* @param encrypt 密文
|
||||
* @return 安全签名
|
||||
* @throws AesException
|
||||
*/
|
||||
public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException
|
||||
{
|
||||
try {
|
||||
String[] array = new String[] { token, timestamp, nonce, encrypt };
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// 字符串排序
|
||||
Arrays.sort(array);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sb.append(array[i]);
|
||||
}
|
||||
String str = sb.toString();
|
||||
// SHA1签名生成
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
md.update(str.getBytes());
|
||||
byte[] digest = md.digest();
|
||||
|
||||
StringBuffer hexstr = new StringBuffer();
|
||||
String shaHex = "";
|
||||
for (int i = 0; i < digest.length; i++) {
|
||||
shaHex = Integer.toHexString(digest[i] & 0xFF);
|
||||
if (shaHex.length() < 2) {
|
||||
hexstr.append(0);
|
||||
}
|
||||
hexstr.append(shaHex);
|
||||
}
|
||||
return hexstr.toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new AesException(AesException.ComputeSignatureError);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package com.bsd.say.util.wechat;
|
||||
|
||||
import static org.aspectj.bridge.MessageUtil.fail;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class WXBizMsgCryptTest {
|
||||
String encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
|
||||
String token = "pamtest";
|
||||
String timestamp = "1409304348";
|
||||
String nonce = "xxxxxx";
|
||||
String appId = "wxb11529c136998cb6";
|
||||
String replyMsg = "我是中文abcd123";
|
||||
String xmlFormat = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>";
|
||||
String afterAesEncrypt = "jn1L23DB+6ELqJ+6bruv21Y6MD7KeIfP82D6gU39rmkgczbWwt5+3bnyg5K55bgVtVzd832WzZGMhkP72vVOfg==";
|
||||
String randomStr = "aaaabbbbccccdddd";
|
||||
|
||||
String replyMsg2 = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>";
|
||||
String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb";
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpBeforeClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownAfterClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormal() throws ParserConfigurationException, SAXException, IOException {
|
||||
try {
|
||||
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
|
||||
String afterEncrpt = pc.encryptMsg(replyMsg, timestamp, nonce);
|
||||
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
StringReader sr = new StringReader(afterEncrpt);
|
||||
InputSource is = new InputSource(sr);
|
||||
Document document = db.parse(is);
|
||||
|
||||
Element root = document.getDocumentElement();
|
||||
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
|
||||
NodeList nodelist2 = root.getElementsByTagName("MsgSignature");
|
||||
|
||||
String encrypt = nodelist1.item(0).getTextContent();
|
||||
String msgSignature = nodelist2.item(0).getTextContent();
|
||||
String fromXML = String.format(xmlFormat, encrypt);
|
||||
|
||||
// 第三方收到公众号平台发送的消息
|
||||
String afterDecrpt = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML);
|
||||
assertEquals(replyMsg, afterDecrpt);
|
||||
} catch (AesException e) {
|
||||
fail("正常流程,怎么就抛出异常了??????");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAesEncrypt() {
|
||||
try {
|
||||
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
|
||||
assertEquals(afterAesEncrypt, pc.encrypt(randomStr, replyMsg));
|
||||
} catch (AesException e) {
|
||||
e.printStackTrace();
|
||||
fail("no异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAesEncrypt2() {
|
||||
try {
|
||||
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
|
||||
assertEquals(afterAesEncrypt2, pc.encrypt(randomStr, replyMsg2));
|
||||
|
||||
} catch (AesException e) {
|
||||
e.printStackTrace();
|
||||
fail("no异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIllegalAesKey() {
|
||||
try {
|
||||
new WXBizMsgCrypt(token, "abcde", appId);
|
||||
} catch (AesException e) {
|
||||
assertEquals(AesException.IllegalAesKey, e.getCode());
|
||||
return;
|
||||
}
|
||||
fail("错误流程不抛出异常???");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateSignatureError() throws ParserConfigurationException, SAXException,
|
||||
IOException {
|
||||
try {
|
||||
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
|
||||
String afterEncrpt = pc.encryptMsg(replyMsg, timestamp, nonce);
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
StringReader sr = new StringReader(afterEncrpt);
|
||||
InputSource is = new InputSource(sr);
|
||||
Document document = db.parse(is);
|
||||
|
||||
Element root = document.getDocumentElement();
|
||||
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
|
||||
|
||||
String encrypt = nodelist1.item(0).getTextContent();
|
||||
String fromXML = String.format(xmlFormat, encrypt);
|
||||
pc.decryptMsg("12345", timestamp, nonce, fromXML); // 这里签名错误
|
||||
} catch (AesException e) {
|
||||
assertEquals(AesException.ValidateSignatureError, e.getCode());
|
||||
return;
|
||||
}
|
||||
fail("错误流程不抛出异常???");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyUrl() throws AesException {
|
||||
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt("QDG6eK",
|
||||
"jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C", "wx5823bf96d3bd56c7");
|
||||
String verifyMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
|
||||
String timeStamp = "1409659589";
|
||||
String nonce = "263014780";
|
||||
String echoStr = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";
|
||||
wxcpt.verifyUrl(verifyMsgSig, timeStamp, nonce, echoStr);
|
||||
// 只要不抛出异常就好
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 对公众平台发送给公众账号的消息加解密示例代码.
|
||||
*
|
||||
* @copyright Copyright (c) 1998-2014 Tencent Inc.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
package com.bsd.say.util.wechat;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
/**
|
||||
* XMLParse class
|
||||
*
|
||||
* 提供提取消息格式中的密文及生成回复消息格式的接口.
|
||||
*/
|
||||
class XMLParse {
|
||||
|
||||
/**
|
||||
* 提取出xml数据包中的加密消息
|
||||
* @param xmltext 待提取的xml字符串
|
||||
* @return 提取出的加密消息字符串
|
||||
* @throws AesException
|
||||
*/
|
||||
public static Object[] extract(String xmltext) throws AesException {
|
||||
Object[] result = new Object[3];
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
StringReader sr = new StringReader(xmltext);
|
||||
InputSource is = new InputSource(sr);
|
||||
Document document = db.parse(is);
|
||||
|
||||
Element root = document.getDocumentElement();
|
||||
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
|
||||
NodeList nodelist2 = root.getElementsByTagName("ToUserName");
|
||||
result[0] = 0;
|
||||
result[1] = nodelist1.item(0).getTextContent();
|
||||
result[2] = nodelist2.item(0).getTextContent();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new AesException(AesException.ParseXmlError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成xml消息
|
||||
* @param encrypt 加密后的消息密文
|
||||
* @param signature 安全签名
|
||||
* @param timestamp 时间戳
|
||||
* @param nonce 随机字符串
|
||||
* @return 生成的xml字符串
|
||||
*/
|
||||
public static String generate(String encrypt, String signature, String timestamp, String nonce) {
|
||||
|
||||
String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n"
|
||||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n"
|
||||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>";
|
||||
return String.format(format, encrypt, signature, timestamp, nonce);
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue