package org.sxkj.common.utils.license;
|
|
import java.io.*;
|
import java.security.PrivateKey;
|
import java.security.PublicKey;
|
import java.text.DateFormat;
|
import java.text.SimpleDateFormat;
|
import java.util.*;
|
|
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.lang.StringUtils;
|
import org.apache.http.client.utils.DateUtils;
|
import org.sxkj.common.constant.CommonConstant;
|
|
/**
|
* 这个类,在打包的时候,不发布到现场。
|
*
|
* @author stzhang
|
*/
|
public class LicenseUtils {
|
public final static String licenseSourceFileName = "LICENSE.txt";
|
public final static String licenseFileName = "LICENSE.lic";
|
|
public static void initLicensePath(String licenseFilePath, LicenseData licenseData) {
|
System.out.println("-----------开始初始化路径-----------");
|
try {
|
String[] content = convertLicenseDataToFileContent(licenseData);
|
|
initLicensePath(licenseFilePath, licenseSourceFileName, content);
|
} catch (Exception e) {
|
System.out.println(e.getMessage());
|
}
|
System.out.println("-----------初始化路径完成-----------");
|
}
|
|
|
/**
|
* 初始化 License
|
*
|
* @param licenseData
|
* @return
|
*/
|
public static byte[] genLicenseFile( LicenseData licenseData) {
|
System.out.println("-----------开始生成证书-----------");
|
String[] content = convertLicenseDataToFileContent(licenseData);
|
byte[] bytes;
|
try {
|
bytes = genLicenseFile(content);
|
} catch (Exception e) {
|
throw new RuntimeException(e);
|
}
|
|
|
System.out.println("-----------生成证书完成-----------");
|
return bytes;
|
}
|
|
/**
|
* 初始化目录
|
*
|
* @param licensePath
|
* @param sourceFileName
|
* @param defaultLicenseContent
|
* @throws Exception
|
*/
|
private static void initLicensePath(String licensePath, String sourceFileName, String[] defaultLicenseContent) throws Exception {
|
File file = new File(licensePath + File.separator + sourceFileName);
|
File filePath = new File(licensePath);
|
if (filePath.exists()) {
|
System.out.println("路径已经存在:" + filePath.getAbsolutePath());
|
} else {
|
filePath.mkdirs();
|
}
|
if (file.exists()) {
|
System.out.println("License源文件已经存在:" + file.getAbsolutePath());
|
} else {
|
List<String> lines = new ArrayList<String>();
|
for (int j = 0, len = defaultLicenseContent.length; j < len; j++) {
|
if (StringUtils.isNotEmpty(defaultLicenseContent[j])) {
|
lines.add(defaultLicenseContent[j]);
|
}
|
}
|
FileUtils.writeLines(file, "UTF-8", lines);
|
System.out.println("初始化路径完成:" + file.getAbsolutePath());
|
}
|
|
}
|
|
|
/**
|
* 生成证书License文件
|
*
|
* @param defaultLicenseContent: 默认产生的内容
|
* @throws Exception
|
*/
|
private static byte[] genLicenseFile( String[] defaultLicenseContent) throws Exception {
|
// 加载证书
|
PrivateKey privateKey = RSAEnCoder.getPrivateKey(CryptoPrivateKeys.KEYMODULUS, CryptoPrivateKeys.PRIVATEKEYEXPONENT);
|
|
byte[] sByte = stringArrayToBytesWithEncoding(defaultLicenseContent, "UTF-8");
|
byte[] encoderData = RSAEnCoder.encryptRSA(sByte, privateKey);
|
String sign = RSAEnCoder.sign(encoderData, privateKey);
|
//把签名也写进证书, 进行License的自我校验。
|
byte[] signBytes = ("\n" + sign).getBytes();
|
byte[] raw = new byte[encoderData.length + signBytes.length];
|
//合并两个byte[]
|
System.arraycopy(encoderData, 0, raw, 0, encoderData.length);
|
System.arraycopy(signBytes, 0, raw, encoderData.length, signBytes.length);
|
// 双重加密
|
byte[] encoderaw = RSAEnCoder.encryptRSA(raw, privateKey);
|
return encoderaw;
|
|
}
|
|
public static byte[] stringArrayToBytesWithEncoding(String[] strings, String charsetName)
|
throws UnsupportedEncodingException {
|
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
for (String str : strings) {
|
byte[] bytes = str.getBytes(charsetName); // 使用指定编码
|
outputStream.write(bytes, 0, bytes.length);
|
outputStream.write('\n'); // 可选:添加分隔符
|
}
|
|
return outputStream.toByteArray();
|
}
|
|
/**
|
* 校验证书文件
|
*
|
* @param licenseFilePath 文件路径
|
* @param currentDate 当前时间
|
* @return
|
* @throws Exception
|
*/
|
public static boolean checkLicense(String licenseFilePath, Date currentDate) throws Exception {
|
// 加载证书
|
PublicKey publicKey = RSADeCoder.getPublicKey(CryptoPublicKeys.KEYMODULUS, CryptoPublicKeys.PUBLICKEYEXPONENT);
|
File licenseFile = new File(licenseFilePath);
|
if (!licenseFile.exists()) {
|
throw new Exception("没有获取License文件");
|
}
|
FileInputStream fis = FileUtils.openInputStream(licenseFile);
|
if (fis.available() == 0) {
|
throw new FileNotFoundException("License加密文件为空");
|
}
|
fis.close();
|
//首先读入License文件
|
byte[] encoderData = FileUtils.readFileToByteArray(licenseFile);
|
byte[] sbytes = RSADeCoder.decryptRSA(encoderData, publicKey);
|
byte[] firstEncoderData = null;
|
byte[] secondSignData = null;
|
int index = 0;
|
for (int len = sbytes.length; index < len; index++) {
|
if (sbytes[index] == '\n') {
|
firstEncoderData = new byte[index];
|
secondSignData = new byte[len - index - 1];
|
//合并两个byte[]
|
System.arraycopy(sbytes, 0, firstEncoderData, 0, firstEncoderData.length);
|
System.arraycopy(sbytes, index + 1, secondSignData, 0, secondSignData.length);
|
//如果已经是base64编码, 则直接返回
|
if (isArrayByteBase64(secondSignData)) {
|
break;
|
}
|
}
|
}
|
String sign = new String(secondSignData);
|
|
boolean verify = RSADeCoder.verify(firstEncoderData, sign, publicKey);
|
if (!verify) {
|
throw new RuntimeException("License签名校验无效,请确认这证书的有效性.");
|
}
|
//再次解密, 得到最终的授权内容
|
byte[] textBytes = RSADeCoder.decryptRSA(firstEncoderData, publicKey);
|
System.out.println("授权文件详情:\n" + new String(textBytes));
|
HashMap<String, String> prop = genDataFromArrayByte(textBytes);
|
String licenseType = prop.get("LICENSETYPE");
|
System.out.println("授权软件给:\n" + prop.get("LICENSENAME"));
|
String machineCode = prop.get("MACHINECODE");
|
String needCheckMachineCode = prop.get("CHECKMACHINECODE");
|
if ((Boolean.TRUE.toString().equalsIgnoreCase(needCheckMachineCode))) {
|
//唯一机器码
|
String machCode = getSerialNumHDForLinux();
|
if (StringUtils.isEmpty(machineCode) || ("," + machineCode + ",").indexOf("," + machCode + ",") < 0) {
|
//--机器码的校验
|
throw new RuntimeException("License校验,机器码与当前机器码不同");
|
}
|
|
}
|
if ("2".equals(licenseType)) {
|
DateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
|
String expireDay = prop.get("EXPIREDAY");
|
Date expireDate = df.parse(expireDay);
|
if (currentDate.after(expireDate)) {
|
throw new RuntimeException("License已经过期,请联系厂商重新获取License授权.");
|
}
|
}
|
return true;
|
|
}
|
|
/**
|
* 获取文件过期时间
|
*
|
* @param licenseFilePath 文件路径
|
* @return
|
* @throws Exception
|
*/
|
public static Date getLicenseExipreDate(String licenseFilePath) throws Exception {
|
// 加载证书
|
PublicKey publicKey = RSADeCoder.getPublicKey(CryptoPublicKeys.KEYMODULUS, CryptoPublicKeys.PUBLICKEYEXPONENT);
|
File licenseFile = new File(licenseFilePath);
|
if (!licenseFile.exists()) {
|
throw new Exception("没有获取License文件");
|
}
|
FileInputStream fis = FileUtils.openInputStream(licenseFile);
|
if (fis.available() == 0) {
|
throw new FileNotFoundException("License加密文件为空");
|
}
|
fis.close();
|
//首先读入License文件
|
byte[] encoderData = FileUtils.readFileToByteArray(licenseFile);
|
byte[] sbytes = RSADeCoder.decryptRSA(encoderData, publicKey);
|
byte[] firstEncoderData = null;
|
byte[] secondSignData = null;
|
int index = 0;
|
for (int len = sbytes.length; index < len; index++) {
|
if (sbytes[index] == '\n') {
|
firstEncoderData = new byte[index];
|
secondSignData = new byte[len - index - 1];
|
//合并两个byte[]
|
System.arraycopy(sbytes, 0, firstEncoderData, 0, firstEncoderData.length);
|
System.arraycopy(sbytes, index + 1, secondSignData, 0, secondSignData.length);
|
//如果已经是base64编码, 则直接返回
|
if (isArrayByteBase64(secondSignData)) {
|
break;
|
}
|
}
|
}
|
String sign = new String(secondSignData);
|
|
boolean verify = RSADeCoder.verify(firstEncoderData, sign, publicKey);
|
if (!verify) {
|
throw new RuntimeException("License签名校验无效,请确认这证书的有效性.");
|
}
|
//再次解密, 得到最终的授权内容
|
byte[] textBytes = RSADeCoder.decryptRSA(firstEncoderData, publicKey);
|
System.out.println("授权文件详情:\n" + new String(textBytes));
|
HashMap<String, String> prop = genDataFromArrayByte(textBytes);
|
String licenseType = prop.get("LICENSETYPE");
|
System.out.println("授权软件给:\n" + prop.get("LICENSENAME"));
|
String machineCode = prop.get("MACHINECODE");
|
String needCheckMachineCode = prop.get("CHECKMACHINECODE");
|
if ((Boolean.TRUE.toString().equalsIgnoreCase(needCheckMachineCode))) {
|
//唯一机器码
|
String machCode = getSerialNumHDForLinux();
|
if (StringUtils.isEmpty(machineCode) || ("," + machineCode + ",").indexOf("," + machCode + ",") < 0) {
|
//--机器码的校验
|
throw new RuntimeException("License校验,机器码与当前机器码不同");
|
}
|
|
}
|
if ("2".equals(licenseType)) {
|
DateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
|
String expireDay = prop.get("EXPIREDAY");
|
Date expireDate = df.parse(expireDay);
|
return expireDate;
|
}
|
return null;
|
}
|
|
private static HashMap<String, String> genDataFromArrayByte(byte[] b) throws IOException {
|
BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(b)));
|
HashMap<String, String> data = new HashMap<String, String>();
|
String str = null;
|
while ((str = br.readLine()) != null) {
|
if (StringUtils.isNotEmpty(str)) {
|
str = str.trim();
|
int pos = str.indexOf("=");
|
if (pos <= 0) continue;
|
if (str.length() > pos + 1) {
|
data.put(str.substring(0, pos).trim().toUpperCase(), str.substring(pos + 1).trim());
|
} else {
|
data.put(str.substring(0, pos).trim().toUpperCase(), "");
|
}
|
}
|
}
|
return data;
|
}
|
|
private static boolean isArrayByteBase64(byte[] b) {
|
try {
|
if (Base64.isArrayByteBase64(b)) {
|
return true;
|
}
|
return false;
|
} catch (Exception e) {//nothing todo
|
return false;
|
}
|
}
|
|
/**
|
* 获取机器唯一码
|
*
|
* @return
|
* @throws IOException
|
*/
|
public static String getSerialNumHDForLinux() throws IOException {
|
String machineIdLinux = "";
|
String os = System.getProperty("os.name").toLowerCase();
|
if (os.contains("win")) {
|
return "";
|
}
|
try {
|
StringBuffer output = new StringBuffer();
|
|
Process p = Runtime.getRuntime().exec("/bin/bash -c\"hdparm -I /dev/sda | grep Serial\"");
|
BufferedReader sNumReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
|
String line = "";
|
while ((line = sNumReader.readLine()) != null) {
|
output.append(line + "\n");
|
}
|
machineIdLinux = output.toString().substring(output.indexOf("\n"), output.length()).trim();
|
|
} catch (IOException e) {
|
e.getMessage();
|
throw e;
|
}
|
return machineIdLinux;
|
}
|
|
public static String[] convertLicenseDataToFileContent(LicenseData licenseData) {
|
List<String> contentList = new ArrayList<>();
|
|
if (licenseData.getLicenseId() != null) {
|
contentList.add("licenseId=" + licenseData.getLicenseId());
|
}
|
if (licenseData.getLicenseName() != null) {
|
contentList.add("licenseName=" + licenseData.getLicenseName());
|
}
|
if (licenseData.getLicenseType() != null) {
|
contentList.add("licenseType=" + licenseData.getLicenseType());
|
}
|
if (licenseData.getExpireDay() != null) {
|
contentList.add("expireDay=" + DateUtils.formatDate(licenseData.getExpireDay(), CommonConstant.YYYY_MM_DD)); // Using timestamp
|
}
|
if (licenseData.getPrintClientCount() != null) {
|
contentList.add("printClientCount=" + licenseData.getPrintClientCount());
|
}
|
if (licenseData.getCheckMachineCode() != null) {
|
contentList.add("checkMachineCode=" + licenseData.getCheckMachineCode());
|
}
|
if (licenseData.getMachineCode() != null && !licenseData.getMachineCode().isEmpty()) {
|
contentList.add("machineCode=" + String.join(",", licenseData.getMachineCode()));
|
}
|
|
return contentList.toArray(new String[0]);
|
}
|
|
}
|