引言
在微信小程序登录的时候,如果用户之前都没有在相关的应用平台登录过(没有关注过开发者的公众号,没有微信登录过web端,app端),那要获取全平台唯一的unionId,就会遇到一个sha1验签和AES-128-CBC解密问题。然后微信很坑爹的给出的SDK中居然只有php,nodeJs,C++,python,没有给出java的demo。参考微信说明地址:
1.https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
2.https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserInfo.html
关于sha1的验签方法,网上其实有很多;同时AES-128-CBC解密也有很多,但是针对AES-128-CBC加密的就比较少。这里展示博主使用的工具类。
具体做法
一、引入相应的包
org.bouncycastle
bcprov-jdk15on
1.51
二、AESDecryptUtil源代码展示
package com.yuxun.fantuan.framework.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Arrays;
/**
* @author wyk on 2018/11/14
*/
@Slf4j
public class AESDecryptUtil {
// 算法名称
private static final String KEY_ALGORITHM = "AES";
/**
* 加解密算法/模式/填充方式
*/
private static final String algorithmStr = "AES/CBC/PKCS7Padding";
private static Key key;
private static Cipher cipher;
public static String encrypt(byte[] originalContent, byte[] encryptKey, byte[] ivByte) {
try {
encryptKey = Base64.decodeBase64(encryptKey);
ivByte = Base64.decodeBase64(ivByte);
init(encryptKey);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivByte));
byte[] encrypted = cipher.doFinal(originalContent);
return new String(Base64.encodeBase64(encrypted),StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void init(byte[] keyBytes) {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
int base = 16;
if (keyBytes.length % base != 0) {
int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
keyBytes = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
// 转化成JAVA的密钥格式
key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
try {
// 初始化cipher
cipher = Cipher.getInstance(algorithmStr);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
log.error("AES加密算法出错",e);
}
}
/**
* 解密方法
* @param encryptedDataStr
* @param keyBytesStr
* @param ivStr
* @return
*/
public static String decrypt(String encryptedDataStr, String keyBytesStr, String ivStr) {
byte[] encryptedText = null;
byte[] encryptedData = null;
byte[] sessionkey = null;
byte[] iv = null;
try {
sessionkey = Base64.decodeBase64(keyBytesStr);
encryptedData = Base64.decodeBase64(encryptedDataStr);
iv = Base64.decodeBase64(ivStr);
init(sessionkey);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
encryptedText = cipher.doFinal(encryptedData);
} catch (Exception e) {
log.error("AES解密算法出错",e);
}
assert encryptedText != null;
return new String(encryptedText,StandardCharsets.UTF_8);
}
public static void main(String[] args) {
String content = "{\"phoneNumber\":\"18789536667\"}";
String key = "9QJPlP2TyuDFH1A73pnGqg==";
String iv = "SABdIDCax6u7H0f6OILGzw==";
String encrptContent = AESDecryptUtil.encrypt(content.getBytes(),key.getBytes(),iv.getBytes());
System.out.println(encrptContent);
System.out.println(AESDecryptUtil.decrypt(encrptContent, key, iv));
}
}
另外,顺便记录一下验证签名的sha1算法
String sha1Hex = DigestUtils.sha1Hex(miniLoginUserInfoDTO.getRawData()+sessionKey);