package ***;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
public class AES {
// 算法名
public static final String KEY_NAME = "AES";
// 加解密算法/模式/填充方式
// ECB模式只用密钥即可对数据进行加密解密,CBC模式需要添加一个iv
public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
/**
* 微信 数据解密
* 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充
* 对称解密的目标密文:encrypted=Base64_Decode(encryptData)
* 对称解密秘钥:key = Base64_Decode(session_key),aeskey是16字节
* 对称解密算法初始向量:iv = Base64_Decode(iv),同样是16字节
*
* @param encrypted 目标密文
* @param session_key 会话ID
* @param iv 加密算法的初始向量
*/
public static String wxDecrypt(String encrypted, String session_key, String iv) {
String json = null;
byte[] encrypted64 = Base64.decodeBase64(encrypted);
byte[] key64 = Base64.decodeBase64(session_key);
byte[] iv64 = Base64.decodeBase64(iv);
byte[] data;
try {
init();
json = new String(decrypt(encrypted64, key64, generateIV(iv64)));
} catch (Exception e) {
e.printStackTrace();
}
return json;
}
/**
* 初始化密钥
*/
public static void init() throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyGenerator.getInstance(KEY_NAME).init(128);
}
/**
* 生成iv
*/
public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
// iv 为一个 16 字节的数组,这里采用和 iOS 端一样的构造方法,数据全为0
// Arrays.fill(iv, (byte) 0x00);
AlgorithmParameters params = AlgorithmParameters.getInstance(KEY_NAME);
params.init(new IvParameterSpec(iv));
return params;
}
/**
* 生成解密
*/
public static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv) throws Exception {
Key key = new SecretKeySpec(keyBytes, KEY_NAME);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(encryptedData);
}
}
在小程序前端通过wx.login获得用户授权码code以后,我们用appid、appSecret、code请求微信的如下接口:
请求方式:http get
接口地址:https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
注意:APPID、SECRET、JSCODE即为需要传的参数appid、appSecret、code,顺序依次对应。
public DecodeTelResVo decodeTel(DecodeTelReqVo reqVo) throws Exception {
DecodeTelResVo resVo=new DecodeTelResVo();
Map params=new HashMap();
params.put("APPID", reqVo.getAppId());
params.put("SECRET", reqVo.getSecret());
params.put("JSCODE", reqVo.getJsCode());
String url= backUrlProperty.getWechatopenidUrl()+"/sns/jscode2session?appid={APPID}&secret={SECRET}&js_code={JSCODE}&grant_type=authorization_code";
String jsonStr = restTemplate.getForObject(url, String.class,params);
System.out.println("调用微信接口返回数据:"+jsonStr);
if(!jsonStr.contains("session_key")) {
throw new Exception("用户信息获取失败,请重新打开小程序!_9999");
}
JSONObject jsonObj=JSONObject.parseObject(jsonStr);
if(jsonObj==null) {
throw new Exception("用户信息获取失败,请重新打开小程序!_9999");
}
String tel="";
try {
System.out.println("解密前的手机号:"+reqVo.getTel());
tel = AES.wxDecrypt(reqVo.getTel(), jsonObj.getString("session_key"), reqVo.getIv());
System.out.println("解密后的手机号:"+reqVo.getTel());
} catch (Exception e) {
throw new Exception("用户信息获取失败,请重新打开小程序!_9999");
}
resVo.setSuccess(true);
resVo.setTel(tel);
resVo.setErrorCode("200");
return resVo;
}
注意文中的get请求调接口的传参方式,我最初做的时候就在这出了错导致屡次失败。
package ***;
import org.hibernate.validator.constraints.NotEmpty;
import com.shibo.etc.net.issuer.server.issuer.vo.base.BaseReqVo;
import io.swagger.annotations.ApiModelProperty;
public class DecodeTelReqVo extends BaseReqVo {
@ApiModelProperty("加密的手机号")
@NotEmpty(message = "用户信息获取失败,请重新打开小程序")
private String tel;
@ApiModelProperty("APPID")
@NotEmpty(message = "用户信息获取失败,请重新打开小程序")
private String appId;
@ApiModelProperty("SECRET")
@NotEmpty(message = "用户信息获取失败,请重新打开小程序")
private String secret;
@ApiModelProperty("JSCODE")
@NotEmpty(message = "用户信息获取失败,请重新打开小程序")
private String jsCode;
@ApiModelProperty("iv是从小程序获取到的加密参数")
@NotEmpty(message = "用户信息获取失败,请重新打开小程序")
private String iv;
/**
* @return the tel
*/
public String getTel() {
return tel;
}
/**
* @param tel the tel to set
*/
public void setTel(String tel) {
this.tel = tel;
}
/**
* @return the appId
*/
public String getAppId() {
return appId;
}
/**
* @param appId the appId to set
*/
public void setAppId(String appId) {
this.appId = appId;
}
/**
* @return the secret
*/
public String getSecret() {
return secret;
}
/**
* @param secret the secret to set
*/
public void setSecret(String secret) {
this.secret = secret;
}
/**
* @return the jsCode
*/
public String getJsCode() {
return jsCode;
}
/**
* @param jsCode the jsCode to set
*/
public void setJsCode(String jsCode) {
this.jsCode = jsCode;
}
/**
* @return the iv
*/
public String getIv() {
return iv;
}
/**
* @param iv the iv to set
*/
public void setIv(String iv) {
this.iv = iv;
}
/**
* 开发人员: wy
* @return
*/
@Override
public String toString() {
return "DecodeTelReqVo [tel=" + tel + ", appId=" + appId + ", secret=" + secret + ", jsCode=" + jsCode + ", iv="
+ iv + "]";
}
}
package ***;
import com.shibo.etc.net.issuer.server.issuer.vo.base.BaseResVo;
import io.swagger.annotations.ApiModelProperty;
public class DecodeTelResVo extends BaseResVo {
@ApiModelProperty("解密后的手机号")
private String tel;
/**
* @return the tel
*/
public String getTel() {
return tel;
}
/**
* @param tel the tel to set
*/
public void setTel(String tel) {
this.tel = tel;
}
/**
* 开发人员: wy
* @return
*/
@Override
public String toString() {
return "DecodeTelResVo [tel=" + tel + "]";
}
}