写这篇的原因:
最近有个项目需要交付给第三方,要使用证书进行验证,不验证就不让他们用
license的流程:
生成:
- license许可证应包含必要的信息,如许可证ID、用户ID、产品ID、有效期、许可类型(如单用户、多用户、永久、试用等)。
- 加密算法:使用非对称加密算法(如RSA)生成公钥和私钥。私钥用于签名许可证,公钥用于验证许可证的签名。
- 签名:使用私钥对许可证的内容进行数字签名,确保许可证的完整性和防篡改性。
导入安装:
用户手动导入许可证,提供一个许可证导入接口(即上传文件并安装)
验证:
- 定期或在特定事件(如使用某些受限功能)时,验证许可证的合法性。
- 或在拦截器中添加验证的逻辑,不需要验证的接口放行即可
license的生成和验证
1.使用工具生成秘钥对(公钥和私钥)
我是在jdk/bin目录下使用keytool生成的,需要的命令如下:
- 创建密钥库(包含公钥、私钥)
- keytool -genkeypair -alias avc-privatekey -keysize 1024 -keystore private-keystore.p12 -storetype PKCS12 -dname "CN=XX, OU=WONSEC, O=WONSEC, L=HZ, ST=ZJ, C=CN"
- 导出证书(证书中包含公钥)
- keytool -exportcert -alias "avc-privatekey" -keystore private-keystore.p12 -file "certfile.cer"
- 导入证书(生成新的密钥库,其中只包含公钥)
- keytool -import -alias "avc-publickey" -file "certfile.cer" -keystore "public-keystore.p12" -storetype PKCS12
2.获取硬件的序列号等
3.根据序列号和秘钥,私钥生成证书
4.证书上传验证
相关代码如下:
获取序列号的类
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wonsec.cc.core.model.LicenseExtraParam;
import com.wonsec.cc.core.service.AServerInfos;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Base64;
@Service
public class UserServerInfo {
@Value("${license.key}")
private String key;
public String ServerInfo() throws Exception {
System.out.println("key = " + key);
LicenseExtraParam serverInfos = AServerInfos.getServer("").getServerInfos();
//对象序列化为json
String jsonString = serverInfos.toJsonString();
System.out.println("原始数据 = " + jsonString);
//加密
String encryptedJson = encrypt(jsonString);
System.out.println("加密后 = " + encryptedJson);
// 解密
String decryptedJson = decrypt(encryptedJson);
System.out.println("解密后 = " + decryptedJson);
// 比较解密后的字符串与原始字符串
if (jsonString.equals(decryptedJson)) {
System.out.println("解密后的字符串与原始字符串相同");
} else {
System.out.println("解密后的字符串与原始字符串不同");
}
// 断言
assert jsonString.equals(decryptedJson) : "解密后的字符串与原始字符串不匹配";
// 反序列化为对象
ObjectMapper mapper = new ObjectMapper();
LicenseExtraParam licenseParam = null;
licenseParam = mapper.readValue(decryptedJson, LicenseExtraParam.class);
// 标记为null的字段需要特殊处理
if ("null".equals(licenseParam.getRegisterAmount())) {
licenseParam.setRegisterAmount(null);
}
System.out.println("licenseParam = " + licenseParam);
return encryptedJson;
}
public String encrypt(String json) throws Exception {
byte[] keyBytes = key.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
keyBytes = sha.digest(keyBytes);
keyBytes = java.util.Arrays.copyOf(keyBytes, 16);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(json.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public String decrypt(String encryptedJson) throws Exception {
byte[] keyBytes = key.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
keyBytes = sha.digest(keyBytes);
keyBytes = java.util.Arrays.copyOf(keyBytes, 16);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedJson);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
}
controller調用代碼如下:
@RestController
@RequestMapping("/license")
public class SeverInfoController {
@Resource
private UserServerInfo userServerInfo;
@ResponseBody
@RequestMapping(value = "/severInfo", method = RequestMethod.POST)
public Map<String,Object> severInfo() throws Exception {
JSONObject jsonObject = new JSONObject();
String value = userServerInfo.ServerInfo();
jsonObject.put("status","200");
jsonObject.put("needValue",value);
return jsonObject;
}
}
创建License的类如下:
public class Main {
public static void main(String[] args) {
// if (args.length == 1 && args[0].equals("-h")) {
// System.out.println("用法: java YourProgramName -k key -i IssuedTime -e ExpiryTime");
// System.out.println("参数说明:");
// System.out.println("-k: 许可证密钥");
// System.out.println("-i: 许可证生效时间(格式: yyyy-MM-dd HH:mm:ss)");
// System.out.println("-e: 许可证过期时间(格式: yyyy-MM-dd HH:mm:ss)");
// System.out.println("示例: java YourProgramName -k ABC123 -i \"2024-05-09 12:00:00\" -e \"2024-05-10 12:00:00\"");
// } else {
// 处理其他参数
//key是上一步接口生成的序列号加密码
String key = "OKMn+Ljh9pMWlb5a4obb9axCwCNAkOzjlHJ3r cxvvdfdCFskgckwY/7RS4gV0BaMz2ySKRu09UDKh+jNHQ4Uy7I4GskVhSfUtC/fQSqcjEMmsNOpPCmxnVWVT7oMQmpeXYR9VkkebaD+1pjx114O+8";
String IssuedTime = "2024-07-15 09:40:00";
String ExpiryTime = "2024-07-22 17:30:00";
// 判断传入参数
// for (int i = 0; i < args.length; i++) {
// if (args[i].equals("-k") && i + 1 < args.length) {
// key = args[i + 1];
// } else if (args[i].equals("-i") && i + 1 < args.length) {
// IssuedTime = args[i + 1];
// } else if (args[i].equals("-e") && i + 1 < args.length) {
// ExpiryTime = args[i + 1];
// }
// }
if (key.isEmpty() || IssuedTime.isEmpty() || ExpiryTime.isEmpty()) {
System.out.println("参数不完整。请使用 '-h' 获取帮助信息。");
} else {
Create create = new Create();
try {
create.Create(key, IssuedTime, ExpiryTime);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// }
}
}
Create类如下:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wonsec.cc.core.model.LicenseCreatorParam;
import com.wonsec.cc.core.model.LicenseExtraParam;
import com.wonsec.cc.core.service.AServerInfos;
import com.wonsec.cc.core.utils.CommonUtils;
import com.wonsec.cc.creator.config.LicenseCreatorProperties;
import com.wonsec.cc.creator.service.LicenseCreatorService;
import com.wonsec.cc.verify.Sevice.UserServerInfo;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.TimeZone;
/**
* @Author: WangYao
* @description:
* @date: 2024-05-08 10:36
*/
public class Create {
private static final String key = "mySecretKey";
private LicenseCreatorService creatorService = new LicenseCreatorService();
private LicenseCreatorProperties properties = new LicenseCreatorProperties();
public void Create(String key,String IssuedTime,String ExpiryTime) throws Exception {
// 解密
String decryptedJson = decrypt(key);
// 反序列化为对象
ObjectMapper mapper = new ObjectMapper();
LicenseExtraParam licenseParam = null;
licenseParam = mapper.readValue(decryptedJson, LicenseExtraParam.class);
// 设置需要校验那些硬件
licenseParam.setCpuCheck(true);
licenseParam.setMacCheck(true);
licenseParam.setIpCheck(true);
licenseParam.setBoardCheck(true);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
// 如果没有人为的指定lic要生成的位置,则程序自动处理
LicenseCreatorParam param = new LicenseCreatorParam();
param.setSubject("license");
param.setPrivateAlias("privateKeys");
param.setKeyPass("123456a");
param.setStorePass("123456a");
param.setPrivateKeysStorePath("/privateKeys.store");
// param.setIssuedTime(dateFormat.parse("2024-03-01 08:30:00"));
// param.setExpiryTime(dateFormat.parse("2024-05-09 14:38:00"));
param.setIssuedTime(dateFormat.parse(IssuedTime));
param.setExpiryTime(dateFormat.parse(ExpiryTime));
param.setDescription("系统软件许可证书");
// 获取当前机器证书
// LicenseExtraParam serverInfos = AServerInfos.getServer("").getServerInfos();
LicenseExtraParam serverInfos = licenseParam;
param.setLicenseCheck(serverInfos);
if (CommonUtils.isEmpty(param.getLicensePath())) {
// 设置格式
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
String tempPath = properties.getTempPath();
if (tempPath == null || "".equals(tempPath)) {
// 如果默认临时文件等于空的话,就获取当前服务执行的路径
tempPath = AServerInfos.getServerTempPath();
}
// 根据时间戳,命名lic文件
String licDir = tempPath + "/lic-files/" + format.format(System.currentTimeMillis());
File file = new File(licDir);
if (!file.exists()) {
if (!file.mkdirs()) {
throw new RuntimeException("创建目录" + licDir + ",失败,请检查是是否有创建目录的权限或者手动进行创建!");
}
}
/**统一下路径分隔符*/
param.setLicensePath(licDir.replace("\\", "/") + "/license.lic");
creatorService.generateLicense(param);
}
}
public void LicenseCreate(String key,String IssuedTime,String ExpiryTime) throws Exception {
// 解密
String decryptedJson = decrypt(key);
// 反序列化为对象
ObjectMapper mapper = new ObjectMapper();
LicenseExtraParam licenseParam = null;
licenseParam = mapper.readValue(decryptedJson, LicenseExtraParam.class);
// 设置需要校验那些硬件
licenseParam.setCpuCheck(true);
licenseParam.setMacCheck(true);
licenseParam.setIpCheck(true);
licenseParam.setBoardCheck(true);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
// 如果没有人为的指定lic要生成的位置,则程序自动处理
LicenseCreatorParam param = new LicenseCreatorParam();
param.setSubject("license");
param.setPrivateAlias("privateKeys");
param.setKeyPass("123456a");
param.setStorePass("123456a");
param.setPrivateKeysStorePath("/privateKeys.store");
// param.setIssuedTime(dateFormat.parse("2024-03-01 08:30:00"));
// param.setExpiryTime(dateFormat.parse("2024-05-09 14:38:00"));
param.setIssuedTime(dateFormat.parse(IssuedTime));
param.setExpiryTime(dateFormat.parse(ExpiryTime));
param.setDescription("系统软件许可证书");
// 获取当前机器证书
// LicenseExtraParam serverInfos = AServerInfos.getServer("").getServerInfos();
LicenseExtraParam serverInfos = licenseParam;
param.setLicenseCheck(serverInfos);
if (CommonUtils.isEmpty(param.getLicensePath())) {
// 设置格式
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
String tempPath = properties.getTempPath();
if (tempPath == null || "".equals(tempPath)) {
// 如果默认临时文件等于空的话,就获取当前服务执行的路径
tempPath = AServerInfos.getServerTempPath();
}
// 根据时间戳,命名lic文件
String licDir = tempPath + "/lic-files/" + format.format(System.currentTimeMillis());
File file = new File(licDir);
if (!file.exists()) {
if (!file.mkdirs()) {
throw new RuntimeException("创建目录" + licDir + ",失败,请检查是是否有创建目录的权限或者手动进行创建!");
}
}
/**统一下路径分隔符*/
param.setLicensePath(licDir.replace("\\", "/") + "/license.lic");
creatorService.generateLicense(param);
}
}
public static String decrypt(String encryptedJson) throws Exception {
// byte[] keyBytes = key.getBytes();
// MessageDigest sha = MessageDigest.getInstance("SHA-256");
// keyBytes = sha.digest(keyBytes);
// keyBytes = Arrays.copyOf(keyBytes, 16);
// SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
// Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
// cipher.init(Cipher.DECRYPT_MODE, secretKey);
// byte[] encryptedBytes = Base64.getDecoder().decode(encryptedJson);
// byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
// return new String(decryptedBytes);
byte[] keyBytes = key.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
keyBytes = sha.digest(keyBytes);
keyBytes = java.util.Arrays.copyOf(keyBytes, 16);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedJson);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
}
证书的上传和验证接口如下:
import com.alibaba.fastjson.JSONObject;
import com.mm.cc.Utils.FileUtils;
import com.mm.cc.verify.Sevice.VerifyMain;
import com.mm.cc.verify.Sevice.VerifyValue;
import com.mm.cc.verify.config.LicenseVerifyProperties;
import com.mm.cc.verify.listener.LicenseVerifyInstallListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
@RestController
@RequestMapping("/license")
public class UploadLicenseFile {
@Value("${license.licenseFilePath}")
private String licenseFilePath;
@Resource
private VerifyValue verifyValue;
@ResponseBody
@RequestMapping(value = "/uploadLicenseFile", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
public JSONObject uploadLicenseFile(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
LicenseVerifyProperties licenseVerifyProperties = verifyValue.licenseVerifyProperties();
JSONObject jsonObject=new JSONObject();
//获取原始文件名
String fileName = file.getOriginalFilename();
if (fileName == null || "".equals(fileName)) {
jsonObject.put("status",303);
jsonObject.put("msg","上传证书不能为空");
return jsonObject;
}
//在此判断文件类型是否为.lic
String fileExtension = getFileSuffix(fileName);
if (!fileExtension.equals(".lic")) {
// 文件类型不正确,返回错误信息
jsonObject.put("status",305);
jsonObject.put("msg","证书格式不正确");
return jsonObject;
}
// if (!fileName.equals("license.lic")) {
// // 文件类型不正确,返回错误信息
// jsonObject.put("status",305);
// jsonObject.put("msg","证书名称不正确");
// return jsonObject;
// }
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
// String filePath = licenseFilePath + "/license/" + format.format(System.currentTimeMillis())+"/";
String filePath = licenseFilePath + format.format(System.currentTimeMillis())+"/";
try {
FileUtils.upload(file.getBytes(), filePath, fileName);
} catch (Exception e) {
e.printStackTrace();
}
boolean b= LicenseVerifyInstallListener.install1(licenseVerifyProperties);
// boolean b = verifyMain.installListener();
if (b) {
jsonObject.put("status",200);
jsonObject.put("msg","证书上传并安装成功");
return jsonObject;
} else {
jsonObject.put("status",500);
jsonObject.put("msg","证书安装失败");
return jsonObject;
}
}
private String getFileSuffix(String fileName) {
int dotIndex = fileName.lastIndexOf(".");
if (dotIndex < 0) {
return "";
}
return fileName.substring(dotIndex);
}
}
用到的验证类:
import com.mm.cc.core.model.LicenseResult;
import com.mm.cc.core.model.LicenseVerifyManager;
import com.mm.cc.core.utils.CommonUtils;
import com.mm.cc.verify.config.LicenseVerifyProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class LicenseVerifyInstallListener {
// @Autowired
// LicenseVerifyProperties licenseVerifyProperties;
public LicenseVerifyProperties licenseVerifyProperties;
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> scheduledFuture;
// 启动定时任务
public void startTimer() {
scheduledFuture = scheduler.scheduleAtFixedRate(this::timer, 0, 5, TimeUnit.SECONDS);
}
// 停止定时任务
public void stopTimer() {
if (scheduledFuture != null) {
scheduledFuture.cancel(false);
}
}
/**
* 文件唯一身份标识 == 相当于人类的指纹一样
*/
private static String md5 = "";
private static boolean isLoad = false;
public static void LicenseVerifyInstall(LicenseVerifyProperties licenseVerifyProperties) {
new LicenseVerifyInstallListener(licenseVerifyProperties);
}
public LicenseVerifyInstallListener(LicenseVerifyProperties licenseVerifyProperties) {
// startTimer();
this.licenseVerifyProperties = licenseVerifyProperties;
System.out.println("licenseVerifyProperties.getLicensePath() = " + licenseVerifyProperties.getLicensePath());
if (CommonUtils.isNotEmpty(licenseVerifyProperties.getLicensePath())) {
install();
try {
String readMd5 = getMd5(licenseVerifyProperties.getLicensePath());
isLoad = true;
if (LicenseVerifyInstallListener.md5 == null || "".equals(LicenseVerifyInstallListener.md5)) {
LicenseVerifyInstallListener.md5 = readMd5;
}
} catch (Exception e) {
}
}
}
/**
* 5秒检测一次,不能太快也不能太慢
*/
protected void timer() {
if (!isLoad) {
return;
}
String readMd5 = null;
try {
readMd5 = getMd5(licenseVerifyProperties.getLicensePath());
} catch (Exception e) {
throw new RuntimeException(e);
}
// 不相等,说明lic变化了
if (!readMd5.equals(LicenseVerifyInstallListener.md5)) {
install();
LicenseVerifyInstallListener.md5 = readMd5;
}
}
private void install() {
System.out.println("++++++++ 开始安装证书 ++++++++");
LicenseVerifyManager licenseVerifyManager = new LicenseVerifyManager();
/** 走定义校验证书并安装 */
LicenseResult result = licenseVerifyManager.install(licenseVerifyProperties.getVerifyParam());
if (result.getResult()) {
System.out.println("++++++++ 证书安装成功 ++++++++");
} else {
System.out.println("++++++++ 证书安装失败 ++++++++");
}
}
public static boolean install1(LicenseVerifyProperties licenseVerifyProperties) {
System.out.println("licenseVerifyProperties = " + licenseVerifyProperties);
System.out.println("++++++++ 开始安装证书 ++++++++");
LicenseVerifyManager licenseVerifyManager = new LicenseVerifyManager();
/** 走定义校验证书并安装 */
LicenseResult result = licenseVerifyManager.install(licenseVerifyProperties.getVerifyParam());
if (result.getResult()) {
System.out.println("++++++++ 证书安装成功 ++++++++");
return true;
} else {
System.out.println("++++++++ 证书安装失败 ++++++++");
return false;
}
}
/**
* <p>获取文件的md5</p>
*/
public String getMd5(String filePath) throws Exception {
File file;
String md5 = "";
try {
file = new File(filePath);
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
md5 = calculateMD5(data);
is.close();
}
} catch (FileNotFoundException e) {
}
return md5;
}
public static String calculateMD5(byte[] data) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hashBytes = md.digest(data);
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace(); // 处理异常,例如打印错误信息
return null;
}
}
}
还有一些涉及的类,有点多,想要的可以私信我
标签:keyBytes,String,License,验证,System,param,生成,new,import From: https://blog.csdn.net/smokeblue/article/details/140522327