在开发的基础框架代码中使用了base-license文件夹,该模块主要实现的功能为对于lic文件的验证,其中不包含license文件的生成,本文主要讲解如何生成license文件。

license文件简单概况就是授权文件,在代码中,我们使用TrueLicense开源的证书管理来实现授权文件的生成、验证等;

1、生成前准备

在生成授权文件前,首先需要密钥对

插入:密钥对分为公钥与私钥,私钥需要本地储存不泄露,公钥需要对外提供;私钥内部包含证书,对于授权文件进行数字签名,相当于加密的步骤,公钥则是在验证步骤时使用。

生成密钥对的工具有很多,鉴于开发过程中团队使用的都是JAVA,使用了JDK自带的KeyTool作为生成工具。

在JDK中

1)创建私钥

打开CMD,在系统环境变量已配置java相关后,可以使用。语句示例

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

keytool -genkey -alias 密钥别称 -keystore 储存位置,上面默认储存在cmd当前路径下 -validity 密钥有效日期

之后会需要输入密钥的访问密码,务必留好记录

密码规则是英文数字与符号,6位以上。

输入并重复密码后,会输入相关信息,可以比较随意,目前没有发现问题。

2)导出证书
keytool -export -alias privatekey -file certfile.cer -keystore privateKeys.store

keytool -export -alias 密钥别名 -file 导出的证书文件 -keystore 密钥位置

3)导入证书并生成公钥
keytool -import -alias publiccert -file certfile.cer -keystore publicCerts.store

keytool -import -alias 公钥别称 -file 导入的证书文件 -keystore 公钥位置

以上三步全部完成后,会在本地生成3个文件

privateKeys.keystore:私钥,不能泄露。
publicCerts.keystore:公钥,配合license进行授权信息的校验。
certfile.cer:证书,已导入公钥,无用。

记录好公钥别称,私钥别称,公钥私钥密码等所输入的信息

2、授权文件生成

生成的工具类代码放在下面

首先是重写储存路径的类CustomKeyStoreParam

package com.hlyz.base.license.entity;

import de.schlichtherle.license.AbstractKeyStoreParam;
import org.springframework.util.ResourceUtils;

import java.io.*;
/**
 * @author fanghao10
 */
public class CustomKeyStoreParam extends AbstractKeyStoreParam {
    protected CustomKeyStoreParam(Class aClass, String s) {
        super(aClass, s);
    }

    /**
     * 公钥/私钥在磁盘上的存储路径
     */
    private String storePath;
    private String alias;
    private String storePwd;
    private String keyPwd;

    public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePwd, String keyPwd) {
        super(clazz, resource);
        this.storePath = resource;
        this.alias = alias;
        this.storePwd = storePwd;
        this.keyPwd = keyPwd;
    }

    @Override
    public String getAlias() {
        return alias;
    }

    @Override
    public String getStorePwd() {
        return storePwd;
    }

    @Override
    public String getKeyPwd() {
        return keyPwd;
    }

    /**
     * AbstractKeyStoreParam里面的getStream()方法默认文件是存储的项目中。
     * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中
     */
    @Override
    public InputStream getStream() throws IOException {
//        return new FileInputStream(new File(storePath));
        File file = ResourceUtils.getFile(storePath);
        if (file.exists()) {
            return new FileInputStream(file);
        } else {
            throw new FileNotFoundException(storePath);
        }
    }
}

然后是证书的生成工具类

package com.hlyz.base.license.manager;

import com.hlyz.base.license.entity.CustomKeyStoreParam;
import com.hlyz.base.license.entity.License;
import de.schlichtherle.license.*;
import lombok.extern.slf4j.Slf4j;

import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.text.MessageFormat;
import java.util.prefs.Preferences;

/**
 * License生成类 -- 用于license生成
 * @author fanghao10
 */
@Slf4j
public class LicenseCreator {

    private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=GENMER, OU=GENM, O=GENM, L=FUZHOU, ST=FUJIAN, C=CHINA");

    private License license;

    public LicenseCreator(License license) {
        this.license = license;
    }

    /**
     * 生成License证书
     */
    public boolean generateLicense() {
        try {
            LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());
            LicenseContent licenseContent = initLicenseContent();
            licenseManager.store(licenseContent, new File(license.getLicensePath()));
            return true;
        } catch (Exception e) {
            log.error(MessageFormat.format("证书生成失败:{0}", license), e);
            return false;
        }
    }

    /**
     * 初始化证书生成参数
     */
    private LicenseParam initLicenseParam() {
        Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);

        //设置对证书内容加密的秘钥
        CipherParam cipherParam = new DefaultCipherParam(license.getStorePass());

        KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class
                , license.getPrivateKeysStorePath()
                , license.getPrivateAlias()
                , license.getStorePass()
                , license.getKeyPass());

        return new DefaultLicenseParam(license.getSubject()
                , preferences
                , privateStoreParam
                , cipherParam);
    }

    /**
     * 设置证书生成正文信息
     */
    private LicenseContent initLicenseContent() {
        LicenseContent licenseContent = new LicenseContent();
        licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER);
        licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER);

        licenseContent.setSubject(license.getSubject());
        licenseContent.setIssued(license.getIssuedTime());
        licenseContent.setNotBefore(license.getIssuedTime());
        licenseContent.setNotAfter(license.getExpiryTime());
        licenseContent.setConsumerType(license.getConsumerType());
        licenseContent.setConsumerAmount(license.getConsumerAmount());
        licenseContent.setInfo(license.getDescription());

        // 扩展校验,这里可以自定义一些额外的校验信息(用json字符串保存)
		// 使用类保存偶有未知错误出现,可能会是json转换失败等难以预见的错误
        if (license.getLicenseExtraModel() != null) {
            licenseContent.setExtra(license.getLicenseExtraModel());
        }

        return licenseContent;
    }
}

生成的工具类代码就这些,需要生成时在代码中使用如下

// 创建证书实体类
		License param = new License();
		/* 需要往证书中填充的信息
	 	例如
		param.setSubject("licTestSub");
		 */
		LicenseCreator licenseCreator = new LicenseCreator(param);
        // 生成license
        licenseCreator.generateLicense();

此时授权文件的生成地址会在参数中的LicensePath位置生成一个.lic文件,这个文件就是我们说的License

相关问题

java.util.prefs.WindowsPreferences <init> WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5. Exception in thread "main" de.schlichtherle.

注册表中没有相关路径,win+R 输入 regedit,新建一个HKEY_LOCAL_MACHINE\Software\JavaSoft\Prefs

Exception in thread "main" de.schlichtherle.license.IllegalPasswordException: The password does not match the default policy: At least six characters consisting of letters and digits!

密码不合规范

certificate is not yet valid

日期不符合现实

Exception in thread "main" java.io.FileNotFoundException

找不到文件,路径写错或者名称写错,使用带中文的路径也会出现此问题