牛骨文教育服务平台(让学习变的简单)
博文笔记

javaEE防盗版-License开发

创建时间:2017-02-13 投稿人: 浏览次数:4771
开发的软件产品在交付使用的时候,往往会授权一段时间的试用期,这个时候license就派上用场了。不同于在代码中直接加上时间约束,需要重新授权的时候使用license可以避免修改源码,改动部署,授权方直接生成一个新的license发送给使用方替换掉原来的license文件即可。下面将讲述使用truelicense来实现license的生成和使用。Truelicense是一个开源的证书管理引擎,详细介绍见https://truelicense.java.net/ license于加密技术一起使用效果更好。 接下来介绍一下license授权机制的原理:
1、生成密钥对,方法有很多。
2、授权者保留私钥,使用私钥对包含授权信息(如使用截止日期,MAC地址等)的license进行数字签名。
3、公钥给使用者(放在验证的代码中使用),用于验证license是否符合使用条件。

1. 生成密钥对

以下命令在dos命令行执行,注意当前执行目录,最后生成的密钥对即在该目录下:
1、首先要用KeyTool工具来生成私匙库:(-alias别名 –validity 3650表示10年有效)
keytool -genkey -alias privatekey -keystore privateKeys.store -validity 3650
 
这里口令使用了noryar123
 
这里的口令是:noryar456
这个时候,会在打开命令行的地方创建出一个文件,privateKeys.store


2、然后把私匙库内的证书导出到一个文件当中:
keytool -export -alias privatekey -file certfile.cer -keystore privateKeys.store
 
口令:noryar123


3、然后再把这个证书文件导入到公匙库:
keytool -import -alias publiccert -file certfile.cer -keystore publicCerts.store
 
这里的口令,用了:noryar123


最后生成文件privateKeys.store、publicCerts.store拷贝出来备用。
文件位置在用户目录下
C:UsersAdministrator
从上面我们可以看到,密钥一共有两种:1. 密钥库,这个需要配置到服务器中。2. 密钥,这个需要保护好,是创建私钥的时候用的

2. Maven

<!-- https://mvnrepository.com/artifact/de.schlichtherle.truelicense/truelicense-core -->
<dependency>
    <groupId>de.schlichtherle.truelicense</groupId>
    <artifactId>truelicense-core</artifactId>
    <version>1.33</version>
</dependency>
<!-- https://mvnrepository.com/artifact/de.schlichtherle.truelicense/truelicense-xml -->
<dependency>
    <groupId>de.schlichtherle.truelicense</groupId>
    <artifactId>truelicense-xml</artifactId>
    <version>1.33</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/de.schlichtherle.truelicense/truelicense-swing -->
<dependency>
    <groupId>de.schlichtherle.truelicense</groupId>
    <artifactId>truelicense-swing</artifactId>
    <version>1.33</version>
</dependency>

3. 生成证书 (该部分代码由授权者独立保管执行)

package com.noryar.license;
import de.schlichtherle.license.LicenseManager;
import de.schlichtherle.license.LicenseParam;
/**
 * 单例模式下的证书管理器
 * @author Leon Lee.
 */
public class LicenseManagerHolder
{
	private static LicenseManager licenseManager;
	private LicenseManagerHolder(){}
	public static synchronized  LicenseManager getLicenseManager(LicenseParam param)
	{
		if(licenseManager==null)
		{
			licenseManager=new LicenseManager(param);
		}
		return licenseManager;
	}
}

package com.noryar.license;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Properties;
import java.util.prefs.Preferences;

import javax.security.auth.x500.X500Principal;

import de.schlichtherle.license.CipherParam;
import de.schlichtherle.license.DefaultCipherParam;
import de.schlichtherle.license.DefaultKeyStoreParam;
import de.schlichtherle.license.DefaultLicenseParam;
import de.schlichtherle.license.KeyStoreParam;
import de.schlichtherle.license.LicenseContent;
import de.schlichtherle.license.LicenseManager;
import de.schlichtherle.license.LicenseParam;

/**
 * 生成证书
 * @author Leon Lee.
 */
public class LicenseMake
{
	private String licPath;
	private String issued ;
	private String notBefore ;
	private String notAfter ;
	private String consumerType;
	private int consumerAmount ;
	private String info ;
	/**
	 * 私钥的别名
	 */
	private String priAlias ;
	/**
	 * 该密码生成密钥对的密码
	 */
	private String privateKeyPwd ;
	/**
	 * 使用keytool生成密钥对时设置的密钥库的访问密码
	 */
	private String keyStorePwd ;
	private String subject ;
	private String priPath ;
	
	// X500Princal是一个证书文件的固有格式,详见API
	private final static X500Principal DEFAULTHOLDERANDISSUER = new X500Principal("CN=noryar, OU=noryar, O=noryar, L=china, ST=dalian, C=china");
	
	public LicenseMake(){}
	public LicenseMake(String confPath)
	{
		initParam(confPath);
	}
	
	/**
	 * 读取属性文件
	 * @param propertiesPath 属性文件路径
	 */
	public void initParam(String confPath) 
	{
		// 获取参数
		Properties prop = new Properties();
		InputStream in = getClass().getResourceAsStream(confPath);
		try
		{
			prop.load(in);
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		//common param
		priAlias = prop.getProperty("private.key.alias");
		privateKeyPwd = prop.getProperty("private.key.pwd");
		keyStorePwd= prop.getProperty("key.store.pwd");
		subject = prop.getProperty("subject");
		priPath = prop.getProperty("priPath");
		licPath = prop.getProperty("licPath");
		 // license content
		issued = prop.getProperty("issuedTime");
		notBefore = prop.getProperty("notBefore");
		notAfter = prop.getProperty("notAfter");
		consumerType = prop.getProperty("consumerType");
		consumerAmount = Integer.valueOf(prop.getProperty("consumerAmount"));
		info = prop.getProperty("info");
	}
	
	/**
	 * 初始化证书的相关参数
	 * @return
	 */
	private LicenseParam initLicenseParams()
	{
		Class<LicenseMake> clazz=LicenseMake.class;
		Preferences pre = Preferences.userNodeForPackage(clazz);
		// 设置对证书内容加密的对称密码
		CipherParam cipherParam = new DefaultCipherParam(keyStorePwd);
		// 参数1,2从哪个Class.getResource()获得密钥库;
		//参数3密钥库的别名;
		//参数4密钥库存储密码;
		//参数5密钥库密码
		KeyStoreParam privateStoreParam = new DefaultKeyStoreParam(clazz, priPath, priAlias, keyStorePwd, privateKeyPwd);
		// 返回生成证书时需要的参数
		LicenseParam licenseParam = new DefaultLicenseParam(subject,pre, privateStoreParam, cipherParam);
		return licenseParam;
	}
	
	/**
	 * 通过外部配置文件构建证书的的相关信息
	 * @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(DEFAULTHOLDERANDISSUER);
		content.setIssuer(DEFAULTHOLDERANDISSUER);
		content.setIssued(formate.parse(issued));
		content.setNotBefore(formate.parse(notBefore));
		content.setNotAfter(formate.parse(notAfter));
		content.setInfo(info);
		content.setExtra(new Object());
		return content;
	}
	
	
	/**
	 * 生成证书,在证书发布者端执行
	 * @throws Exception 
	 */
	public void create() throws Exception
	{
		LicenseManager licenseManager=LicenseManagerHolder.getLicenseManager(initLicenseParams());
		LicenseContent content=buildLicenseContent();
		licenseManager.store(content, new File(licPath));
		System.out.println("证书发布成功");
	}
}

licenseMakeConf.properties

##########common parameters###########
#私钥的别名
private.key.alias=privatekey
#privateKeyPwd(该密码生成密钥对的密码,需要妥善保管,不能让使用者知道)
private.key.pwd=noryar456
#keyStorePwd(该密码是在使用keytool生成密钥对时设置的密钥库的访问密码)
key.store.pwd=noryar123
#项目的唯一识别码
subject=noryar
#生成证书的地址
licPath=license.lic
#密钥库的地址
priPath=privateKeys.store
##########license content###########
#发布日期
issuedTime=2016-07-25
#有效开始日期
notBefore=2016-07-25
#有效截止日期
notAfter=2016-08-30
#consumerType
consumerType=user
#ConsumerAmount
consumerAmount=1
#info
info=this is a license

测试方法

public static void main(String[] args) throws Exception
	{
		LicenseMake clicense=new LicenseMake("/licenseMakeConf.properties");
		clicense.create();
	}
以上就可以创建出一个license文件了。然后开发者将license文件传递给购买方。购买方在系统中上传该文件进行验证即可。


4. 验证证书

package com.noryar.license;

import de.schlichtherle.license.LicenseManager;
import de.schlichtherle.license.LicenseParam;
/**
 * 单例模式下的证书管理器
 * @author Leon Lee
 */
public class LicenseManagerHolder
{
	private static LicenseManager licenseManager;
	private LicenseManagerHolder(){}
	public static synchronized  LicenseManager getLicenseManager(LicenseParam param)
	{
		if(licenseManager==null)
		{
			licenseManager=new LicenseManager(param);
		}
		return licenseManager;
	}
}

认证核心方法

package com.noryar.license;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.prefs.Preferences;

import de.schlichtherle.license.CipherParam;
import de.schlichtherle.license.DefaultCipherParam;
import de.schlichtherle.license.DefaultKeyStoreParam;
import de.schlichtherle.license.DefaultLicenseParam;
import de.schlichtherle.license.KeyStoreParam;
import de.schlichtherle.license.LicenseContentException;
import de.schlichtherle.license.LicenseManager;
import de.schlichtherle.license.LicenseParam;

public class LicenseVertify
{
	/**
	 * 公钥别名
	 */
	private String pubAlias;
	/**
	 * 该密码是在使用keytool生成密钥对时设置的密钥库的访问密码 
	 */
	private String keyStorePwd;
	/**
	 * 系统的统一识别码
	 */
	private String onlykey;
	/**
	 * 证书路径 
	 */
	private String licName;
	/**
	 * 公钥库路径
	 */
	private String pubPath;
	private String confPath="/licenseVertifyConf.properties";
	public LicenseVertify(String onlykey)
	{
		setConf(confPath,onlykey);
	}
	
	public LicenseVertify(String confPath,String onlykey)
	{
		setConf(confPath,onlykey);
	}
	/**
	 * 通过外部配置文件获取配置信息
	 * @param confPath  配置文件路径
	 * @param onlykey 系统的统一识别码
	 */
	public void setConf(String confPath,String onlykey)
	{
		// 获取参数
		Properties prop = new Properties();
		InputStream in = getClass().getResourceAsStream(confPath);
		try
		{
			prop.load(in);
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
		this.onlykey=onlykey;
		pubAlias = prop.getProperty("public.alias");
		keyStorePwd = prop.getProperty("key.store.pwd");
		licName = prop.getProperty("license.name");
		pubPath = prop.getProperty("public.store.path");
		
	}
	
	/**
	 * 初始化证书的相关参数
	 * @param 系统的统一识别码
	 * @return
	 */
	private LicenseParam initLicenseParams()
	{
		Class<LicenseVertify> clazz=LicenseVertify.class;
		Preferences pre=Preferences.userNodeForPackage(clazz);
		CipherParam cipherParam=new DefaultCipherParam(storePwd);
		KeyStoreParam pubStoreParam=new DefaultKeyStoreParam(clazz, pubPath, pubAlias, keyStorePwd, null);
		LicenseParam licenseParam=new DefaultLicenseParam(onlykey, pre, pubStoreParam, cipherParam);
		return licenseParam;
	}
	
	private LicenseManager getLicenseManager()
	{
		return LicenseManagerHolder.getLicenseManager(initLicenseParams());
	}
	/**
	 * 安装证书证书
	 * @param 存放证书的路径
	 * @return
	 */
	public void install(String licdir)
	{
		try
		{
			LicenseManager licenseManager=getLicenseManager();
			licenseManager.install(new File(licdir+File.separator+licName));
			System.out.println("安装证书成功!");
		}
		catch (Exception e)
		{
			System.out.println("安装证书失败!");
			e.printStackTrace();
			System.exit(0);
		}
		
	}
	
	/**
	 * 验证证书的合法性
	 * @return 0、合法,1、证书过期,2、证书错误
	 */
	public int vertify()
	{
		try
		{
			LicenseManager licenseManager=getLicenseManager();
			licenseManager.verify();
			System.out.println("验证证书成功!");
			return 0;
		}
		catch(LicenseContentException ex)
		{
			System.out.println("证书已经过期!");
			ex.printStackTrace();
			return 1;
		}
		catch (Exception e)
		{
			System.out.println("验证证书失败!");
			e.printStackTrace();
			return 2;
		}
	}
}

测试

public static void main(String[] args) throws Exception
	{
		LicenseVertify vlicense=new LicenseVertify("noryar"); // 项目唯一识别码,对应生成配置文件的subject
		vlicense.install(System.getProperty("user.dir"));
		vlicense.vertify();
	}
licenseVertifyConf.properties

##########common parameters###########
#公钥别名
public.alias=publiccert
#使用keytool生成密钥对时设置的密钥库的访问密码
key.store.pwd= noryar123
#证书路径
license.name=license.lic
#公共库路径
public.store.path=publicCerts.store

以上就是license开发的常规手段。对于web项目来说,只需要提供license认证模块的开发即可。对于开发者来说,可以将license生成单独开发一个小程序。以后就可以通过这个程序来给不同的企业生成license。

此外,为了更好的保护代码。license认证模块可以进行混淆加密,解密部分使用C语言来编写,同时针对不同的平台进行编译。增强破解难度。

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。