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