基于 TrueLicense 项目证书生成


、创建公钥私钥

1、首先要用 KeyTool 工具来生成私匙库:(-alias别名 -validity 3650 表示10年有效,这个时间不是License的过期时间)

keytool -genkey -alias privatekey -keysize 1024 -keystore privateKeys.store -validity 3650

2、然后把私匙库内的证书导出到一个文件当中

keytool -export -alias privatekey -file certfile.cer -keystore privateKeys.store

3、然后再把这个证书文件导入到公匙库

keytool -import -alias publiccert -file certfile.cer -keystore publicCerts.store

根据提示的信息进行填写,完成后将生成三个文件,

  • certfile.cer      不知道干嘛的,没去研究;
  • privateKeys.store      用于当前的 ServerDemo 项目给客户生成license文件;
  • publicCerts.store      则随应用代码部署到客户服务器,用户解密license文件并校验其许可信息;

二、生成证书文件

创建一个maven项目,将privateKeys.store放到创建的项目中去,

引入依赖文件


      junit
      junit
      4.11
      test
    
    
      de.schlichtherle.truelicense
      truelicense-core
      1.33
    
    
      com.alibaba
      fastjson
      1.2.35
    

新建一个Java文件,

public static void main( String[] args ) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入访问密钥库的密码(使用 keytool 生成密钥对时设置的密码):");
        String keyStorePwd = scanner.nextLine();

        System.out.print("请输入生成密钥对的密码(需要妥善保管,不能让使用者知道):");
        String privateKeyPwd = scanner.nextLine();

        System.out.print("请输入项目的唯一识别码:");
        String projectId = scanner.nextLine();

        System.out.print("请输入发布日期(yyyy-MM-dd):");
        String releaseDate = scanner.nextLine();

        System.out.print("请输入有效开始日期(yyyy-MM-dd):");
        String startDate = scanner.nextLine();

        System.out.print("请输入截止日期(yyyy-MM-dd):");
        String expirationDate = scanner.nextLine();

        System.out.print("请输入要注册的MAC地址:");
        String mac = scanner.nextLine();

        System.out.print("请输入证书说明:");
        String info = scanner.nextLine();

        String property = System.getProperty("user.dir") + "\\license.lic";

        Map map = new HashMap<>();
        map.put("alias", "privatekey");
        map.put("privateKeyPwd", privateKeyPwd);
        map.put("keyStorePwd", keyStorePwd);
        map.put("projectId", projectId);
        map.put("issuedTime", releaseDate);
        map.put("notBefore", startDate);
        map.put("notAfter", expirationDate);
        map.put("macAddress", mac);
        map.put("info", info);
        map.put("licPath", property);

        try {
            CreateLicense clicense = new CreateLicense(map);
            clicense.create();
            System.out.println("正在生成文件");
            for (int i = 0; i <= 100; i++) {
                printSchedule(i);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println();

            logger.info("证书生成成功!");
            logger.info("证书路径:" + property);

        }catch(IllegalPasswordException illegalPasswordException) {
            logger.error("文件生成失败!密码与默认策略不匹配:至少6个字符,由字母和数字组成!");
            illegalPasswordException.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static int TOTLE_LENGTH = 20;
    public static void printSchedule(int percent){
        for (int i = 0; i < TOTLE_LENGTH + 10; i++) {
            System.out.print("\b");
        }
        int now = TOTLE_LENGTH * percent / 100;
        for (int i = 0; i < now; i++) {
            System.out.print(">");
        }
        for (int i = 0; i < TOTLE_LENGTH - now; i++) {
            System.out.print(" ");
        }
        System.out.print("  已完成" + percent + "%");
    }

创建两个工具类

import de.schlichtherle.license.*;

import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.prefs.Preferences;

/**
 * @version 1.0
 * @description
 **/
public class CreateLicense {

    /**
     * X500Princal 是一个证书文件的固有格式,详见API
     */
    private final static X500Principal DEFAULT_HOLDERAND_ISSUER = new X500Principal("CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US");

    private String priAlias;
    private String privateKeyPwd;
    private String keyStorePwd;
    private String subject;
    private String priPath;

    private String issued;
    private String notBefore;
    private String notAfter;
    private String macAddress;
    private String consumerType;
    private int consumerAmount;
    private String info;

    private String licPath;


    /**
     * 构造器,参数初始化
     */
    public CreateLicense(Map map) {

        // 私钥的别名
        priAlias = (String) map.get("alias");
        privateKeyPwd = (String) map.get("privateKeyPwd");
        keyStorePwd = (String) map.get("keyStorePwd");
        subject = (String) map.get("projectId");
        // 密钥库的地址(放在 resource 目录下)
        priPath = "/privateKeys.store";
        issued = (String) map.get("issuedTime");
        notBefore = (String) map.get("notBefore");
        notAfter = (String) map.get("notAfter");
        macAddress = (String) map.get("macAddress");
        consumerType = "user";
        consumerAmount = 1;
        info = (String) map.get("info");
        licPath = (String) map.get("licPath");
    }


    /**
     * 生成证书,在证书发布者端执行
     * 18-31-BF-0F-28-D6
     * @throws Exception
     */
    public void create() throws Exception {
        LicenseManager licenseManager = LicenseManagerHolder.getLicenseManager(initLicenseParams());
        licenseManager.store(buildLicenseContent(), new File(licPath));
        System.out.println("------ 证书发布成功 ------");
    }

    /**
     * 初始化证书的相关参数
     * @return
     */
    private LicenseParam initLicenseParams() {
        Class clazz = CreateLicense.class;
        Preferences preferences = Preferences.userNodeForPackage(clazz);
        // 设置对证书内容加密的对称密码
        CipherParam cipherParam = new DefaultCipherParam(keyStorePwd);
        // 参数 1,2 从哪个Class.getResource()获得密钥库;
        // 参数 3 密钥库的别名;
        // 参数 4 密钥库存储密码;
        // 参数 5 密钥库密码
        KeyStoreParam privateStoreParam = new DefaultKeyStoreParam(clazz, priPath, priAlias, keyStorePwd, privateKeyPwd);
        // 返回生成证书时需要的参数
        return new DefaultLicenseParam(subject, preferences, privateStoreParam, cipherParam);
    }

    /**
     * 通过外部配置文件构建证书的的相关信息
     *
     * @return
     * @throws ParseException
     */
    public LicenseContent buildLicenseContent() throws ParseException {
        LicenseContent content = new LicenseContent();
        SimpleDateFormat formate = new SimpleDateFormat("yyyy-MM-dd");
        content.setConsumerAmount(consumerAmount);
        content.setConsumerType(consumerType);
        content.setHolder(DEFAULT_HOLDERAND_ISSUER);
        content.setIssuer(DEFAULT_HOLDERAND_ISSUER);
        content.setIssued(formate.parse(issued));
        content.setNotBefore(formate.parse(notBefore));
        content.setNotAfter(formate.parse(notAfter));
        content.setInfo(info);
        // 扩展字段
        Map map = new HashMap<>(4);
        map.put("mac", macAddress);
        content.setExtra(map);
        return content;
    }
}

运行main方法,输入对应的信息即可生成文件;

四、证书的使用

将生成的license.lic和publicCerts.store文件放到项目中的resources文件夹中,并在resources中创建licenseVerifyParam.properties配置文件信息,

########## 公钥的配置信息 ###########
# 公钥别名
public.alias=test123
# 该密码是访问密钥库的密码 — 使用 keytool 生成密钥对时设置,使用者知道该密码
key.store.pwd=test123
# 项目的唯一识别码 — 和私钥的 subject 保持一致
subject = test
# 证书路径(放在 resource 目录下)
license.dir=/license.lic
# 公共库路径(放在 resource 目录下)
public.store.path=/publicCerts.store

创建文件检查license,分别在项目启动和定时任务中检查

import com.superscene.xopendisplayserver.common.config.License.VerifyLicense;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

/**
 * @version 1.0
 * @description 服务权限检查类
 **/
@Slf4j
@Configuration
@EnableScheduling
public class AuthorityConfig implements CommandLineRunner {

    private static volatile VerifyLicense vlicense = null;

    /**
    * @description  服务启动检查
    **/
    @Override
    public void run(String... args) {
        vlicense = new VerifyLicense();
        vlicense.install();
        if (!vlicense.vertify()) {
            Runtime.getRuntime().halt(1);
        } else {
            log.info("证书安装成功");
        }
    }

    /**
    * @description  定时检查权限
    **/
    @Scheduled(cron = "0 1 0 1/1 * ?")
    private void configureTasks() {
        vlicense.install();
        if (!vlicense.vertify()) {
            Runtime.getRuntime().halt(1);
        }
    }

}
import com.superscene.xopendisplayserver.common.utils.ComputerTools;
import de.schlichtherle.license.*;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.prefs.Preferences;

import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX;
import static org.springframework.util.ResourceUtils.getFile;

/**
 * @version 1.0
 * @description
 **/
@Slf4j
public class VerifyLicense {

    private String pubAlias;
    private String keyStorePwd;
    private String subject;
    private String licDir;
    private String pubPath;

    public VerifyLicense() {
        // 读取默认配置
        setConf("/licenseVerifyParam.properties");
    }

    public VerifyLicense(String confPath) {
        setConf(confPath);
    }

    /**
     * 通过外部配置文件获取配置信息
     *
     * @param confPath 配置文件路径
     */
    private void setConf(String confPath) {
        // 获取参数
        Properties prop = new Properties();
        InputStream in = getClass().getResourceAsStream(confPath);
        try {
            prop.load(in);
        } catch (IOException e) {
            log.error("VerifyLicense Properties load inputStream error.", e);
        }
        this.subject = prop.getProperty("subject");
        this.pubAlias = prop.getProperty("public.alias");
        this.keyStorePwd = prop.getProperty("key.store.pwd");
        this.licDir = prop.getProperty("license.dir");
        this.pubPath = prop.getProperty("public.store.path");
    }

    /**
     * 安装证书证书
     */
    public void install() {
        try {
            LicenseManager licenseManager = getLicenseManager();
            File file = getFile(CLASSPATH_URL_PREFIX + licDir);
            licenseManager.install(file);
            LicenseContent verify = licenseManager.verify();
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String issued = formatter.format(verify.getIssued());
            String notAfter = formatter.format(verify.getNotAfter());
            log.info("发布日期:" + issued);
            log.info("到期日期:" + notAfter);
        } catch (Exception e) {
            log.error("安装证书失败!", e);
            Runtime.getRuntime().halt(1);
        }

    }

    private LicenseManager getLicenseManager() {
        return LicenseManagerHolder.getLicenseManager(initLicenseParams());
    }

    /**
     * 初始化证书的相关参数
     */
    private LicenseParam initLicenseParams() {
        Class clazz = VerifyLicense.class;
        Preferences pre = Preferences.userNodeForPackage(clazz);
        CipherParam cipherParam = new DefaultCipherParam(keyStorePwd);
        KeyStoreParam pubStoreParam = new DefaultKeyStoreParam(clazz, pubPath, pubAlias, keyStorePwd, null);
        return new DefaultLicenseParam(subject, pre, pubStoreParam, cipherParam);
    }

    /**
     * 验证证书的合法性 issued  notBefore  notAfter
     */
    public boolean vertify() {
        try {
            LicenseManager licenseManager = getLicenseManager();
            LicenseContent verify = licenseManager.verify();
            Map extra = (Map) verify.getExtra();
            String mac = extra.get("mac");
            String localMac = ComputerTools.getMacAddress();
            if (!Objects.equals(mac, localMac)) {
                log.error("MAC 地址验证不通过");
                return false;
            }
            log.info("验证证书成功!");
            return true;
        } catch (LicenseContentException ex) {
            log.error("证书已经过期!", ex);
            return false;
        } catch (Exception e) {
            log.error("验证证书失败!", e);
            return false;
        }
    }

    public static void main(String[] args) {
        VerifyLicense vlicense = new VerifyLicense();
        vlicense.install();
        if (!vlicense.vertify()) {
            Runtime.getRuntime().halt(1);
        } else {
            System.out.println("安装成功");
        }
    }
}
import de.schlichtherle.license.LicenseManager;
import de.schlichtherle.license.LicenseParam;

/**
 * @version 1.0
 * @description
 **/
public class LicenseManagerHolder {

    private static volatile LicenseManager licenseManager = null;

    private LicenseManagerHolder() {
    }

    public static LicenseManager getLicenseManager(LicenseParam param) {
        if (licenseManager == null) {
            synchronized (LicenseManagerHolder.class) {
                if (licenseManager == null) {
                    licenseManager = new LicenseManager(param);
                }
            }
        }
        return licenseManager;
    }
}

至此就结束了,虽然并没有实质性的卵用,但是老板要,我也没办法,所有的代码都在上面了,基本拉过去就可以直接使用,仅做分享,有不足的地方还希望各位大佬指点。