赞
踩
关于License的概念、用途和好坏处,网上一搜一大堆的,但我讲的这个License,并非是自娱自乐,而是教大家如何做一个符合商用的license。
可以定义一个或者多个唯一的属性,用来标识License使用方,说白了就是标识谁使用了你的平台、软件、模块或者库,这个标识可以是machine也可以是people,一经生成,便不能修改和删除。比如计算机Mac地址、主板序列号和CPU序列号,或者是软件安装序列号、用户UUID,根据这些number,可以确保授权对象唯一,不可复制。
有了上面的保护之后,那就可以赚钱了。比如你可以区分业务类型,其实就是使用范围,不同业务不同套餐费用,或者是实例创建数量和API调用次数,这些都可以是计费标准,还有最常见的就是时间限制,超过有效时间期限,则无法继续使用。
君子爱财取之有道。是时候切入正题了,在写代码之前,需要制作一对秘钥,私钥对授权内容进行签名,公钥给授权方校验License文件是否正确有效。
在这里,我用的是JDK自带keytool工具制作的。方法如下:
前提:使用CMD命令进入JDK的bin目录下:
## 1. 生成私匙库
# validity:私钥的有效期多少天
# alias:私钥别称
#keyalg:指定加密算法,默认是DSA
# keystore: 指定私钥库文件的名称(生成在当前目录)
# storepass:指定私钥库的密码(获取keystore信息所需的密码)
# keypass:指定别名条目的密码(私钥的密码)
keytool -genkeypair -keysize 1024 -validity 730 -alias "privateKey" -keyalg "RSA" -keystore "D:\KeyStore\privateKey.keystore" -storepass "654321" -keypass "123456" -dname "CN=mine, OU=test, O=test, L=gz, ST=gd, C=CN"
## 2. 生成证书
# alias:私钥别称
# keystore:指定私钥库的名称(在当前目录查找)
# storepass: 指定私钥库的密码
# file:证书名称
keytool -exportcert -alias "privateKey" -keystore "D:\KeyStore\privateKey.keystore" -storepass "654321" -file "D:\KeyStore\publicCer.cer"
到这里,已经创建好了两个文件了。对于License文件的制作和验证,这两个文件是够用的,我在这里顺便生成公匙库,后面验证签名时,可以选择证书或者公钥进行验证。
## 3. 生成公匙库
# alias:公钥别称
# file:证书名称
# keystore:公钥文件名称
# storepass:指定私钥库的密码
keytool -import -alias "publicKey" -file "D:\KeyStore\publicCer.cer" -keystore "D:\KeyStore\publicKey.keystore" -storepass "123456"
运行完之后截图:
生成文件截图:
<?xml version="1.0" encoding="UTF-8"?>
<license vendor="Steven Jon" expiration="2021-04-29" hostId="Win-C498-97D7-262D-55DD" generated="2020-04-29">
<feature name="INFramework" expiration="2021-04-29"/>
<signature>MC0CFHyTYkh59tPA50tylcHk6gVX0KtfAhUAhLnBwN1S1Pi8A1js0BG7cnFzkRE=</signature>
</license>
vendor--------------------------------------------license供应商
expiration----------------------------------------license有效期
hostId---------------------------------------------软件安装序列号,以此为标识
generated----------------------------------------license生成时间
在license下面定义需要使用的功能名称
feature name------------------------------------功能名称,区分业务类型和范围
feature expiration------------------------------功能有效期
我只写了INFramework一个feature,可以写多个feature在这里。
signature-----------------------------------------顾名思义,签名 下面内容讲如何生成
//格式:vender expiration hostId generated feature name feature expiration sys hostId
//需要注意的是sys hostId,这个是软件实际的安装序列号,签名时跟hostId值一样,但是验证时一定要读取实际的序列号
//示例
Steven Jon2021-04-29Win-C498-97D7-262D-55DD2020-04-29INFramework2021-04-29Win-C498-97D7-262D-55DD
需要获取私钥对上面内容进行签名:
public class KeyConfig { private static final String BASE_FILE_PATH = "D:/KeyStore"; //私钥存放路径 public static final String PRIVATE_KEY_FILE_PATH = BASE_FILE_PATH + "/privateKey.keystore"; //公钥存放路径 public static final String PUBLICKEY_FILE_PATH = BASE_FILE_PATH + "/publicKey.keystore"; //Cer证书存放路径 public static final String CER_FILE_PATH = BASE_FILE_PATH + "/publicCer.cer"; //私钥别名 public static final String PRIVATE_ALIAS = "privateKey"; //公钥别名 public static final String PUBLIC_ALIAS = "publicKey"; //获取keystore所需的密码 public static final String KEYSTORE_PASSWORD = "654321"; //获取私钥所需密码 public static final String KEY_PASSWORD = "123456"; }
获取私钥工具类:
public class KeyTools { /** * 通过keystore获取private key * */ public static PrivateKey getPrivateKey() { FileInputStream is = null; PrivateKey privateKey = null; try { KeyStore keyStore = KeyStore.getInstance("JKS"); is = new FileInputStream(new File(KeyConfig.PRIVATE_KEY_FILE_PATH)); keyStore.load(is, KeyConfig.KEYSTORE_PASSWORD.toCharArray()); privateKey = (PrivateKey) keyStore.getKey(KeyConfig.PRIVATE_ALIAS, KeyConfig.KEY_PASSWORD.toCharArray()); } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null) { is.close(); } } catch (IOException e) { e.printStackTrace(); } } return privateKey; } }
签名工具类:
public class SignTools { //非对称密钥算法 private static final String KEY_ALGORITHM = "SHA1withRSA"; /** * 使用私钥进行许可内容签名 */ public static byte[] sign(byte[] message, PrivateKey privateKey) throws Exception { Signature signature; signature = Signature.getInstance(KEY_ALGORITHM); signature.initSign(privateKey); signature.update(message); return Base64.getEncoder().encode(signature.sign()); } }
对示例内容进行签名:
//示例签名内容
String signContent = "Steven Jon2021-04-29Win-C498-97D7-262D-55DD2020-04-29INFramework2021-04-29Win-C498-97D7-262D-55DD";
//进行签名
byte[] sign = SignTools.sign(signContent.getBytes(), KeyTools.getPrivateKey());
System.out.println(">>>>>>>>>>>>>>>>>>私钥签名结果 :" + new String(sign));
把new String(sign)结果填入licence文件下的signature标签里,写到这里,license文件制作完成。
看到这里,是不是觉得so easy,自己完全可以写一个license生成器,这样生成license文件就会很轻松了,一键到位。
验证license是否有效,需要用到证书文件,或者公钥,用cer证书最后也是为了获取公钥的,至于用哪个,看自己的咯,可以把这个文件发给客户,也可以放到服务器上,只要能找到就可以了。
举个栗子,发cer证书给客户,让客户把证书安装在指定位置。
获取公钥工具类:
public class KeyTools { /** * 通过 cer证书获取公钥 */ public static PublicKey getPublicKeyFromCer(){ PublicKey publicKey = null; FileInputStream in = null; try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); in = new FileInputStream(KeyConfig.CER_FILE_PATH); Certificate c = cf.generateCertificate(in); publicKey = c.getPublicKey(); } catch (CertificateException | FileNotFoundException e) { e.printStackTrace(); } finally { try { if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } return publicKey; } }
验证签名工具类:
public class VerifyTools { //非对称密钥算法 private static final String KEY_ALGORITHM = "SHA1withRSA"; /** * 验签 */ public static boolean verify(byte[] message, byte[] signMessage, PublicKey publicKey) throws Exception { Signature signature; signature = Signature.getInstance(KEY_ALGORITHM); signature.initVerify(publicKey); signature.update(message); return signature.verify(Base64.getDecoder().decode(signMessage)); } }
解析License文件,将里面的关键信息提取出来,按照签名之前的格连接起来。
这一步我就不详细写了,大概过程就是解析xml,难倒是不难,感觉会有点繁琐的,毕竟需要提取的信息比较多。
//偷个懒直接拿上面的
String message = "Steven Jon2021-04-29Win-C498-97D7-262D-55DD2020-04-29INFramework2021-04-29Win-C498-97D7-262D-55DD";
boolean verify = VerifyTools.verify(message.getBytes(), sign, KeyTools.getPublicKeyFromCer());
System.out.println(">>>>>>>>>>>>>>>>>>验证结果 verify =" + verify);
好了,文章写完了,非常感谢你能看到最后,如果觉得有帮助,麻烦点个赞哦!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。