开发的软件产品在交付使用的时候,往往会授权一段时间的试用期,这个时候license就派上用场了。不同于在代码中直接加上时间约束,需要重新授权的时候使用license可以避免修改源码,改动部署,授权方直接生成一个新的license发送给使用方替换掉原来的license文件即可。下面将讲述使用truelicense来实现license的生成和使用。Truelicense是一个开源的证书管理引擎,详细介绍见https://truelicense.java.net/
一、首先介绍下license授权机制的原理:
1、 生成密钥对,方法有很多。
2、 授权者保留私钥,使用私钥对包含授权信息(如使用截止日期,MAC地址等)的license进行数字签名。
3、 公钥给使用者(放在验证的代码中使用),用于验证license是否符合使用条件。
接下来是本例制作license的具体步骤:
二、第一步:使用keytool生成密钥对
以下命令在dos命令行执行,注意当前执行目录,最后生成的密钥对即在该目录下:
1、首先要用KeyTool工具来生成私匙库:(-alias别名 –validity 3650表示10年有效)
keytool -genkey -alias privatekey -keystoreprivateKeys.store -validity 3650
2、然后把私匙库内的公匙导出到一个文件当中:
1.
keytool -export -alias privatekey -file certfile.cer -keystore privateKeys.store
3、然后再把这个证书文件导入到公匙库:
1.
keytool -
import
-alias publiccert -file certfile.cer -keystore publicCerts.store
最后生成文件privateKeys.store、publicCerts.store拷贝出来备用。
三、第二步:生成证书(该部分代码由授权者独立保管执行)
1、 首先LicenseManagerHolder.java类:
01.
package
cn.melina.license;
02.
import
de.schlichtherle.license.LicenseManager;
03.
import
de.schlichtherle.license.LicenseParam;
04.
05.
/**
06.
* LicenseManager??????
07.
* melina
08.
*/
09.
public
class
LicenseManagerHolder {
10.
11.
private
static
LicenseManager licenseManager;
12.
13.
public
static
synchronized
LicenseManager getLicenseManager(LicenseParam licenseParams) {
14.
if
(licenseManager ==
null
) {
15.
licenseManager =
new
LicenseManager(licenseParams);
16.
}
17.
return
licenseManager;
18.
}
19.
}
2、 然后是主要生成license的代码CreateLicense.java:
001.
package
cn.melina.license;
002.
003.
import
java.io.File;
004.
import
java.io.IOException;
005.
import
java.io.InputStream;
006.
import
java.text.DateFormat;
007.
import
java.text.ParseException;
008.
import
java.text.SimpleDateFormat;
009.
import
java.util.Properties;
010.
import
java.util.prefs.Preferences;
011.
import
javax.security.auth.x500.X500Principal;
012.
import
de.schlichtherle.license.CipherParam;
013.
import
de.schlichtherle.license.DefaultCipherParam;
014.
import
de.schlichtherle.license.DefaultKeyStoreParam;
015.
import
de.schlichtherle.license.DefaultLicenseParam;
016.
import
de.schlichtherle.license.KeyStoreParam;
017.
import
de.schlichtherle.license.LicenseContent;
018.
import
de.schlichtherle.license.LicenseParam;
019.
import
de.schlichtherle.license.LicenseManager;
020.
021.
/**
022.
* CreateLicense
023.
* melina
024.
*/
025.
public
class
CreateLicense {
026.
//common param
027.
private
static
String PRIVATEALIAS =
""
;
028.
private
static
String KEYPWD =
""
;
029.
private
static
String STOREPWD =
""
;
030.
private
static
String SUBJECT =
""
;
031.
private
static
String licPath =
""
;
032.
private
static
String priPath =
""
;
033.
//license content
034.
private
static
String issuedTime =
""
;
035.
private
static
String notBefore =
""
;
036.
private
static
String notAfter =
""
;
037.
private
static
String consumerType =
""
;
038.
private
static
int
consumerAmount =
0
;
039.
private
static
String info =
""
;
040.
// 为了方便直接用的API里的例子
041.
// X500Princal是一个证书文件的固有格式,详见API
042.
private
final
static
X500Principal DEFAULTHOLDERANDISSUER =
new
X500Principal(
043.
"CN=Duke、OU=JavaSoft、O=Sun Microsystems、C=US"
);
044.
045.
public
void
setParam(String propertiesPath) {
046.
// 获取参数
047.
Properties prop =
new
Properties();
048.
InputStream in = getClass().getResourceAsStream(propertiesPath);
049.
try
{
050.
prop.load(in);
051.
}
catch
(IOException e) {
052.
// TODO Auto-generated catch block
053.
e.printStackTrace();
054.
}
055.
PRIVATEALIAS = prop.getProperty(
"PRIVATEALIAS"
);
056.
KEYPWD = prop.getProperty(
"KEYPWD"
);
057.
STOREPWD = prop.getProperty(
"STOREPWD"
);
058.
SUBJECT = prop.getProperty(
"SUBJECT"
);
059.
KEYPWD = prop.getProperty(
"KEYPWD"
);
060.
licPath = prop.getProperty(
"licPath"
);
061.
priPath = prop.getProperty(
"priPath"
);
062.
//license content
063.
issuedTime = prop.getProperty(
"issuedTime"
);
064.
notBefore = prop.getProperty(
"notBefore"
);
065.
notAfter = prop.getProperty(
"notAfter"
);
066.
consumerType = prop.getProperty(
"consumerType"
);
067.
consumerAmount = Integer.valueOf(prop.getProperty(
"consumerAmount"
));
068.
info = prop.getProperty(
"info"
);
069.
070.
}
071.
072.
public
boolean
create() {
073.
try
{
074.
/************** 证书发布者端执行 ******************/
075.
LicenseManager licenseManager = LicenseManagerHolder
076.
.getLicenseManager(initLicenseParams0());
077.
licenseManager.store((createLicenseContent()),
new
File(licPath));
078.
}
catch
(Exception e) {
079.
e.printStackTrace();
080.
System.out.println(
"客户端证书生成失败!"
);
081.
return
false
;
082.
}
083.
System.out.println(
"服务器端生成证书成功!"
);
084.
return
true
;
085.
}
086.
087.
// 返回生成证书时需要的参数
088.
private
static
LicenseParam initLicenseParams0() {
089.
Preferences preference = Preferences
090.
.userNodeForPackage(CreateLicense.
class
);
091.
// 设置对证书内容加密的对称密码
092.
CipherParam cipherParam =
new
DefaultCipherParam(STOREPWD);
093.
// 参数1,2从哪个Class.getResource()获得密钥库;参数3密钥库的别名;参数4密钥库存储密码;参数5密钥库密码
094.
KeyStoreParam privateStoreParam =
new
DefaultKeyStoreParam(
095.
CreateLicense.
class
, priPath, PRIVATEALIAS, STOREPWD, KEYPWD);
096.
LicenseParam licenseParams =
new
DefaultLicenseParam(SUBJECT,
097.
preference, privateStoreParam, cipherParam);
098.
return
licenseParams;
099.
}
100.
101.
// 从外部表单拿到证书的内容
102.
public
final
static
LicenseContent createLicenseContent() {
103.
DateFormat format =
new
SimpleDateFormat(
"yyyy-MM-dd"
);
104.
LicenseContent content =
null
;
105.
content =
new
LicenseContent();
106.
content.setSubject(SUBJECT);
107.
content.setHolder(DEFAULTHOLDERANDISSUER);
108.
content.setIssuer(DEFAULTHOLDERANDISSUER);
109.
try
{
110.
content.setIssued(format.parse(issuedTime));
111.
content.setNotBefore(format.parse(notBefore));
112.
content.setNotAfter(format.parse(notAfter));
113.
}
catch
(ParseException e) {
114.
// TODO Auto-generated catch block
115.
e.printStackTrace();
116.
}
117.
content.setConsumerType(consumerType);
118.
content.setConsumerAmount(consumerAmount);
119.
content.setInfo(info);
120.
// 扩展
121.
content.setExtra(
new
Object());
122.
return
content;
123.
}
124.
}
3、 测试程序licenseCreateTest.java:
01.
package
cn.melina.license;
02.
import
cn.melina.license.CreateLicense;
03.
public
class
licenseCreateTest {
04.
public
static
void
main(String[] args){
05.
CreateLicense cLicense =
new
CreateLicense();
06.
//获取参数
07.
cLicense.setParam(
"./param.properties"
);
08.
//生成证书
09.
cLicense.create();
10.
}
11.
}
4、 生成时使用到的param.properties文件如下:
01.
##########common parameters###########
02.
#alias
03.
PRIVATEALIAS=privatekey
04.
#key(该密码生成密钥对的密码,需要妥善保管,不能让使用者知道)
05.
KEYPWD=bigdata123456
06.
#STOREPWD(该密码是在使用keytool生成密钥对时设置的密钥库的访问密码)
07.
STOREPWD=abc123456
08.
#SUBJECT
09.
SUBJECT=bigdata
10.
#licPath
11.
licPath=bigdata.lic
12.
#priPath
13.
priPath=privateKeys.store
14.
##########license content###########
15.
#issuedTime
16.
issuedTime=
2014
-
04
-
01
17.
#notBeforeTime
18.
notBefore=
2014
-
04
-
01
19.
#notAfterTime
20.
notAfter=
2014
-
05
-
01
21.
#consumerType
22.
consumerType=user
23.
#ConsumerAmount
24.
consumerAmount=
1
25.
#info
26.
info=
this
is a license
根据properties文件可以看出,这里只简单设置了使用时间的限制,当然可以自定义添加更多限制。该文件中表示授权者拥有私钥,并且知道生成密钥对的密码。并且设置license的内容。
四、第三步:验证证书(使用证书)(该部分代码结合需要授权的程序使用)
1、 首先LicenseManagerHolder.java类,同上。
2、 然后是主要验证license的代码VerifyLicense.java:
01.
package
cn.melina.license;
02.
03.
import
java.io.File;
04.
import
java.io.IOException;
05.
import
java.io.InputStream;
06.
import
java.util.Properties;
07.
import
java.util.prefs.Preferences;
08.
09.
import
de.schlichtherle.license.CipherParam;
10.
import
de.schlichtherle.license.DefaultCipherParam;
11.
import
de.schlichtherle.license.DefaultKeyStoreParam;
12.
import
de.schlichtherle.license.DefaultLicenseParam;
13.
import
de.schlichtherle.license.KeyStoreParam;
14.
import
de.schlichtherle.license.LicenseParam;
15.
import
de.schlichtherle.license.LicenseManager;
16.
17.
/**
18.
* VerifyLicense
19.
* melina
20.
*/
21.
public
class
VerifyLicense {
22.
//common param
23.
private
static
String PUBLICALIAS =
""
;
24.
private
static
String STOREPWD =
""
;
25.
private
static
String SUBJECT =
""
;
26.
private
static
String licPath =
""
;
27.
private
static
String pubPath =
""
;
28.
29.
public
void
setParam(String propertiesPath) {
30.
// 获取参数
31.
Properties prop =
new
Properties();
32.
InputStream in = getClass().getResourceAsStream(propertiesPath);
33.
try
{
34.
prop.load(in);
35.
}
catch
(IOException e) {
36.
// TODO Auto-generated catch block
37.
e.printStackTrace();
38.
}
39.
PUBLICALIAS = prop.getProperty(
"PUBLICALIAS"
);
40.
STOREPWD = prop.getProperty(
"STOREPWD"
);
41.
SUBJECT = prop.getProperty(
"SUBJECT"
);
42.
licPath = prop.getProperty(
"licPath"
);
43.
pubPath = prop.getProperty(
"pubPath"
);
44.
}
45.
46.
public
boolean
verify() {
47.
/************** 证书使用者端执行 ******************/
48.
49.
LicenseManager licenseManager = LicenseManagerHolder
50.
.getLicenseManager(initLicenseParams());
51.
// 安装证书
52.
try
{
53.
licenseManager.install(
new
File(licPath));
54.
System.out.println(
"客户端安装证书成功!"
);
55.
}
catch
(Exception e) {
56.
e.printStackTrace();
57.
System.out.println(
"客户端证书安装失败!"
);
58.
return
false
;
59.
}
60.
// 验证证书
61.
try
{
62.
licenseManager.verify();
63.
System.out.println(
"客户端验证证书成功!"
);
64.
}
catch
(Exception e) {
65.
e.printStackTrace();
66.
System.out.println(
"客户端证书验证失效!"
);
67.
return
false
;
68.
}
69.
return
true
;
70.
}
71.
72.
// 返回验证证书需要的参数
73.
private
static
LicenseParam initLicenseParams() {
74.
Preferences preference = Preferences
75.
.userNodeForPackage(VerifyLicense.
class
);
76.
CipherParam cipherParam =
new
DefaultCipherParam(STOREPWD);
77.
78.
KeyStoreParam privateStoreParam =
new
DefaultKeyStoreParam(
79.
VerifyLicense.
class
, pubPath, PUBLICALIAS, STOREPWD,
null
);
80.
LicenseParam licenseParams =
new
DefaultLicenseParam(SUBJECT,
81.
preference, privateStoreParam, cipherParam);
82.
return
licenseParams;
83.
}
84.
}
3、 测试程序licenseVerifyTest.java:
01.
package
cn.melina.license;
02.
03.
public
class
licenseVerifyTest {
04.
public
static
void
main(String[] args){
05.
VerifyLicense vLicense =
new
VerifyLicense();
06.
//获取参数
07.
vLicense.setParam(
"./param.properties"
);
08.
//验证证书
09.
vLicense.verify();
10.
}
11.
}
4、 验证时使用到的Properties文件如下:
01.
##########common parameters###########
02.
#alias
03.
PUBLICALIAS=publiccert
04.
#STOREPWD(该密码是在使用keytool生成密钥对时设置的密钥库的访问密码)
05.
STOREPWD=abc123456
06.
#SUBJECT
07.
SUBJECT=bigdata
08.
#licPath
09.
licPath=bigdata.lic
10.
#pubPath
11.
pubPath=publicCerts.store
根据该验证的properties可以看出,使用者只拥有公钥,没有私钥,并且也只知道访问密钥库的密码,而不能知道生成密钥对的密码。
五、说明:
注意实际操作中,公钥、私钥、证书等文件的存放路径。
以上代码需要用到truelicense的一些包,可以自行网上搜,也可以下载我的完整工程,里面附带了所需的jar包。