/*
* Copyright (c) 2020-2020, org.smartboot. All rights reserved.
* project name: smart-license
* file name: License.java
* Date: 2020-03-22
* Author: sandao ([email protected])
*/
package org.smartboot.license.client;
import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.concurrent.TimeUnit;
/**
* 读取License并解析其内容
*
* @author 三刀
* @version V1.0 , 2020/3/20
*/
public class License {
/**
* 忽略
*/
public static final RuntimeExpireStrategy EXPIRE_STRATEGY_IGNORE = entity -> System.err.println("invalid license");
/**
* 异常
*/
public static final RuntimeExpireStrategy EXPIRE_STRATEGY_THROWS = entity -> {
throw new LicenseException("invalid license");
};
private static final String KEY_ALGORITHM = "RSA";
private final byte[] readBuffer = new byte[8];
/**
* 过期策略
*/
private final RuntimeExpireStrategy expireStrategy;
/**
* 试用版过期策略
*/
private final RuntimeExpireStrategy trialExpireStrategy;
private final long period;
private LicenseEntity entity;
private long timestamp;
public License() {
this(EXPIRE_STRATEGY_IGNORE, TimeUnit.HOURS.toMillis(1));
}
public License(RuntimeExpireStrategy expireStrategy, long period) {
this(expireStrategy, expireStrategy, period);
}
public License(RuntimeExpireStrategy expireStrategy, RuntimeExpireStrategy trialExpireStrategy, long period) {
if (period < TimeUnit.SECONDS.toMillis(1)) {
throw new IllegalArgumentException("period is too fast");
}
this.expireStrategy = expireStrategy;
this.trialExpireStrategy = trialExpireStrategy;
this.period = period;
}
/**
* 使用公钥进行解密
*
* @param data
* @param publicKey
* @return
*/
private byte[] decryptByPublicKey(byte[] data, byte[] publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(publicKey);
PublicKey pubKey = keyFactory.generatePublic(encodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, pubKey);
return cipher.doFinal(data);
} catch (Exception e) {
throw new LicenseException("decrypt exception", e);
}
}
/**
* 启动License过期监控
*/
private void monitorExpireThread() {
Thread thread = new Thread(() -> {
while (true) {
long sleep = Math.min(entity.getExpireTime() - System.currentTimeMillis(), period);
if (entity.getTrialDuration() > 0) {
sleep = Math.min(sleep, entity.getTrialDuration() * 60000L);
}
if (sleep <= 0) {
sleep = 10000;
}
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (entity.getExpireTime() < System.currentTimeMillis()) {
expireStrategy.expire(entity);
} else if (entity.getTrialDuration() > 0 && System.currentTimeMillis() - timestamp > entity.getTrialDuration() * 60000L) {
trialExpireStrategy.expire(entity);
}
}
}, "licenseMonitor");
thread.setDaemon(true);
thread.start();
}
public LicenseEntity loadLicense(byte[] bytes) throws IOException {
LicenseEntity entity = replace(bytes);
monitorExpireThread();
return entity;
}
public LicenseEntity replace(byte[] bytes) throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
byte[] magicBytes = new byte[LicenseEntity.MAGIC_NUM.length];
inputStream.read(magicBytes);
checkBytes(magicBytes, LicenseEntity.MAGIC_NUM);
// 申请时间
long applyTime = readLong(inputStream);
if (applyTime > System.currentTimeMillis()) {
throw new LicenseException("invalid license");
}
//过期时间
long expireTime = readLong(inputStream);
if (expireTime < System.currentTimeMillis()) {
throw new LicenseException("license expire");
}
//md5
byte[] md5 = new byte[readInt(inputStream)];
inputStream.read(md5);
//公钥
byte[] publicKey = new byte[readInt(inputStream)];
inputStream.read(publicKey);
// System.out.println(Base64.getEncoder().encodeToString(publicKey));
//申请者
byte[] applicant = new byte[readInt(inputStream)];
inputStream.read(applicant);
//联系方式
byte[] contact = new byte[readInt(inputStream)];
inputStream.read(contact);
// System.out.println("contact:" + new String(contact));
//试用时长
int trialDuration = readInt(inputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int size = 0;
while ((size = inputStream.read()) > 0) {
byte[] part = new byte[size];
inputStream.read(part);
byte[] decodeData = decryptByPublicKey(part, publicKey);
byteArrayOutputStream.write(decodeData);
if (readLong(inputStream) != expireTime % decodeData.length) {
throw new LicenseException("invalid license");
}
}
byte[] data = byteArrayOutputStream.toByteArray();
if (!Md5.md5(data).equals(new String(md5))) {
throw new LicenseException("invalid license");
}
LicenseEntity entity = new LicenseEntity(expireTime, publicKey);
entity.setApplicant(new String(applicant));
entity.setContact(new String(contact));
entity.setTrialDuration(trialDuration);
entity.setData(data);
this.entity = entity;
timestamp = System.currentTimeMillis();
return entity;
}
public LicenseEntity getEntity() {
return entity;
}
private void checkBytes(byte[] b1, byte[] b2) {
if (b1.length != b2.length) {
throw new LicenseException("invalid license");
}
for (int i = 0; i < b1.length; i++) {
if (b1[i] != b2[i]) {
throw new LicenseException("invalid license");
}
}
}
private long readLong(InputStream inputStream) throws IOException {
inputStream.read(readBuffer, 0, 8);
return (((long) readBuffer[0] << 56) + ((long) (readBuffer[1] & 255) << 48) + ((long) (readBuffer[2] & 255) << 40) + ((long) (readBuffer[3] & 255) << 32) + ((long) (readBuffer[4] & 255) << 24) + ((readBuffer[5] & 255) << 16) + ((readBuffer[6] & 255) << 8) + ((readBuffer[7] & 255) << 0));
}
private int readInt(InputStream in) throws IOException {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
}
没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
收起资源包目录
smart-license-master.zip (20个子文件)
smart-license-master
LICENSE 11KB
security.md 1KB
images
revert.gif 3.82MB
security.png 40KB
license_apply.png 35KB
license_forget.png 29KB
keypair.gif 2.77MB
smart-license.png 36KB
dencrypt_data.png 257KB
encrypt_data.png 299KB
demo_result.png 324KB
keystore.png 317KB
license-client
pom.xml 8KB
src
test
resources
License 386B
java
org
smartboot
license
client
LicenseClientTest.java 1KB
main
java
org
smartboot
license
client
License.java 7KB
LicenseEntity.java 2KB
RuntimeExpireStrategy.java 405B
Md5.java 1KB
LicenseException.java 533B
共 20 条
- 1
资源评论
Java程序员-张凯
- 粉丝: 1w+
- 资源: 6746
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功