IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> License文档 -> 正文阅读

[网络协议]License文档

License学习文档

一、简介

License,即版权许可证,一般用于收费软件给付费用户提供的访问许可证明。根据应用部署位置的不同,一般可以分为以下两种情况讨论:

  • 应用部署在开发者自己的云服务器上。这种情况下用户通过账号登录的形式远程访问,因此只需要在账号登录的时候校验目标账号的有效期、访问权限等信息即可。
  • 应用部署在客户的内网环境。因为这种情况开发者无法控制客户的网络环境,也不能保证应用所在服务器可以访问外网,因此通常的做法是使用服务器许可文件,在应用启动的时候加载证书,然后在登录或者其他关键操作的地方校验证书的有效性。

License使用前提:

想要使用License,我们需要明确证书内部的内容,生成方式,存储位置,加密方法,证书内容校验等。
使用原理:
1、生成密钥对,包含私钥和公钥。
2、授权者保留私钥,使用私钥对授权信息诸如使用截止日期,mac 地址等内容生成 license 签名证书。
3、公钥给使用者,放在代码中使用,用于验证 license 签名证书是否符合使用条件。

二、环境配置

1.导入依赖
<dependency>
    <groupId>de.schlichtherle.truelicense</groupId>
    <artifactId>truelicense-core</artifactId>
    <version>1.33</version>
    <scope>provided</scope>
</dependency>

本机端口号和MAC地址记录

{
    "ipAddress":["192.168.0.102"],     			//IP地址
    "macAddress":["B4-0E-DE-D1-ED-9F"],			//MAC地址
    "cpuSerial":"178BFBFF00A50F00",				//CPU序列号
        "mainBoardSerial":"YX026BMC"			    //主板序列号
}

License证书模板内容如下:实体类LicenseCreatorParam

{
	"subject":"ccx-models",
	"privateAlias":"privateKey",
	"keyPass":"5T7Zz5Y0dJFcqTxvzkH5LDGJJSGMzQ",
	"storePass":"3538cef8e7",
	"licensePath":"C:/Users/zifangsky/Desktop/license.lic",
	"privateKeysStorePath":"C:/Users/zifangsky/Desktop/privateKeys.keystore",
	"issuedTime":"2018-04-26 14:48:12",
	"expiryTime":"2018-12-31 00:00:00",	
	"consumerType":"User",
	"consumerAmount":1,
	"description":"这是证书描述信息",
	"licenseCheckModel":{
        "ipAddress":["192.168.245.1","10.0.5.22"],
		"macAddress":["00-50-56-C0-00-01","50-7B-9D-F9-18-41"],
		"cpuSerial":"BFEBFBFF000406E3",
		"mainBoardSerial":"L1HF65E00X9"
    } 
}

三、使用JDK自带的 keytool 工具生成公私钥证书库

1.keytool简介
Keytool 是一个Java 数据证书的管理工具 ,Keytool 将密钥(key)和证书(certificates)存在一个称为keystore的文件中 在keystore里,包含两种数据: 
密钥实体(Key entity)——密钥(secret key)又或者是私钥和配对公钥(采用非对称加密) 
可信任的证书实体(trusted certificate entries)——只包含公钥
2.项目中的使用

以下命令在 window cmd 命令窗口执行,注意当前执行目录,最后生成的密钥对即在该目录下:
1、首先要用 KeyTool 工具来生成私匙库:(-alias别名 -validity 3650 表示10年有效)

#生成命令
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

最后生成的文件 privateKeys.store(私钥)、publicCerts.store(公钥)拷贝出来备用。

密钥需包含字母+数字
踩坑:The password does not match the default policy: At least six characters cons
密码与默认策略不匹配:至少6个字符

上述命令执行完成之后,会在当前路径下生成三个文件,分别是:privateKeys.keystore、publicCerts.keystore、certfile.cer。其中文件certfile.cer不再需要可以删除,文件privateKeys.keystore用于当前的项目给客户生成license文件,而文件publicCerts.keystore则随应用代码部署到客户服务器,用户解密license文件并校验其许可信息。

注意:我们使用的公钥和私钥分别如下

privateKeys: private1234
publicCerts: public1234

四、为客户生成license文件

1.建立一个单例模式下的证书管理器
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;
    }
}
2.配置证书和公私钥内容

在licenseMakeConf.properties配置文件中配置私钥的配置信息和自定义的项目证书信息,该配置信息用于为客户生成License证书文件,内容如下:

##########common parameters###########
#私钥的别名
private.key.alias=privatekey
#privateKeyPwd(该密码生成密钥对的密码,需要妥善保管,不能让使用者知道)
private.key.pwd=private1234
#keyStorePwd(该密码是在使用keytool生成密钥对时设置的密钥库的访问密码)
key.store.pwd=public1234
#项目的唯一识别码
subject=licenseTest
#生成证书的地址
licPath=D:/license/license.lic
#密钥库的地址
priPath=/privateKeys.store
##########license content###########
#发布日期
issuedTime=2021-07-25
#有效开始日期
notBefore=2021-07-25
#有效截止日期
notAfter=2022-08-30
# ip 地址
ipAddress=192.168.0.102
# mac 地址
macAddress=B4-0E-DE-D1-ED-9F
# 使用者类型,用户(user)、电脑(computer)、其他(else)
consumerType=user
# 证书允许使用的消费者数量
consumerAmount=1
# 证书说明
info=power by xiamen yungu

在licenseVertifyConf.properties配置文件中配置证书验证时所需的配置信息,内容如下:

##########common parameters###########
#公钥别名
public.alias=publiccert
#使用keytool生成密钥对时设置的密钥库的访问密码
key.store.pwd= public1234
#证书路径
license.name=license.lic
#公共库路径
public.store.path=/publicCerts.store
#项目的唯一识别码
subject=licenseTest
3.生成License文件
@Slf4j
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 ipAddress;
    private String macAddress;
    private String consumerType;
    private int consumerAmount;
    private String info;

    private String licPath;


    /**
     * 构造器,参数初始化
     *
     * @param confPath 参数配置文件路径
     */
    public CreateLicense(String confPath) {
        // 获取参数
        Properties prop = new Properties();
        try (InputStream in = getClass().getResourceAsStream(confPath)) {
            prop.load(in);
        } catch (IOException e) {
            log.error("CreateLicense Properties load inputStream error.", e);
        }
        //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");
        // license content
        issued = prop.getProperty("issuedTime");
        notBefore = prop.getProperty("notBefore");
        notAfter = prop.getProperty("notAfter");
        ipAddress = prop.getProperty("ipAddress");
        macAddress = prop.getProperty("macAddress");
        consumerType = prop.getProperty("consumerType");
        consumerAmount = Integer.valueOf(prop.getProperty("consumerAmount"));
        info = prop.getProperty("info");

        licPath = prop.getProperty("licPath");
    }


    /**
     * 生成证书,在证书发布者端执行
     *
     * @throws Exception
     */
    public void create() throws Exception {
        LicenseManager licenseManager = LicenseManagerHolder.getLicenseManager(initLicenseParams());
        licenseManager.store(buildLicenseContent(), new File(licPath));
        log.info("------ 证书发布成功 ------");
    }

    /**
     * 初始化证书的相关参数
     *
     * @return
     */
    private LicenseParam initLicenseParams() {
        Class<CreateLicense> 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<String, String> map = new HashMap<>(4);
        map.put("ip", ipAddress);
        map.put("mac", macAddress);
        content.setExtra(map);
        return content;
    }
}
4.生成证书方法测试
public static void main(String[] args) throws Exception {
    CreateLicense clicense = new CreateLicense("/licenseCreateParam.properties");
    clicense.create();
}
5.测试结果
在配置文件对应的磁盘目录中,系统可自动生成License许可证书,需注意,证书内部的开始时间,生效时间,结束时间要符合正常逻辑,否则无法生成License证书。

五、License校验

1.书写验证License信息类

用公钥验证 license 证书,验证用户系统启动时是否有证书且证书是否在生效期间内等信息。

package com.zony.metro.license;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.prefs.Preferences;

import de.schlichtherle.license.*;
import lombok.extern.slf4j.Slf4j;

@Slf4j
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");
        keyStorePwd = prop.getProperty("key.store.pwd");
    }

    /**
     * 初始化证书的相关参数
     * @param  //系统的统一识别码
     * @return
     */
    private LicenseParam initLicenseParams()
    {
        Class<LicenseVertify> clazz=LicenseVertify.class;
        Preferences pre=Preferences.userNodeForPackage(clazz);
        CipherParam cipherParam=new DefaultCipherParam(keyStorePwd);
        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 boolean vertifyExtra() {
        try {
            LicenseManager licenseManager = getLicenseManager();
            LicenseContent verify = licenseManager.verify();
            log.info("验证证书成功!");
            Map<String, String> extra = (Map) verify.getExtra();
            String ip = extra.get("ip");
            InetAddress inetAddress = InetAddress.getLocalHost();
            String localIp = inetAddress.toString().split("/")[1];
            if (!Objects.equals(ip, localIp)) {
                log.error("IP 地址验证不通过");
                return false;
            }
            String mac = extra.get("mac");
            String localMac = getLocalMac(inetAddress);
            if (!Objects.equals(mac, localMac)) {
                log.error("MAC 地址验证不通过");
                return false;
            }
            log.info("IP、MAC地址验证通过");
            return true;
        } catch (LicenseContentException ex) {
            log.error("证书已经过期!", ex);
            return false;
        } catch (Exception e) {
            log.error("验证证书失败!", e);
            return false;
        }
    }

    /**
     * 得到本机 mac 地址
     *
     * @param inetAddress
     * @throws //SocketException
     */
    private String getLocalMac(InetAddress inetAddress) throws SocketException {
        //获取网卡,获取地址
        byte[] mac = NetworkInterface.getByInetAddress(inetAddress).getHardwareAddress();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < mac.length; i++) {
            if (i != 0) {
                sb.append("-");
            }
            //字节转换为整数
            int temp = mac[i] & 0xff;
            String str = Integer.toHexString(temp);
            if (str.length() == 1) {
                sb.append("0" + str);
            } else {
                sb.append(str);
            }
        }
        return sb.toString().toUpperCase();
    }
}
2.项目启动时,安装证书并验证信息

在项目启动时,加载并安装License证书信息,然后进行License证书内容判断,如果项目未安装证书或证书内容不合法,则项目无法启动。

@Component
public class LicenseCheck {

    @PostConstruct
    public void init() {
        VerifyLicense vlicense = new VerifyLicense();
        vlicense.install();
        if (!vlicense.vertify()) {
            Runtime.getRuntime().halt(1);
        }
    }
}
3.添加用户类型,用户数量验证
/**
     * 验证证书的合法性
     */
public boolean vertifyExtra(Map<String,Object> userLicenseContent) {
    try {
        LicenseManager licenseManager = getLicenseManager();
        //证书内部信息
        LicenseContent verify = licenseManager.verify();
        log.info("验证证书成功!");
        Map<String, String> extra = (Map) verify.getExtra();
        String ip = extra.get("ip");
        InetAddress inetAddress = InetAddress.getLocalHost();
        String localIp = inetAddress.toString().split("/")[1];
        if (!Objects.equals(ip, localIp)) {
            log.error("IP 地址验证不通过");
            return false;
        }
        String mac = extra.get("mac");
        String localMac = getLocalMac(inetAddress);
        if (!Objects.equals(mac, localMac)) {
            log.error("MAC 地址验证不通过");
            return false;
        }
        log.info("IP、MAC地址验证通过");
        //授权用户数量验证
        int consumerAmount = verify.getConsumerAmount();
        Integer userConsumerAmount = (Integer)userLicenseContent.get("userConsumerAmount");
        if(userConsumerAmount > consumerAmount){
            log.error("用户数量过大");
            return false;
        }
        //授权对象验证
        String consumerType = verify.getConsumerType();
        String userConsumerType = (String)userLicenseContent.get("userConsumerType");
        if(!consumerType.equals(userConsumerType)){
            log.error("该用户并不是授权用户");
            return false;
        }

        return true;
    } catch (LicenseContentException ex) {
        log.error("证书已经过期!", ex);
        return false;
    } catch (Exception e) {
        log.error("验证证书失败!", e);
        return false;
    }
}

在测试时,需要获取到用户的数量以及用户的类型,这里直接赋值了。

/**
    *@Description 安装证书,并验证证书的合法性
    *@Param []
    *@Return void
    *@Author ChuXu
    *@Date 2021/9/23
    *@Time 17:06
    */
@Test
public void testInstallLicense(){
    LicenseVertify vlicense=new LicenseVertify("licenseTest"); // 项目唯一识别码,对应生成配置文件的subject
    //        vlicense.install(System.getProperty("D:/license"));
    vlicense.install("D:/license");
    //验证License证书的基本信息
    //        vlicense.vertify();
    //验证License证书内部IP,MAC地址,用户数量,用户类型等信息
    Map<String, Object> map = new HashMap<>();
    map.put("userConsumerAmount",1);
    map.put("userConsumerType","op");
    vlicense.vertifyExtra(map);
}

参考文档:
https://www.cnblogs.com/jmcui/p/11909579.html
https://www.zifangsky.cn/1277.html

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-09-26 10:33:32  更:2021-09-26 10:35:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:04:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码