微信语音java_微信公众号开发java版-消息回复(普通文字消息和语音消息)

这个代码示例展示了如何使用Apache Commons Codec库来实现微信公众号消息的加解密,包括AES/CBC/NoPadding模式的加密解密过程,以及对消息的签名验证。该代码用于确保与公众平台交互时消息的安全性。
摘要由CSDN通过智能技术生成

/*** 对公众平台发送给公众账号的消息加解密示例代码.

*

* @copyright Copyright (c) 1998-2014 Tencent Inc.*/

//------------------------------------------------------------------------

/*** 针对org.apache.commons.codec.binary.Base64,

* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)

* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi

*/

packagewebapp.ext.wechat;importorg.apache.commons.codec.binary.Base64;importjavax.crypto.Cipher;importjavax.crypto.spec.IvParameterSpec;importjavax.crypto.spec.SecretKeySpec;importjava.nio.charset.Charset;importjava.util.Arrays;importjava.util.Random;/*** 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串).

*

*

第三方回复加密消息给公众平台

*

第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。

*

* 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案

*

*

在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:

*http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html

*

下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt

*

如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件

*

如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件

*

*/

public classWXBizMsgCrypt {static Charset CHARSET = Charset.forName("utf-8");

Base64 base64= newBase64();byte[] aesKey;

String token;

String appId;/*** 构造函数

*@paramtoken 公众平台上,开发者设置的token

*@paramencodingAesKey 公众平台上,开发者设置的EncodingAESKey

*@paramappId 公众平台appid

*

*@throwsAesException 执行失败,请查看该异常的错误码和具体的错误信息*/

public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throwsAesException {if (encodingAesKey.length() != 43) {throw newAesException(AesException.IllegalAesKey);

}this.token =token;this.appId =appId;

aesKey= Base64.decodeBase64(encodingAesKey + "=");

}//生成4个字节的网络字节序

byte[] getNetworkBytesOrder(intsourceNumber) {byte[] orderBytes = new byte[4];

orderBytes[3] = (byte) (sourceNumber & 0xFF);

orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);

orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);

orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);returnorderBytes;

}//还原4个字节的网络字节序

int recoverNetworkBytesOrder(byte[] orderBytes) {int sourceNumber = 0;for (int i = 0; i < 4; i++) {

sourceNumber<<= 8;

sourceNumber|= orderBytes[i] & 0xff;

}returnsourceNumber;

}//随机生成16位字符串

String getRandomStr() {

String base= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

Random random= newRandom();

StringBuffer sb= newStringBuffer();for (int i = 0; i < 16; i++) {int number =random.nextInt(base.length());

sb.append(base.charAt(number));

}returnsb.toString();

}/*** 对明文进行加密.

*

*@paramtext 需要加密的明文

*@return加密后base64编码的字符串

*@throwsAesException aes加密失败*/String encrypt(String randomStr, String text)throwsAesException {

ByteGroup byteCollector= newByteGroup();byte[] randomStrBytes =randomStr.getBytes(CHARSET);byte[] textBytes =text.getBytes(CHARSET);byte[] networkBytesOrder =getNetworkBytesOrder(textBytes.length);byte[] appidBytes =appId.getBytes(CHARSET);//randomStr + networkBytesOrder + text + appid

byteCollector.addBytes(randomStrBytes);

byteCollector.addBytes(networkBytesOrder);

byteCollector.addBytes(textBytes);

byteCollector.addBytes(appidBytes);//... + pad: 使用自定义的填充方式对明文进行补位填充

byte[] padBytes =PKCS7Encoder.encode(byteCollector.size());

byteCollector.addBytes(padBytes);//获得最终的字节流, 未加密

byte[] unencrypted =byteCollector.toBytes();try{//设置加密模式为AES的CBC模式

Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");

SecretKeySpec keySpec= new SecretKeySpec(aesKey, "AES");

IvParameterSpec iv= new IvParameterSpec(aesKey, 0, 16);

cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);//加密

byte[] encrypted =cipher.doFinal(unencrypted);//使用BASE64对加密后的字符串进行编码

String base64Encrypted =base64.encodeToString(encrypted);returnbase64Encrypted;

}catch(Exception e) {

e.printStackTrace();throw newAesException(AesException.EncryptAESError);

}

}/*** 对密文进行解密.

*

*@paramtext 需要解密的密文

*@return解密得到的明文

*@throwsAesException aes解密失败*/String decrypt(String text)throwsAesException {byte[] original;try{//设置解密模式为AES的CBC模式

Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");

SecretKeySpec key_spec= new SecretKeySpec(aesKey, "AES");

IvParameterSpec iv= new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));

cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);//使用BASE64对密文进行解码

byte[] encrypted =Base64.decodeBase64(text);//解密

original =cipher.doFinal(encrypted);

}catch(Exception e) {

e.printStackTrace();throw newAesException(AesException.DecryptAESError);

}

String xmlContent, from_appid;try{//去除补位字符

byte[] bytes =PKCS7Encoder.decode(original);//分离16位随机字符串,网络字节序和AppId

byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);int xmlLength =recoverNetworkBytesOrder(networkOrder);

xmlContent= new String(Arrays.copyOfRange(bytes, 20, 20 +xmlLength), CHARSET);

from_appid= new String(Arrays.copyOfRange(bytes, 20 +xmlLength, bytes.length),

CHARSET);

}catch(Exception e) {

e.printStackTrace();throw newAesException(AesException.IllegalBuffer);

}//appid不相同的情况

if (!from_appid.equals(appId)) {throw newAesException(AesException.ValidateAppidError);

}returnxmlContent;

}/*** 将公众平台回复用户的消息加密打包.

*

*

对要发送的消息进行AES-CBC加密

*

生成安全签名

*

将消息密文和安全签名打包成xml格式

*

*

*@paramreplyMsg 公众平台待回复用户的消息,xml格式的字符串

*@paramtimeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp

*@paramnonce 随机串,可以自己生成,也可以用URL参数的nonce

*

*@return加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串

*@throwsAesException 执行失败,请查看该异常的错误码和具体的错误信息*/

public String encryptMsg(String replyMsg, String timeStamp, String nonce) throwsAesException {//加密

String encrypt =encrypt(getRandomStr(), replyMsg);//生成安全签名

if (timeStamp == "") {

timeStamp=Long.toString(System.currentTimeMillis());

}

String signature=SHA1.getSHA1(token, timeStamp, nonce, encrypt);//System.out.println("发送给平台的签名是: " + signature[1].toString());//生成发送的xml

String result =XMLParse.generate(encrypt, signature, timeStamp, nonce);returnresult;

}/*** 检验消息的真实性,并且获取解密后的明文.

*

*

利用收到的密文生成安全签名,进行签名验证

*

若验证通过,则提取xml中的加密消息

*

对消息进行解密

*

*

*@parammsgSignature 签名串,对应URL参数的msg_signature

*@paramtimeStamp 时间戳,对应URL参数的timestamp

*@paramnonce 随机串,对应URL参数的nonce

*@parampostData 密文,对应POST请求的数据

*

*@return解密后的原文

*@throwsAesException 执行失败,请查看该异常的错误码和具体的错误信息*/

publicString decryptMsg(String msgSignature, String timeStamp, String nonce, String postData)throwsAesException {//密钥,公众账号的app secret//提取密文

Object[] encrypt =XMLParse.extract(postData);//验证安全签名

String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());//和URL中的签名比较是否相等//System.out.println("第三方收到URL中的签名:" + msg_sign);//System.out.println("第三方校验签名:" + signature);

if (!signature.equals(msgSignature)) {throw newAesException(AesException.ValidateSignatureError);

}//解密

String result = decrypt(encrypt[1].toString());returnresult;

}/*** 验证URL

*@parammsgSignature 签名串,对应URL参数的msg_signature

*@paramtimeStamp 时间戳,对应URL参数的timestamp

*@paramnonce 随机串,对应URL参数的nonce

*@paramechoStr 随机串,对应URL参数的echostr

*

*@return解密之后的echostr

*@throwsAesException 执行失败,请查看该异常的错误码和具体的错误信息*/

publicString verifyUrl(String msgSignature, String timeStamp, String nonce, String echoStr)throwsAesException {

String signature=SHA1.getSHA1(token, timeStamp, nonce, echoStr);if (!signature.equals(msgSignature)) {throw newAesException(AesException.ValidateSignatureError);

}

String result=decrypt(echoStr);returnresult;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值