TrueLicense实现license验证

Posted tuacy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TrueLicense实现license验证相关的知识,希望对你有一定的参考价值。

       TrueLicense是一个开源的证书管理引擎,可以用于license的生成和有效性的验证。

一 使用keytool生产密钥对

       keytool是jdk里面自带的命令。我们直接用keytool命令来生成密钥对。需要执行的命令如下(命令里面的参数大家根据情况不同做相应的调整)

## 1. 生成私匙库
# validity:私钥的有效期多少天
# alias:私钥别称
# keystore: 指定私钥库文件的名称(生成在当前目录)
# storepass:指定私钥库的密码(获取keystore信息所需的密码) 
# keypass:指定别名条目的密码(私钥的密码) 
keytool -genkeypair -keysize 1024 -validity 3650 -alias "privateKey" -keystore "privateKeys.keystore" -storepass "a123456" -keypass "a123456" -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN"

## 2. 把私匙库内的公匙导出到一个文件当中
# alias:私钥别称
# keystore:指定私钥库的名称(在当前目录查找)
# storepass: 指定私钥库的密码
# file:证书名称
keytool -exportcert -alias "privateKey" -keystore "privateKeys.keystore" -storepass "a123456" -file "certfile.cer"

## 3. 再把这个证书文件导入到公匙库
# alias:公钥别称
# file:证书名称
# keystore:公钥文件名称
# storepass:指定私钥库的密码
keytool -import -alias "publicCert" -file "certfile.cer" -keystore "publicCerts.keystore" -storepass "a123456"


       在任意目录下执行完上述三个命令之后。我们会在当前目录下面得到三个文件:privateKeys.keystore、publicCerts.keystore、certfile.cer。

  • privateKeys.keystore:私钥,这个我们自己留着,不能泄露给别人。
  • publicCerts.keystore:公钥,这个给客人用的。在我们程序里面就是用他来解析license文件里面的信息的。
  • certfile.cer:这个文件没啥用,可以删掉。

二 TrueLicense的使用

       我们已经生成好了密钥对(私钥、公钥)。接下来就是生成license文件,已经严重license是否有效了。license文件里面保存了我们需要验证的一些信息。这里没问使用TrueLicense来实现license的创建和验证。

项目中添加TrueLicense依赖

        <!-- lisence验证 -->
        <dependency>
            <groupId>de.schlichtherle.truelicense</groupId>
            <artifactId>truelicense-core</artifactId>
            <version>1.33</version>
            <scope>provided</scope>
        </dependency>

2.1 代码封装

       自定义KeyStoreParam类CustomKeyStoreParam类继承AbstractKeyStoreParam,实现里面一些该实现的方法。并且重写getStream()获取文件内容的方法,改成从磁盘位置读取。

/**
 * 自定义KeyStoreParam,用于将公私钥存储文件存放到其他磁盘位置而不是项目中。现场使用的时候公钥大部分都不会放在项目中的
 */
public class CustomKeyStoreParam extends AbstractKeyStoreParam 

    /**
     * 公钥/私钥在磁盘上的存储路径
     */
    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));
    

       自定义一个LicenseExtraModel类。我们可以在这里添加一些额外的验证信息(TrueLicense默认只帮我们验证了时间),比如我们可以验证客户的机器码啥的。大家可以根据具体的需求添加具体的字段。

/**
 * 自定义需要校验的License参数,可以增加一些额外需要校验的参数,比如项目信息,ip地址信息等等,待完善
 */
public class LicenseExtraModel 

    // 这里可以添加一些往外的自定义信息,比如我们可以增加项目验证,客户电脑sn码的验证等等


       继承LicenseManager类,增加我们额外信息的验证(TrueLicense默认只给我们验证了时间)。大家需要根据自己的需求在validate()里面增加额外的验证。

/**
 * 自定义LicenseManager,用于增加额外的信息校验(除了LicenseManager的校验,我们还可以在这个类里面添加额外的校验信息)
 */
public class CustomLicenseManager extends LicenseManager 

    private static Logger logger = LogManager.getLogger(CustomLicenseManager.class);

    public CustomLicenseManager(LicenseParam param) 
        super(param);
    

    /**
     * 复写create方法
     */
    @Override
    protected synchronized byte[] create(LicenseContent content, LicenseNotary notary) throws Exception 
        initialize(content);
        this.validateCreate(content);
        final GenericCertificate certificate = notary.sign(content);
        return getPrivacyGuard().cert2key(certificate);
    

    /**
     * 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息
     */
    @Override
    protected synchronized LicenseContent install(final byte[] key, final LicenseNotary notary) throws Exception 
        final GenericCertificate certificate = getPrivacyGuard().key2cert(key);
        notary.verify(certificate);
        final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
        this.validate(content);
        setLicenseKey(key);
        setCertificate(certificate);

        return content;
    

    /**
     * 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息
     */
    @Override
    protected synchronized LicenseContent verify(final LicenseNotary notary) throws Exception 

        // Load license key from preferences,
        final byte[] key = getLicenseKey();
        if (null == key) 
            throw new NoLicenseInstalledException(getLicenseParam().getSubject());
        

        GenericCertificate certificate = getPrivacyGuard().key2cert(key);
        notary.verify(certificate);
        final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
        this.validate(content);
        setCertificate(certificate);

        return content;
    

    /**
     * 校验生成证书的参数信息
     */
    protected synchronized void validateCreate(final LicenseContent content) throws LicenseContentException 
        final LicenseParam param = getLicenseParam();
        final Date now = new Date();
        final Date notBefore = content.getNotBefore();
        final Date notAfter = content.getNotAfter();
        if (null != notAfter && now.after(notAfter)) 
            throw new LicenseContentException("证书失效时间不能早于当前时间");
        
        if (null != notBefore && null != notAfter && notAfter.before(notBefore)) 
            throw new LicenseContentException("证书生效时间不能晚于证书失效时间");
        
        final String consumerType = content.getConsumerType();
        if (null == consumerType) 
            throw new LicenseContentException("用户类型不能为空");
        
    


    /**
     * 复写validate方法,用于增加我们额外的校验信息
     */
    @Override
    protected synchronized void validate(final LicenseContent content) throws LicenseContentException 
        //1. 首先调用父类的validate方法
        super.validate(content);
        //2. 然后校验自定义的License参数,去校验我们的license信息
        LicenseExtraModel expectedCheckModel = (LicenseExtraModel) content.getExtra();
        // 做我们自定义的校验
    


    /**
     * 重写XMLDecoder解析XML
     */
    private Object load(String encoded) 
        BufferedInputStream inputStream = null;
        XMLDecoder decoder = null;
        try 
            inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XMLConstants.XML_CHARSET)));
            decoder = new XMLDecoder(new BufferedInputStream(inputStream, XMLConstants.DEFAULT_BUFSIZE), null, null);
            return decoder.readObject();
         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
         finally 
            try 
                if (decoder != null) 
                    decoder.close();
                
                if (inputStream != null) 
                    inputStream.close();
                
             catch (Exception e) 
                logger.error("XMLDecoder解析XML失败", e);
            
        

        return null;
    


2.2 license生成

       生成证书需要的参数类。LicenseCreatorParam

/**
 * License生成类需要的参数
 */
public class LicenseCreatorParam implements Serializable 

    private static final long serialVersionUID = -7793154252684580872L;
    /**
     * 证书subject
     */
    private String subject;

    /**
     * 私钥别称
     */
    private String privateAlias;

    /**
     * 私钥密码(需要妥善保管,不能让使用者知道)
     */
    private String keyPass;

    /**
     * 访问私钥库的密码
     */
    private String storePass;

    /**
     * 证书生成路径
     */
    private String licensePath;

    /**
     * 私钥库存储路径
     */
    private String privateKeysStorePath;

    /**
     * 证书生效时间
     */
    private Date issuedTime = new Date();

    /**
     * 证书失效时间
     */
    private Date expiryTime;

    /**
     * 用户类型
     */
    private String consumerType = "user";

    /**
     * 用户数量
     */
    private Integer consumerAmount = 1;

    /**
     * 描述信息
     */
    private String description = "";

    /**
     * 额外的服务器硬件校验信息
     */
    private LicenseExtraModel licenseExtraModel;

    public String getSubject() 
        return subject;
    

    public void setSubject(String subject) 
        this.subject = subject;
    

    public String getPrivateAlias() 
        return privateAlias;
    

    public void setPrivateAlias(String privateAlias) 
        this.privateAlias = privateAlias;
    

    public String getKeyPass() 
        return keyPass;
    

    public void setKeyPass(String keyPass) 
        this.keyPass = keyPass;
    

    public String getStorePass() 
        return storePass;
    

    public void setStorePass(String storePass) 
        this.storePass = storePass;
    

    public String getLicensePath() 
        return licensePath;
    

    public void setLicensePath(String licensePath) 
        this.licensePath = licensePath;
    

    public String getPrivateKeysStorePath() 
        return privateKeysStorePath;
    

    public void setPrivateKeysStorePath(String privateKeysStorePath) 
        this.privateKeysStorePath = privateKeysStorePath;
    

    public Date getIssuedTime() 
        return issuedTime;
    

    public void setIssuedTime(Date issuedTime) 
        this.issuedTime = issuedTime;
    

    public Date getExpiryTime() 
        return expiryTime;
    

    public void setExpiryTime(Date expiryTime) 
        this.expiryTime = expiryTime;
    

    public String getConsumerType() 
        return consumerType;
    

    public void setConsumerType(String consumerType) 
        this.consumerType = consumerType;
    

    public Integer getConsumerAmount() 
        return consumerAmount;
    

    public void setConsumerAmount(Integer consumerAmount) 
        this.consumerAmount = consumerAmount;
    

    public String getDescription() 
        return description;
    

    public void setDescription(String description) 
        this.description = description;
    

    public LicenseExtraModel getLicenseExtraModel() 
        return licenseExtraModel;
    

    public void setLicenseExtraModel(LicenseExtraModel licenseExtraModel) 
        this.licenseExtraModel = licenseExtraModel;
    
    

       生成license的逻辑实现。

/**
 * License生成类 -- 用于license生成
 */
public class LicenseCreator 

    private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");

    private static Logger logger = LogManager.getLogger(LicenseCreator.class);

    private LicenseCreatorParam param;

    public LicenseCreator(LicenseCreatorParam param) 
        this.param = param;
    

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

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

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

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

        return new DefaultLicenseParam(param.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(param.getSubject());
        licenseContent.setIssued(param.getIssuedTime());
        licenseContent.setNotBefore(param.getIssuedTime());
        licenseContent.setNotAfter(param.getExpiryTime());
        licenseContent.setConsumerType(param.getConsumerType());
        licenseContent.setConsumerAmount(param.getConsumerAmount());
        licenseContent.setInfo(param.getDescription());

        //扩展校验,这里可以自定义一些额外的校验信息(也可以用json字符串保存)
        if (param.getLicenseExtraModel() != null) 
            licenseContent.setExtra(param.getLicenseExtraModel());
        

        return licenseContent;
    


       这里我们简单一点直接测试代码来生成证书。最后会在我们的指定目录下面生成一个license.lic文件。

    @Test
    public void licenseCreate() 
        // 生成license需要的一些参数
        LicenseCreatorParam param = new LicenseCreatorParam();
        param.setSubject("ioserver");
        param.setPrivateAlias("privateKey");
        param.setKeyPass("a123456");
        param.setStorePass("a123456");
        param.setLicensePath("D:\\\\licenseTest\\\\license.lic");
        param.setPrivateKeysStorePath("D:\\\\licenseTest\\\\privateKeys.keystore");
        Calendar issueCalendar = Calendar.getInstance();
        param.setIssuedTime(issueCalendar.getTime());
        Calendar expiryCalendar = Calendar.getInstance();
        expiryCalendar.set(2020, Calendar.DECEMBER, 31, 23, 59, 59);
        param.setExpiryTime(expiryCalendar.getTime());
        param.setConsumerType("user");
        param.setConsumerAmount(1);
        param.setDescription("测试");
        LicenseCreator licenseCreator = new LicenseCreator(param);
        // 生成license
        licenseCreator.generateLicense();
    

2.3 license验证

       license验证的具体实现。

/**
 * License校验类
 */
public class LicenseVerify 

    private static Logger logger = LogManager.getLogger(LicenseVerify.class);
    /**
     * 证书subject
     */
    private String subject;
    /**
     * 公钥别称
     */
    private String publicAlias;
    /**
     * 访问公钥库的密码
     */
    private String storePass;
    /**
     * 证书生成路径
     */
    private String licensePath;
    /**
     * 密钥库存储路径
     */
    private String publicKeysStorePath;
    /**
     * LicenseManager
     */
    private LicenseManager licenseManager;
    /**
     * 标识证书是否安装成功
     */
    private boolean installSuccess;

    public LicenseVerify(String subject, String publicAlias, String storePass, String licensePath, String publicKeysStorePath) 
        this.subject = subject;
        this.publicAlias = publicAlias;
        this.storePass = storePass;
        this.licensePath = licensePath;
        this.publicKeysStorePath = publicKeysStorePath;
    

    /**
     * 安装License证书,读取证书相关的信息, 在bean加入容器的时候自动调用
     */
    public void installLicense() 
        try 
            Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);

            CipherParam cipherParam = new DefaultCipherParam(storePass);

            KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class,
                    publicKeysStorePath,
                    publicAlias,
                    storePass,
                    null);
            LicenseParam licenseParam = new DefaultLicenseParam(subject, preferences, publicStoreParam, cipherParam);

            licenseManager = new CustomLicenseManager(licenseParam);
            licenseManager.uninstall();
            LicenseContent licenseContent = licenseManager.install(new File(licensePath));
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            installSuccess = true;
            logger.info("------------------------------- 证书安装成功 -------------------------------");
            logger.info(MessageFormat.format("证书有效期:0 - 1", format.format(licenseContent.getNotBefore()), format.format(licenseContent.getNotAfter())));
         catch (Exception e) 
            installSuccess = false;
            logger.error("------------------------------- 证书安装成功 -------------------------------");
            logger.error(e);
        
    

    /**
     * 卸载证书,在bean从容器移除的时候自动调用
     */
    public void unInstallLicense() 
        if (installSuccess) 
            try 
                licenseManager.uninstall();
             catch (Exception e) 
                // ignore
            
        
    

    /**
     * 校验License证书
     */
    public boolean verify() 
        try 
            LicenseContent licenseContent = licenseManager.verify();
            return true;
         catch (Exception e) 
            return false;
        
    


       把LicenseVerify类添加到Spring容器里面去。在LicenseVerify添加到spring容器的时候顺便调用LicenseVerify对象里面的installLicense()方法加载证书。

@Configuration
public class LicenseConfig 

    /**
     * 证书subject
     */
    @Value("$license.subject")
    private String subject;

    /**
     * 公钥别称
     */
    @Value("$license.publicAlias")
    private String publicAlias;

    /**
     * 访问公钥库的密码
     */
    @Value("$license.storePass")
    private String storePass;

    /**
     * 证书生成路径
     */
    @Value("$license.licensePath")
    private String licensePath;

    /**
     * 密钥库存储路径
     */
    @Value("$license.publicKeysStorePath")
    private String publicKeysStorePath;

    @Bean(initMethod = "installLicense", destroyMethod = "unInstallLicense")
    public LicenseVerify licenseVerify() 
        return new LicenseVerify(subject, publicAlias, storePass, licensePath, publicKeysStorePath);
    


application.yml文件需要的配置

#License相关配置
license:
  subject: ioserver #主题
  publicAlias: publicCert #公钥别称
  storePass: a123456 #访问公钥的密码
  licensePath: D:\\licenseTest\\license.lic #license位置
  publicKeysStorePath: D:\\licenseTest\\publicCerts.keystore #公钥位置

       license验证测试。

@SpringBootTest
@RunWith(SpringRunner.class)
public class LicenseTest 

    private LicenseVerify licenseVerify;

    @Autowired
    public void setLicenseVerify(LicenseVerify licenseVerify) 
        this.licenseVerify = licenseVerify;
    

    @Test
    public void licenseVerify() 
       System.out.println("licese是否有效:" + licenseVerify.verify());
    



       代码也不是很复杂,所以很多地方咱们都是直接把代码贴出来了。最后给出源码的下载地址https://github.com/tuacy/java-study。注意在license模块下面(重要的事情在说遍在相关代码license文件夹下面)。

以上是关于TrueLicense实现license验证的主要内容,如果未能解决你的问题,请参考以下文章

Springboot整合TrueLicense(包括License的生成安装和验证)

truelicense 将许可证文件保存在哪里

androidapp如何制作验证license

springboot实现Web系统License授权认证

springboot实现Web系统License授权认证

flutter doctor 验证环境licenses不通过