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.
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_password1234Place 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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.