Information Security 15 min read

Generating and Verifying Java License Files with TrueLicense

This article explains how to generate a Java license using TrueLicense, configure key pairs, create the license file, and integrate verification and installation steps into a Spring Boot application, including detailed code examples and deployment considerations for secure software licensing.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Generating and Verifying Java License Files with TrueLicense

1. License Overview

A license is a copyright certificate used to grant paid users access to software, especially when the application is deployed in a customer's internal network where the developer cannot control the environment.

Application Scenarios

Deployed in a customer's intranet.

Developer cannot control network or guarantee external access.

Use a server license file and load the certificate at startup.

Validate the certificate during login or critical operations.

License Authorization Principle

Uses the open‑source certificate management engine TrueLicense :

Generate a key pair with keytool to create a public‑private keystore.

The licensor keeps the private key and creates a license using the private key and validity period.

The public key is embedded in the client code to verify the license’s validity.

2. Generating the Key Pair

Important parameters: storepass (private keystore password) and keypass (private key password). Other parameters can use defaults; set validity to ten years.

## 1. 生成私匙库
# validity:私钥的有效期(单位:天)
# alias:私钥别称
# keystore: 私钥库文件名称(生成在当前目录)
# storepass:私钥库的密码(获取keystore信息所需的密码)
# keypass:私钥的密码
# dname 证书个人信息
#  CN 为你的姓名
# OU 为你的组织单位名称
# O 为你的组织名称
# L 为你所在的城市名称
# ST 为你所在的省份名称
# C 为你的国家名称 或 区号
keytool -genkeypair -keysize 1024 -validity 3650 -alias "privateKey" -keystore "privateKeys.keystore" -storepass "public_password1234" -keypass "private_password1234" -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN"

## 2. 把私匙库内的公匙导出到一个文件当中
keytool -exportcert -alias "privateKey" -keystore "privateKeys.keystore" -storepass "public_password1234" -file "certfile.cer"

## 3. 再把这个证书文件导入到公匙库
keytool -import -alias "publicCert" -file "certfile.cer" -keystore "publicCerts.keystore" -storepass "public_password1234"

After execution three files are generated:

certfile.cer – temporary certificate file (can be deleted).

privateKeys.keystore – private keystore; keep it for future license generation.

publicKeys.keystore – public keystore; distribute it together with license.lic to the client project.

3. Creating the license.lic File

Add the TrueLicense dependency to pom.xml :

<dependency>
    <groupId>de.schlichtherle.truelicense</groupId>
    <artifactId>truelicense-core</artifactId>
    <version>1.33</version>
</dependency>

License Generation Class

import de.schlichtherle.license.*;
import lombok.extern.slf4j.Slf4j;
import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.prefs.Preferences;

@Slf4j
public class LicenseCreator {
    private static final X500Principal DEFAULT_HOLDER_AND_ISSUER =
        new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");

    /**
     * @description main method to generate license file
     */
    public static void main(String[] args) throws IOException {
        LicenseCreatorParam param = new LicenseCreatorParam();
        param.setSubject("license");
        param.setPrivateAlias("privateKey");
        param.setKeyPass("private_password1234");
        param.setStorePass("public_password1234");
        param.setLicensePath("/Users/xuchang/Documents/license/license.lic");
        param.setPrivateKeysStorePath("/Users/xuchang/Documents/license/privateKeys.keystore");
        param.setIssuedTime(new Date());
        LocalDateTime localDateTime = LocalDateTime.of(2024,12,31,23,59,59);
        Date expiry = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
        param.setExpiryTime(expiry);
        param.setConsumerType("user");
        param.setConsumerAmount(1);
        param.setDescription("证书描述信息");
        LicenseCreator creator = new LicenseCreator();
        creator.generateLicense(param);
    }

    public void generateLicense(LicenseCreatorParam param) {
        try {
            LicenseManager manager = new LicenseManager(initLicenseParam(param));
            LicenseContent content = initLicenseContent(param);
            manager.store(content, new File(param.getLicensePath()));
        } catch (Exception e) {
            log.error("证书生成失败", e);
        }
    }

    private static LicenseParam initLicenseParam(LicenseCreatorParam param) {
        Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);
        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
        KeyStoreParam privateStore = new CustomKeyStoreParam(
            LicenseCreator.class,
            param.getPrivateKeysStorePath(),
            param.getPrivateAlias(),
            param.getStorePass(),
            param.getKeyPass()
        );
        return new DefaultLicenseParam(param.getSubject(), preferences, privateStore, cipherParam);
    }

    private static LicenseContent initLicenseContent(LicenseCreatorParam param) {
        LicenseContent content = new LicenseContent();
        content.setHolder(DEFAULT_HOLDER_AND_ISSUER);
        content.setIssuer(DEFAULT_HOLDER_AND_ISSUER);
        content.setSubject(param.getSubject());
        content.setIssued(param.getIssuedTime());
        content.setNotBefore(param.getIssuedTime());
        content.setNotAfter(param.getExpiryTime());
        content.setConsumerType(param.getConsumerType());
        content.setConsumerAmount(param.getConsumerAmount());
        content.setInfo(param.getDescription());
        return content;
    }
}

Parameter class LicenseCreatorParam holds all configuration fields (subject, alias, passwords, paths, dates, consumer info, description).

License Verification Class

@Slf4j
public class LicenseVerify {
    public synchronized LicenseContent install(LicenseVerifyParam param) {
        LicenseContent result = null;
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            LicenseManager manager = LicenseManagerHolder.getInstance(initLicenseParam(param));
            manager.uninstall();
            result = manager.install(new File(param.getLicensePath()));
            log.info(MessageFormat.format("证书安装成功,证书有效期:{0} - {1}",
                format.format(result.getNotBefore()), format.format(result.getNotAfter())));
        } catch (Exception e) {
            log.error("证书安装失败: {}", e.getMessage());
        }
        return result;
    }

    public boolean verify() {
        LicenseManager manager = LicenseManagerHolder.getInstance(null);
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            LicenseContent content = manager.verify();
            log.info(MessageFormat.format("证书校验通过,证书有效期:{0} - {1}",
                format.format(content.getNotBefore()), format.format(content.getNotAfter())));
            return true;
        } catch (Exception e) {
            log.error("证书校验失败: {}", e.getMessage());
            return false;
        }
    }

    private LicenseParam initLicenseParam(LicenseVerifyParam param) {
        Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);
        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
        KeyStoreParam publicStore = new CustomKeyStoreParam(
            LicenseVerify.class,
            param.getPublicKeysStorePath(),
            param.getPublicAlias(),
            param.getStorePass(),
            null
        );
        return new DefaultLicenseParam(param.getSubject(), preferences, publicStore, cipherParam);
    }
}

Parameter classes LicenseVerifyParam and LicenseCreatorParam are simple POJOs annotated with Lombok @Data to hold configuration values.

4. Client‑Side Configuration

Add the same TrueLicense dependency to the client pom.xml . Create a Spring Boot runner to install the license at startup:

@Slf4j
@Component
public class LicenseCheckRunner implements ApplicationRunner {
    @Value("${license.subject}") private String subject;
    @Value("${license.publicAlias}") private String publicAlias;
    @Value("${license.storePass}") private String storePass;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("++++++++ 开始安装证书 ++++++++" );
        LicenseVerifyParam param = new LicenseVerifyParam();
        param.setSubject(subject);
        param.setPublicAlias(publicAlias);
        param.setStorePass(storePass);
        String resourcePath = getClass().getClassLoader().getResource("").getPath();
        param.setLicensePath(resourcePath + "license.lic");
        param.setPublicKeysStorePath(resourcePath + "publicCerts.keystore");
        new LicenseVerify().install(param);
        log.info("++++++++ 证书安装结束 ++++++++" );
    }
}

Define properties in application.properties :

#License configuration
license.subject=license
license.publicAlias=publicCert
license.storePass=public_password1234

Place license.lic and publicCerts.keystore under resources . Optionally share them via a common service for multiple micro‑services.

Interceptor for Request Validation

@Slf4j
public class LicenseCheckInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean ok = new LicenseVerify().verify();
        if (ok) return true;
        response.setCharacterEncoding("utf-8");
        Map
result = new HashMap<>(1);
        result.put("result","您的证书无效,请核查服务器是否取得授权或重新申请证书!");
        response.getWriter().write(JSON.toJSONString(result));
        return false;
    }
}

Register the interceptor in a WebMvcConfigurer implementation.

The article also includes screenshots showing successful and failed license checks, and a brief promotional note for a backend technical community.

JavasecuritySpringBootLicenseCodeExampleKeyStoreTrueLicense
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.