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 lines = new ArrayList(); 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 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 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 genDataFromArrayByte(byte[] b) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(b))); HashMap data = new HashMap(); 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 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]); } }