# 前言
**用一个第三方之前提供加签验签操作 本操作在线上访问该接口 是可以 但是在本地迟迟不行 在同事其他人本地即可运行 **
一、排查法?
**把代码的加密参数 跟同事的一一对比 后面发现一样 只有环境不一样 怀疑环境问题 本人jdk 17环境 由于同事是1.8 环境 **
二、使用步骤
1.源代码
package cn.cws.framework.core.common.util;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @Author zw
* @create 2024/3/27 10:46
* @Description
*/
@Slf4j
public class DanglangSender {
/**
* 发送当郎短信
* @param mobile 接收人手机号
* @param smsTemplate 模板
* @param smsTemplate 替换的内容
* @return 成功或者失败
*/
public static boolean send(String mobile,String smsTemplate, Object[] params) {
String smsMessage = (params==null||params.length ==0)?smsTemplate:String.format(smsTemplate,params);
String post = sendResult(mobile, smsMessage);
JSONObject jsonObject = JSONObject.parseObject(post);
String errorCode = jsonObject.getString("code");
if(errorCode.equalsIgnoreCase("SUCCESS")){
return true;
}else{
log.error("发送当郎短信失败->接收人:{}-内容:{}-返回数据:{}",mobile,smsMessage,post);
return false;
}
}
/**
* 发送当郎短信
* @param mobile 接收人手机号
* @param content 内容
* @return 返回数据自己解析判断
*/
public static String sendResult(String mobile,String content) {
String addr = ""; //替换自己addr
String appId = "";//AppID
String pwd = "";//密钥
String sign = parseByte2HexStr(Objects.requireNonNull(encrypt(String.valueOf(System.currentTimeMillis()), pwd)));//生成签名
List<MtSmsReqInfoDetail> mtSmsReqInfoDetailList = new ArrayList<>();
for (int i = 0; i < 1; i++){
MtSmsReqInfoDetail mtSmsReqInfoDetail = new MtSmsReqInfoDetail();
mtSmsReqInfoDetail.setMobile(mobile);
// mtSmsReqInfoDetail.setSchtime("2017-05-26 19:54:00");
mtSmsReqInfoDetail.setCustomSmsId(String.valueOf(System.currentTimeMillis()));
// mtSmsReqInfoDetail.setExtendedCode("901");
mtSmsReqInfoDetail.setSmscontent(content);
mtSmsReqInfoDetailList.add(mtSmsReqInfoDetail);
}
String s = JSON.toJSONString(mtSmsReqInfoDetailList);
byte[] b = encrypt(s, pwd);//AES加密
String smsData = parseByte2HexStr(b);
String info = "appId="+appId+"&sign="+sign+"&smsData="+smsData;
String post = HttpUtil.post(addr, info);
log.info("发送当郎短信->接收人:{}-内容:{}-返回数据:{}",mobile,content,post);
return post;
}
@Data
public static class MtSmsReqInfoDetail{
private String mobile;
private String customSmsId;
private String smscontent;
}
/**
* 加密
*
* @param content
* 需要加密的内容
* @param password
* 加密密码
* @return
*/
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
String os = System.getProperty("os.name");
if(os.toLowerCase().startsWith("win")){
kgen.init(128, new SecureRandom(password.getBytes()));
}else{
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(password.getBytes());
kgen.init(128, secureRandom);
}
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param content
* 待解密内容
* @param password
* 解密密钥
* @return
*/
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
String os = System.getProperty("os.name");
if(os.toLowerCase().startsWith("win")){
kgen.init(128, new SecureRandom(password.getBytes()));
}else{
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(password.getBytes());
kgen.init(128, secureRandom);
}
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(content);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将二进制转换成16进制
*
* @param buf
* @return
*/
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 将16进制转换为二进制
*
* @param hexStr
* @return
*/
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2),
16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
2.通过debug 怀疑是自己的问题
代码如下(示例):
/**
* 加密
*
* @param content
* 需要加密的内容
* @param password
* 加密密码
* @return
*/
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
String os = System.getProperty("os.name");
//怀疑这里的问题
if(os.toLowerCase().startsWith("win")){
kgen.init(128, new SecureRandom(password.getBytes()));
}else{
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(password.getBytes());
kgen.init(128, secureRandom);
}
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
**由于该代码是第三方提供 以为是没有问题 所以没有过多关注 毕竟线上老项目已经可以运行了的 由于需要项目使用 本地测试不行 后 我用for 打印 **secretKey 发现每次 都会重新生成一次 secretKey 尝试打印该变量
JDK8环境运行
JDK17环境运行
可以看到JDK17两次生成的secretKey值不同
小结
** 那么问题的直接原因应该是JDK8时每次生成的secretKey都相同,而JDK17都不同,导致加密后解密失败**
3. 改为自己认为是这里的代码问题
/**
* 加密
*
* @param content
* 需要加密的内容
* @param password
* 加密密码
* @return
*/
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(password.getBytes());
kgen.init(128, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
经过自己测试 发现完美可行
总结
** 那么问题的直接原因应该是JDK8时每次生成的secretKey都相同,而JDK17都不同,导致加密后 对方第三方 解密失败**
评论区