龙空技术网

SpringBoot整合TrueLicense生成和验证License证书(一)

青锋爱编程 2144

前言:

现时姐妹们对“springboot自动生成controller”大致比较关怀,同学们都需要分析一些“springboot自动生成controller”的相关内容。那么小编在网络上搜集了一些关于“springboot自动生成controller””的相关内容,希望同学们能喜欢,大家快快来学习一下吧!

今日分享

每天分享技术实战干货,技术在于积累和收藏,点赞关注小锋,每天实战技术分享。

架构开源地址:

License介绍

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

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

任何加密都有反编译、破解、跳过的手段。

license授权机制的原理

TrueLicense是一个开源的证书管理引擎。

生成密钥对,使用Keytool生成公私钥证书库授权者保留私钥,使用私钥对包含授权信息(如使用截止日期,MAC地址等)的license进行数字签名。公钥给使用者(放在验证的代码中使用),用于验证license是否符合使用条件。springboot整合TrueLicense-生成License证书1、引入依赖

<!-- License --><dependency>    <groupId>de.schlichtherle.truelicense</groupId>    <artifactId>truelicense-core</artifactId>    <version>1.33</version></dependency>
2、新增LicenseCheckModel自定义校验参数
/** * @title LicenseCheckModel * @description 自定义需要校验的License参数 * @author Administrator * @updateTime 2022/4/30 0030 18:19 */@Datapublic class LicenseCheckModel implements Serializable{    private static final long serialVersionUID = 8600137500316662317L;    /**     * 可被允许的IP地址     */    private List<String> ipAddress;    /**     * 可被允许的MAC地址     */    private List<String> macAddress;    /**     * 可被允许的CPU序列号     */    private String cpuSerial;    /**     * 可被允许的主板序列号     */    private String mainBoardSerial;}
3、新增LicenseCreatorParam生成类参数
/** * @ProjectName LicenseCreatorParam * @author Administrator * @version 1.0.0 * @Description License生成类需要的参数 * @createTime 2022/4/30 0030 18:19 */@Datapublic class LicenseCreatorParam implements Serializable {    private static final long serialVersionUID = -7793154252684580872L;    /**     * 证书subject     */    private String subject;    /**     * 密钥别称     */    private String privateAlias;    /**     * 密钥密码(需要妥善保管,不能让使用者知道)     */    private String keyPass;    /**     * 访问秘钥库的密码     */    private String storePass;    /**     * 证书生成路径     */    private String licensePath;    /**     * 密钥库存储路径     */    private String privateKeysStorePath;    /**     * 证书生效时间     */    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")    private Date issuedTime = new Date();    /**     * 证书失效时间     */    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")    private Date expiryTime;    /**     * 用户类型     */    private String consumerType = "user";    /**     * 用户数量     */    private Integer consumerAmount = 1;    /**     * 描述信息     */    private String description = "";    /**     * 额外的服务器硬件校验信息     */    private LicenseCheckModel licenseCheckModel;}
4、新建抽象类AbstractServerInfos

用户获取服务器的硬件信息

/** * @ProjectName AbstractServerInfos * @author Administrator * @version 1.0.0 * @Description 用于获取客户服务器的基本信息,如:IP、Mac地址、CPU序列号、主板序列号等 * @createTime 2022/4/30 0030 18:15 */public abstract class AbstractServerInfos {    private static Logger logger = LogManager.getLogger(AbstractServerInfos.class);    /**     * @title getServerInfos     * @description 组装需要额外校验的License参数     * @author Administrator     * @updateTime 2022/4/30 0030 18:15     */    public LicenseCheckModel getServerInfos(){        LicenseCheckModel result = new LicenseCheckModel();        try {            result.setIpAddress(this.getIpAddress());            result.setMacAddress(this.getMacAddress());            result.setCpuSerial(this.getCPUSerial());            result.setMainBoardSerial(this.getMainBoardSerial());        }catch (Exception e){            logger.error("获取服务器硬件信息失败",e);        }        return result;    }    /**     * @title getIpAddress     * @description 获取IP地址     * @author Administrator     * @updateTime 2022/4/30 0030 18:15     */    protected abstract List<String> getIpAddress() throws Exception;    /**     * @title getMacAddress     * @description 获取Mac地址     * @author Administrator     * @updateTime 2022/4/30 0030 18:16     */    protected abstract List<String> getMacAddress() throws Exception;    /**     * @title getCPUSerial     * @description 获取CPU序列号     * @author Administrator     * @updateTime 2022/4/30 0030 18:16     */    protected abstract String getCPUSerial() throws Exception;    /**     * @title getMainBoardSerial     * @description 获取主板序列号     * @author Administrator     * @updateTime 2022/4/30 0030 18:16     */    protected abstract String getMainBoardSerial() throws Exception;    /**     * @title getLocalAllInetAddress     * @description 获取当前服务器所有符合条件的InetAddress     * @author Administrator     * @updateTime 2022/4/30 0030 18:16     */    protected List<InetAddress> getLocalAllInetAddress() throws Exception {        List<InetAddress> result = new ArrayList<>(4);        // 遍历所有的网络接口        for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) {            NetworkInterface iface = (NetworkInterface) networkInterfaces.nextElement();            // 在所有的接口下再遍历IP            for (Enumeration inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) {                InetAddress inetAddr = (InetAddress) inetAddresses.nextElement();                //排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址                if(!inetAddr.isLoopbackAddress() /*&& !inetAddr.isSiteLocalAddress()*/                        && !inetAddr.isLinkLocalAddress() && !inetAddr.isMulticastAddress()){                    result.add(inetAddr);                }            }        }        return result;    }    /**     * @title getMacByInetAddress     * @description 获取某个网络接口的Mac地址     * @author Administrator     * @updateTime 2022/4/30 0030 18:16     */    protected String getMacByInetAddress(InetAddress inetAddr){        try {            byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress();            StringBuffer stringBuffer = new StringBuffer();            for(int i=0;i<mac.length;i++){                if(i != 0) {                    stringBuffer.append("-");                }                //将十六进制byte转化为字符串                String temp = Integer.toHexString(mac[i] & 0xff);                if(temp.length() == 1){                    stringBuffer.append("0" + temp);                }else{                    stringBuffer.append(temp);                }            }            return stringBuffer.toString().toUpperCase();        } catch (SocketException e) {            e.printStackTrace();        }        return null;    }}
新增LinuxServerInfos获取硬件信息
/** * @ProjectName LinuxServerInfos * @author Administrator * @version 1.0.0 * @Description 用于获取客户Linux服务器的基本信息 * @createTime 2022/4/30 0030 18:20 */public class LinuxServerInfos extends AbstractServerInfos {    @Override    protected List<String> getIpAddress() throws Exception {        List<String> result = null;        //获取所有网络接口        List<InetAddress> inetAddresses = getLocalAllInetAddress();        if(inetAddresses != null && inetAddresses.size() > 0){            result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());        }        return result;    }    @Override    protected List<String> getMacAddress() throws Exception {        List<String> result = null;        //1. 获取所有网络接口        List<InetAddress> inetAddresses = getLocalAllInetAddress();        if(inetAddresses != null && inetAddresses.size() > 0){            //2. 获取所有网络接口的Mac地址            result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());        }        return result;    }    @Override    protected String getCPUSerial() throws Exception {        //序列号        String serialNumber = "";        //使用dmidecode命令获取CPU序列号        String[] shell = {"/bin/bash","-c","dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"};        Process process = Runtime.getRuntime().exec(shell);        process.getOutputStream().close();        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));        String line = reader.readLine().trim();        if(StringUtils.isNotBlank(line)){            serialNumber = line;        }        reader.close();        return serialNumber;    }    @Override    protected String getMainBoardSerial() throws Exception {        //序列号        String serialNumber = "";        //使用dmidecode命令获取主板序列号        String[] shell = {"/bin/bash","-c","dmidecode | grep 'Serial Number' | awk -F ':' '{print $2}' | head -n 1"};        Process process = Runtime.getRuntime().exec(shell);        process.getOutputStream().close();        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));        String line = reader.readLine().trim();        if(StringUtils.isNotBlank(line)){            serialNumber = line;        }        reader.close();        return serialNumber;    }}
新增WindowsServer获取硬件信息
/** * @ProjectName WindowsServerInfos * @author Administrator * @version 1.0.0 * @Description 用于获取客户Windows服务器的基本信息 * @createTime 2022/4/30 0030 18:20 */public class WindowsServerInfos extends AbstractServerInfos {    @Override    protected List<String> getIpAddress() throws Exception {        List<String> result = null;        //获取所有网络接口        List<InetAddress> inetAddresses = getLocalAllInetAddress();        if(inetAddresses != null && inetAddresses.size() > 0){            result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());        }        return result;    }    @Override    protected List<String> getMacAddress() throws Exception {        List<String> result = null;        //1. 获取所有网络接口        List<InetAddress> inetAddresses = getLocalAllInetAddress();        if(inetAddresses != null && inetAddresses.size() > 0){            //2. 获取所有网络接口的Mac地址            result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());        }        return result;    }    @Override    protected String getCPUSerial() throws Exception {        //序列号        String serialNumber = "";        //使用WMIC获取CPU序列号        Process process = Runtime.getRuntime().exec("wmic cpu get processorid");        process.getOutputStream().close();        Scanner scanner = new Scanner(process.getInputStream());        if(scanner.hasNext()){            scanner.next();        }        if(scanner.hasNext()){            serialNumber = scanner.next().trim();        }        scanner.close();        return serialNumber;    }    @Override    protected String getMainBoardSerial() throws Exception {        //序列号        String serialNumber = "";        //使用WMIC获取主板序列号        Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber");        process.getOutputStream().close();        Scanner scanner = new Scanner(process.getInputStream());        if(scanner.hasNext()){            scanner.next();        }        if(scanner.hasNext()){            serialNumber = scanner.next().trim();        }        scanner.close();        return serialNumber;    }}
5、新增自定义CustomKeyStoreParam

自定义KeyStoreParam类CustomKeyStoreParam类继承AbstractKeyStoreParam,实现里面一些该实现的方法。并且重写getStream()获取文件内容的方法,改成从磁盘位置读取。

/** * @ProjectName CustomKeyStoreParam * @author Administrator * @version 1.0.0 * @Description 自定义KeyStoreParam,用于将公私钥存储文件存放到其他磁盘位置而不是项目中 * @createTime 2022/4/30 0030 18:16 */public class CustomKeyStoreParam extends AbstractKeyStoreParam {    /**     * 公钥/私钥在磁盘上的存储路径     */    private String storePath;    private String alias;    private String storePwd;    private String keyPwd;    public CustomKeyStoreParam(Class clazz, String resource,String alias,String storePwd,String keyPwd) {        super(clazz, resource);        this.storePath = resource;        this.alias = alias;        this.storePwd = storePwd;        this.keyPwd = keyPwd;    }    @Override    public String getAlias() {        return alias;    }    @Override    public String getStorePwd() {        return storePwd;    }    @Override    public String getKeyPwd() {        return keyPwd;    }    /**     * @title getStream     * @description     * 复写de.schlichtherle.license.AbstractKeyStoreParam的getStream()方法<br/>     * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中     * @author Administrator     * @updateTime 2022/4/30 0030 18:16     */    @Override    public InputStream getStream() throws IOException {        final InputStream in = new FileInputStream(new File(storePath));        if (null == in){            throw new FileNotFoundException(storePath);        }        return in;    }}
6、新增自定义LicenseManager

继承LicenseManager类,增加我们额外信息的验证(TrueLicense默认只给我们验证了时间)。大家需要根据自己的需求在validate()里面增加额外的验证。

/** * @title CustomLicenseManager * @description 自定义LicenseManager,用于增加额外的服务器硬件信息校验 * @author Administrator * @updateTime 2022/4/30 0030 18:17 */public class CustomLicenseManager extends LicenseManager{    private static Logger logger = LogManager.getLogger(CustomLicenseManager.class);    //XML编码    private static final String XML_CHARSET = "UTF-8";    //默认BUFSIZE    private static final int DEFAULT_BUFSIZE = 8 * 1024;    public CustomLicenseManager() {    }    public CustomLicenseManager(LicenseParam param) {        super(param);    }    /**     * @title create     * @description 复写create方法     * @author Administrator     * @updateTime 2022/4/30 0030 18:17     */    @Override    protected synchronized byte[] create(            LicenseContent content,            LicenseNotary notary)            throws Exception {        initialize(content);        this.validateCreate(content);        final GenericCertificate certificate = notary.sign(content);        return getPrivacyGuard().cert2key(certificate);    }    /**     * @title install     * @description 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息     * @author Administrator     * @updateTime 2022/4/30 0030 18:17     */    @Override    protected synchronized LicenseContent install(            final byte[] key,            final LicenseNotary notary)            throws Exception {        final GenericCertificate certificate = getPrivacyGuard().key2cert(key);        notary.verify(certificate);        final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded());        this.validate(content);        setLicenseKey(key);        setCertificate(certificate);        return content;    }    /**     * @title verify     * @description 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息     * @author Administrator     * @updateTime 2022/4/30 0030 18:17     */    @Override    protected synchronized LicenseContent verify(final LicenseNotary notary)            throws Exception {        GenericCertificate certificate = getCertificate();        // Load license key from preferences,        final byte[] key = getLicenseKey();        if (null == key){            throw new NoLicenseInstalledException(getLicenseParam().getSubject());        }        certificate = getPrivacyGuard().key2cert(key);        notary.verify(certificate);        final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded());        this.validate(content);        setCertificate(certificate);        return content;    }    /**     * @title validateCreate     * @description 校验生成证书的参数信息     * @author Administrator     * @updateTime 2022/4/30 0030 18:18     */    protected synchronized void validateCreate(final LicenseContent content)            throws LicenseContentException {        final LicenseParam param = getLicenseParam();        final Date now = new Date();        final Date notBefore = content.getNotBefore();        final Date notAfter = content.getNotAfter();        if (null != notAfter && now.after(notAfter)){            throw new LicenseContentException("证书失效时间不能早于当前时间");        }        if (null != notBefore && null != notAfter && notAfter.before(notBefore)){            throw new LicenseContentException("证书生效时间不能晚于证书失效时间");        }        final String consumerType = content.getConsumerType();        if (null == consumerType){            throw new LicenseContentException("用户类型不能为空");        }    }    /**     * @title validate     * @description 复写validate方法,增加IP地址、Mac地址等其他信息校验     * @author Administrator     * @updateTime 2022/4/30 0030 18:18     */    @Override    protected synchronized void validate(final LicenseContent content)            throws LicenseContentException {        //1. 首先调用父类的validate方法        super.validate(content);        //2. 然后校验自定义的License参数        //License中可被允许的参数信息        LicenseCheckModel expectedCheckModel = (LicenseCheckModel) content.getExtra();        //当前服务器真实的参数信息        LicenseCheckModel serverCheckModel = getServerInfos();        if(expectedCheckModel != null && serverCheckModel != null){            //校验IP地址            if(!checkIpAddress(expectedCheckModel.getIpAddress(),serverCheckModel.getIpAddress())){                throw new LicenseContentException("当前服务器的IP没在授权范围内");            }            //校验Mac地址            if(!checkIpAddress(expectedCheckModel.getMacAddress(),serverCheckModel.getMacAddress())){                throw new LicenseContentException("当前服务器的Mac地址没在授权范围内");            }            //校验主板序列号            if(!checkSerial(expectedCheckModel.getMainBoardSerial(),serverCheckModel.getMainBoardSerial())){                throw new LicenseContentException("当前服务器的主板序列号没在授权范围内");            }            //校验CPU序列号            if(!checkSerial(expectedCheckModel.getCpuSerial(),serverCheckModel.getCpuSerial())){                throw new LicenseContentException("当前服务器的CPU序列号没在授权范围内");            }        }else{            throw new LicenseContentException("不能获取服务器硬件信息");        }    }    /**     * @title load     * @description 重写XMLDecoder解析XML     * @author Administrator     * @updateTime 2022/4/30 0030 18:18     */    private Object load(String encoded){        BufferedInputStream inputStream = null;        XMLDecoder decoder = null;        try {            inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET)));            decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE),null,null);            return decoder.readObject();        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        } finally {            try {                if(decoder != null){                    decoder.close();                }                if(inputStream != null){                    inputStream.close();                }            } catch (Exception e) {                logger.error("XMLDecoder解析XML失败",e);            }        }        return null;    }    /**     * @title getServerInfos     * @description 获取当前服务器需要额外校验的License参数     * @author Administrator     * @updateTime 2022/4/30 0030 18:18     */    private LicenseCheckModel getServerInfos(){        //操作系统类型        String osName = System.getProperty("os.name").toLowerCase();        AbstractServerInfos abstractServerInfos = null;        //根据不同操作系统类型选择不同的数据获取方法        if (osName.startsWith("windows")) {            abstractServerInfos = new WindowsServerInfos();        } else if (osName.startsWith("linux")) {            abstractServerInfos = new LinuxServerInfos();        }else{//其他服务器类型            abstractServerInfos = new LinuxServerInfos();        }        return abstractServerInfos.getServerInfos();    }    /**     * @title checkIpAddress     * @description     * 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内<br/>     * 如果存在IP在可被允许的IP/Mac地址范围内,则返回true     * @author Administrator     * @updateTime 2022/4/30 0030 18:18     */    private boolean checkIpAddress(List<String> expectedList,List<String> serverList){        if(expectedList != null && expectedList.size() > 0){            if(serverList != null && serverList.size() > 0){                for(String expected : expectedList){                    if(serverList.contains(expected.trim())){                        return true;                    }                }            }            return false;        }else {            return true;        }    }    /**     * @title checkSerial     * @description 校验当前服务器硬件(主板、CPU等)序列号是否在可允许范围内     * @author Administrator     * @updateTime 2022/4/30 0030 18:18     */    private boolean checkSerial(String expectedSerial,String serverSerial){        if(StringUtils.isNotBlank(expectedSerial)){            if(StringUtils.isNotBlank(serverSerial)){                if(expectedSerial.equals(serverSerial)){                    return true;                }            }            return false;        }else{            return true;        }    }}
7、新增LicenseCreator证书生成类
/** * @ProjectName LicenseCreator * @author Administrator * @version 1.0.0 * @Description License生成类 * @createTime 2022/4/30 0030 18:19 */public class LicenseCreator {    private static Logger logger = LogManager.getLogger(LicenseCreator.class);    private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");    private LicenseCreatorParam param;    public LicenseCreator(LicenseCreatorParam param) {        this.param = param;    }    /**     * @title generateLicense     * @description 生成License证书     * @author Administrator     * @updateTime 2022/4/30 0030 18:19     */    public boolean generateLicense(){        try {            LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());            LicenseContent licenseContent = initLicenseContent();            licenseManager.store(licenseContent,new File(param.getLicensePath()));            return true;        }catch (Exception e){            logger.error(MessageFormat.format("证书生成失败:{0}",param),e);            return false;        }    }    /**     * @title initLicenseParam     * @description 初始化证书生成参数     * @author Administrator     * @updateTime 2022/4/30 0030 18:19     */    private LicenseParam initLicenseParam(){        Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);        //设置对证书内容加密的秘钥        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());        KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class                ,param.getPrivateKeysStorePath()                ,param.getPrivateAlias()                ,param.getStorePass()                ,param.getKeyPass());        LicenseParam licenseParam = new DefaultLicenseParam(param.getSubject()                ,preferences                ,privateStoreParam                ,cipherParam);        return licenseParam;    }    /**     * @title initLicenseContent     * @description 设置证书生成正文信息     * @author Administrator     * @updateTime 2022/4/30 0030 18:19     */    private LicenseContent initLicenseContent(){        LicenseContent licenseContent = new LicenseContent();        licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER);        licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER);        licenseContent.setSubject(param.getSubject());        licenseContent.setIssued(param.getIssuedTime());        licenseContent.setNotBefore(param.getIssuedTime());        licenseContent.setNotAfter(param.getExpiryTime());        licenseContent.setConsumerType(param.getConsumerType());        licenseContent.setConsumerAmount(param.getConsumerAmount());        licenseContent.setInfo(param.getDescription());        //扩展校验服务器硬件信息        licenseContent.setExtra(param.getLicenseCheckModel());        return licenseContent;    }}
8、新增LicenseCreatorController证书生成

这个Controller对外提供了两个RESTful接口,分别是「获取服务器硬件信息」和「生成证书」

/** * @ProjectName LicenseCreatorController * @author Administrator * @version 1.0.0 * @Description 于生成证书文件,不能放在给客户部署的代码里 * @createTime 2022/4/30 0030 18:13 */@RestController@RequestMapping("/license")public class LicenseCreatorController {    /**     * 证书生成路径     */    @Value("${license.licensePath}")    private String licensePath;    /**     * @title 获取服务器硬件信息     * @description @param osName 操作系统类型,如果为空则自动判断     * @author Administrator     * @updateTime 2022/4/30 0030 18:14     */    @RequestMapping(value = "/getServerInfos")    public LicenseCheckModel getServerInfos(@RequestParam(value = "osName",required = false) String osName) {        //操作系统类型        if(StringUtils.isBlank(osName)){            osName = System.getProperty("os.name");        }        osName = osName.toLowerCase();        AbstractServerInfos abstractServerInfos = null;        //根据不同操作系统类型选择不同的数据获取方法        if (osName.startsWith("windows")) {            abstractServerInfos = new WindowsServerInfos();        } else if (osName.startsWith("linux")) {            abstractServerInfos = new LinuxServerInfos();        }else{//其他服务器类型            abstractServerInfos = new LinuxServerInfos();        }        return abstractServerInfos.getServerInfos();    }    /**     * @title 生成证书     * @description     * {     *     "result": "ok",     *     "msg": {     *         "subject": "license_demo",     *         "privateAlias": "privateKey",     *         "keyPass": "private_password1234",     *         "storePass": "public_password1234",     *         "licensePath": "D:/license/license.lic",     *         "privateKeysStorePath": "D:/license/privateKeys.keystore",     *         "issuedTime": "2022-04-10 00:00:01",     *         "expiryTime": "2022-05-31 23:59:59",     *         "consumerType": "User",     *         "consumerAmount": 1,     *         "description": "这是证书描述信息",     *         "licenseCheckModel": {     *             "ipAddress": [],     *             "macAddress": [],     *             "cpuSerial": "",     *             "mainBoardSerial": ""     *         }     *     }     * }     * @author Administrator     * @updateTime 2022/4/30 0030 18:14      */    @RequestMapping(value = "/generateLicense",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})    public Map<String,Object> generateLicense(@RequestBody(required = true) LicenseCreatorParam param) {        Map<String,Object> resultMap = new HashMap<>(2);        if(StringUtils.isBlank(param.getLicensePath())){            param.setLicensePath(licensePath);        }        LicenseCreator licenseCreator = new LicenseCreator(param);        boolean result = licenseCreator.generateLicense();        if(result){            resultMap.put("result","ok");            resultMap.put("msg",param);        }else{            resultMap.put("result","error");            resultMap.put("msg","证书文件生成失败!");        }        return resultMap;    }}
9、使用Keytool生成公私钥证书库

假如我们设置公钥库密码为:public_password1234,私钥库密码为:private_password1234,则生成命令如下:

## 1. 生成私匙库# validity:私钥的有效期多少天# alias:私钥别称# keystore: 指定私钥库文件的名称(生成在当前目录)# storepass:指定私钥库的密码(获取keystore信息所需的密码) # keypass:指定别名条目的密码(私钥的密码) keytool -genkeypair -keysize 1024 -validity 3650 -alias "privateKey" -keystore "privateKeys.keystore" -storepass "public_password1234" -keypass "private_password1234" -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN"## 2. 把私匙库内的公匙导出到一个文件当中# alias:私钥别称# keystore:指定私钥库的名称(在当前目录查找)# storepass: 指定私钥库的密码# file:证书名称keytool -exportcert -alias "privateKey" -keystore "privateKeys.keystore" -storepass "public_password1234" -file "certfile.cer"## 3. 再把这个证书文件导入到公匙库# alias:公钥别称# file:证书名称# keystore:公钥文件名称# storepass:指定私钥库的密码keytool -import -alias "publicCert" -file "certfile.cer" -keystore "publicCerts.keystore" -storepass "public_password1234"

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

10、生成license证书

运行项目,项目启动后,通过postmain进行测试:

获取服务器硬件信息

请求地址:

生成license证书

访问地址:

请求时需要在Header中添加一个 Content-Type ,其值为:application/json;charset=UTF-8。参数示例如下

{	"subject": "license_demo",	"privateAlias": "privateKey",	"keyPass": "private_password1234",	"storePass": "public_password1234",	"licensePath": "D:/license/license.lic",	"privateKeysStorePath": "D:/license/privateKeys.keystore",	"issuedTime": "2022-04-10 00:00:01",	"expiryTime": "2022-05-31 23:59:59",	"consumerType": "User",	"consumerAmount": 1,	"description": "这是证书描述信息",	"licenseCheckModel": {		"ipAddress": [],		"macAddress": [],		"cpuSerial": "",		"mainBoardSerial": ""	}}

如果请求成功,那么最后会在 licensePath 参数设置的路径生成一个 license.lic 的文件,这个文件就是给客户部署代码的服务器许可文件。

新建Test测试类生成证书

/** * @author Administrator * @version 1.0.0 * @ProjectName qingfeng-license * @Description TODO * @createTime 2022年04月30日 21:27:00 */@SpringBootTestpublic class LicenseTest {    /**     * {     * 	"subject": "license_demo",     * 	"privateAlias": "privateKey",     * 	"keyPass": "private_password1234",     * 	"storePass": "public_password1234",     * 	"licensePath": "D:/license/license.lic",     * 	"privateKeysStorePath": "D:/license/privateKeys.keystore",     * 	"issuedTime": "2022-04-10 00:00:01",     * 	"expiryTime": "2022-05-31 23:59:59",     * 	"consumerType": "User",     * 	"consumerAmount": 1,     * 	"description": "这是证书描述信息",     * 	"licenseCheckModel": {     * 		"ipAddress": [],     * 		"macAddress": [],     * 		"cpuSerial": "",     * 		"mainBoardSerial": ""     *        }     * }     */    @Test    public void licenseCreate() {        // 生成license需要的一些参数        LicenseCreatorParam param = new LicenseCreatorParam();        param.setSubject("license_demo");        param.setPrivateAlias("privateKey");        param.setKeyPass("private_password1234");        param.setStorePass("public_password1234");        param.setLicensePath("D:/license/license.lic");        param.setPrivateKeysStorePath("D:/license/privateKeys.keystore");        Calendar issueCalendar = Calendar.getInstance();        param.setIssuedTime(issueCalendar.getTime());        Calendar expiryCalendar = Calendar.getInstance();        expiryCalendar.set(2022, Calendar.JUNE, 31, 23, 59, 59);        param.setExpiryTime(expiryCalendar.getTime());        param.setConsumerType("user");        param.setConsumerAmount(1);        param.setDescription("这是证书描述信息");        //自定义需要校验的License参数        LicenseCheckModel licenseCheckModel = new LicenseCheckModel();        licenseCheckModel.setCpuSerial("");        licenseCheckModel.setMainBoardSerial("");        licenseCheckModel.setIpAddress(new ArrayList<>());        licenseCheckModel.setMacAddress(new ArrayList<>());        LicenseCreator licenseCreator = new LicenseCreator(param);        param.setLicenseCheckModel(licenseCheckModel);        // 生成license        licenseCreator.generateLicense();    }}

标签: #springboot自动生成controller #linux导入cer证书