License的生成,安装,验证

写这篇的原因:

最近有个项目需要交付给第三方,要使用证书进行验证,不验证就不让他们用

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;
        }
    }

}

还有一些涉及的类,有点多,想要的可以私信我

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值