精华内容
下载资源
问答
  • web系统Licence验证 保证java web ,可以现在IP,mac,自定义参数,License生成器 (JAVA源码+界面) 其中包括license授权机制的原理和制作license的具体步骤 增加了mac 地址验证
  • 保证java web ,spirngboot,tomcate web安全,可以现在IP,mac,自定义参数,License生成器 (JAVA源码+界面) 其中包括license授权机制的原理和制作license的具体步骤 增加了mac 地址验证
  • JAVA代码添加License

    万次阅读 2018-08-07 16:39:01
    在开源代码的时候,我们经常会在代码顶部添加License信息,每个文件复制粘贴显然是比较麻烦的,所以写了一个工具类方便为源码添加License部分,供大家参考,可以按照实际情况进行修改。 import java.io....

    在开源代码的时候,我们经常会在代码顶部添加License信息,每个文件复制粘贴显然是比较麻烦的,我们可以在工具中进行配置,在创建新的类的时候自动为我们添加相关信息,以eclipse为例。

    进入Preference->Java->Code Style->Code Template
    这里写图片描述

    在中间的面板中选择Comments->Files,然后单击Edit...按钮,在弹出的对话框中填写我们的License信息。
    这里写图片描述

    最后在我们创建新的JAVA类的时候需要勾选Generate comments

    这里写图片描述

    这样,我们新创建的文件就会自动添加License信息了。如果现存的一些历史文件,用上面的方法显然就不太好了,所以写了一个工具类方便为源码添加License部分,供大家参考,可以按照实际情况进行修改。

    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileFilter;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    
    import org.junit.Test;
    
    /**
     * License复制工具
     * 
     * @author jianggujin
     *
     */
    public class LicenseCopyUtils implements FileFilter {
       /**
        * 读取License文件
        * 
        * @param in
        * @param charset
        * @return
        * @throws IOException
        */
       public String readLicenseHeader(InputStream in, String charset) throws IOException {
          BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
          StringBuilder builder = new StringBuilder("/**\r\n");
          String line = null;
          while ((line = reader.readLine()) != null) {
             builder.append(" * ");
             builder.append(line);
             builder.append("\r\n");
          }
          builder.append(" */");
          return builder.toString();
       }
    
       /**
        * 处理license头部信息
        * 
        * @param root
        * @param in
        * @param charset
        * @throws IOException
        */
       public void processLicenseHeader(File root, InputStream in, String charset) throws IOException {
          System.out.println("开始读取并格式化license...");
          String headerBody = readLicenseHeader(in, charset);
          System.out.println(headerBody);
          System.out.println("读取并格式化license完成...");
          if (root.isDirectory() || root.getName().endsWith(".java")) {
             System.out.println("开始处理:" + root.getAbsolutePath());
             processLicenseHeader(root, charset, headerBody);
          }
       }
    
       private void processLicenseHeader(File root, String charset, String headerBody) throws IOException {
          if (root.isFile()) {
             System.out.println("开始读取并处理:" + root.getAbsolutePath());
             BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(root), charset));
             ByteArrayOutputStream stream = new ByteArrayOutputStream();
             BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stream, charset));
             writer.write(headerBody);
             writer.write("\r\n");
             String line = null;
             boolean body = false;
             while ((line = reader.readLine()) != null) {
                if (body || line.startsWith("package ") || line.startsWith("import ")) {
                   body = true;
                   writer.write(line);
                   writer.write("\r\n");
                }
             }
             reader.close();
             writer.close();
             FileOutputStream out = new FileOutputStream(root);
             stream.writeTo(out);
             out.flush();
             out.close();
             System.out.println("读取并处理[" + root.getAbsolutePath() + "]完成");
          } else {
             File[] list = root.listFiles(this);
             if (list != null)
                for (File file : list) {
                   processLicenseHeader(file, charset, headerBody);
                }
          }
       }
    
       @Override
       public boolean accept(File file) {
          return file.isDirectory() || file.getName().endsWith(".java");
       }
    
       @Test
       public void test() throws IOException {
          processLicenseHeader(new File("src/main/java"), new FileInputStream("license.txt"), "UTF-8");
       }
    }
    展开全文
  • 我们使用trueLicense来做软件产品的保护,我们主要使用它的LicenseManager类来生成证书文件、安装证书文件、验证证书文件. 二、原理 首先需要生成密钥对,方法有很多,JDK中提供的KeyTool即可生成。 授权者保留私钥...

    前言

    最近接到一个情况,公司平台有个授权使用的机制,之前负载这个事情的人走了,留在svn上的代码是无法通过授权的,所以让我看看什么情况

    一、使用场景以及truelicense是什么

    官网地址

    TrueLicense是一个开源的证书管理引擎,使用场景:当项目交付给客户之后用签名来保证客户不能随意使用项目 默认校验了开始结束时间,可扩展增加mac地址校验等。 其中还有ftp的校验没有尝试,本demo详细介绍的是本地校验 license授权机制的原理: 生成密钥对,方法有很多。我们使用trueLicense来做软件产品的保护,我们主要使用它的LicenseManager类来生成证书文件、安装证书文件、验证证书文件.

    二、原理

    1. 首先需要生成密钥对,方法有很多,JDK中提供的KeyTool即可生成。
    2. 授权者保留私钥,使用私钥对包含授权信息(如截止日期,MAC地址等)的license进行数字签名。
    3. 公钥交给使用者(放在验证的代码中使用),用于验证license是否符合使用条件。

    三、使用Keytool命令生成密钥对

    首先

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

    在接触代码前,我们先来大概熟悉下密钥生成的流程吧
    Tips: 以下命令详细的参数需要可查看参考资料.4
    1、首先要用KeyTool工具来生成私匙库:(-alias别名 –validity 3650表示10年有效)

    keytool -genkey -alias privatekey -keystore privateKeys.store -validity 3650
    

    这里密码我使用123456q
    注意!!!默认的密码策略是6未数字与字母,如果不遵守会报错 第五节第二点的错
    在这里插入图片描述
    这个时候,会在打开命令行的地方创建出一个文件,privateKeys.store()
    2、然后把私匙库内的证书导出到一个文件当中:

    keytool -export -alias privatekey -file certfile.cer -keystore privateKeys.store
    

    生成certfile.cer(证书),生成公钥库后就没什么用了

    3、然后再把这个证书文件导入到公匙库:

    keytool -import -alias publiccert -file certfile.cer -keystore publicCerts.store
    

    生成 publicCerts.store

    privateKeys.keystore:私钥,这个我们自己留着,不能泄露给别人。
    publicCerts.keystore:公钥,这个给客户用的。在我们程序里面就是用他配合license进行授权信息的校验的。
    certfile.cer:这个文件没啥用,可以删掉。

    最后自行将生成文件privateKeys.store、publicCerts.store拷贝出来备用。

    四、实现代码 - 证书生成

    maven依赖

    <!--        truelicense 依赖-->
            <!-- https://mvnrepository.com/artifact/de.schlichtherle.truelicense/truelicense-core -->
            <dependency>
                <groupId>de.schlichtherle.truelicense</groupId>
                <artifactId>truelicense-core</artifactId>
                <version>1.33</version>
            </dependency>
    

    首先从整个流程上来讲,现在这步是证书生成,证书生成需要私钥库和证书参数
    在这个引擎中,公/私钥库默认是存储在项目中的。** 但是,我们实际生产环境中,都是将配置文件等脱离项目部署的**,所以我们需要重写它获取公/私钥库的地方。
    CustomKeyStoreParam.java

    import de.schlichtherle.license.AbstractKeyStoreParam;
    import org.springframework.util.ResourceUtils;
    
    import java.io.*;
    
    /**
     * 自定义KeyStoreParam,用于将公私钥存储文件存放到其他磁盘位置而不是项目中。现场使用的时候公钥大部分都不会放在项目中的
     */
    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;
        }
    
        /**
         * AbstractKeyStoreParam里面的getStream()方法默认文件是存储的项目中。
         * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中
         */
        @Override
        public InputStream getStream() throws IOException {
    //        return new FileInputStream(new File(storePath));
            File file = ResourceUtils.getFile(storePath);
            if (file.exists()) {
                return new FileInputStream(file);
            } else {
                throw new FileNotFoundException(storePath);
            }
        }
    }
    
    

    证书参数可以用配置文件配置,也可以写成类,这个方法用的就是类的方式

    License.java

    import cn.genm.license.dto.LicenseExtraModel;
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * License生成类需要的参数
     */
    @Data
    public class License 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;
    
        /**
         * 证书生效时间
         */
        private Date issuedTime = new Date();
    
        /**
         * 证书失效时间
         */
        private Date expiryTime;
    
        /**
         * 用户类型
         */
        private String consumerType = "user";
    
        /**
         * 用户数量
         */
        private Integer consumerAmount = 1;
    
        /**
         * 描述信息
         */
        private String description = "";
    
        /**
         * 额外的服务器硬件校验信息
         */
        private LicenseExtraModel licenseExtraModel;
    }
    
    

    其中的扩展参数类

    
    /**
     * 自定义需要校验的License参数,可以增加一些额外需要校验的参数,比如项目信息,ip地址信息等等,待完善
     */
    public class LicenseExtraModel {
    
        // 这里可以添加一些往外的自定义信息,比如我们可以增加项目验证,客户电脑sn码的验证等等
    
    }
    
    

    由于引擎本身默认只验证了有效期,当我们需要自定义一个继承于LicenseManager的自定义证书管理器。
    额外的信息的校验可以加在validate()方法里

    CustomLicenseManager.java

    import de.schlichtherle.license.*;
    import de.schlichtherle.xml.GenericCertificate;
    import de.schlichtherle.xml.XMLConstants;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    import java.beans.XMLDecoder;
    import java.io.BufferedInputStream;
    import java.io.ByteArrayInputStream;
    import java.io.UnsupportedEncodingException;
    import java.util.Date;
    
    /**
     * 自定义LicenseManager,用于增加额外的信息校验(除了LicenseManager的校验,我们还可以在这个类里面添加额外的校验信息)
     */
    public class CustomLicenseManager extends LicenseManager {
    
        private static Logger logger = LogManager.getLogger(CustomLicenseManager.class);
    
        public CustomLicenseManager(LicenseParam param) {
            super(param);
        }
    
        /**
         * 复写create方法
         */
        @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);
        }
    
        /**
         * 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息
         */
        @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;
        }
    
        /**
         * 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息
         */
        @Override
        protected synchronized LicenseContent verify(final LicenseNotary notary) throws Exception {
    
            // Load license key from preferences,
            final byte[] key = getLicenseKey();
            if (null == key) {
                throw new NoLicenseInstalledException(getLicenseParam().getSubject());
            }
    
            GenericCertificate certificate = getPrivacyGuard().key2cert(key);
            notary.verify(certificate);
            final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
            this.validate(content);
            setCertificate(certificate);
    
            return content;
        }
    
        /**
         * 校验生成证书的参数信息
         */
        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("用户类型不能为空");
            }
        }
    
    
        /**
         * 复写validate方法,用于增加我们额外的校验信息
         */
        @Override
        protected synchronized void validate(final LicenseContent content) throws LicenseContentException {
            //1. 首先调用父类的validate方法
            super.validate(content);
            //2. 然后校验自定义的License参数,去校验我们的license信息
            LicenseExtraModel expectedCheckModel = (LicenseExtraModel) content.getExtra();
            // 做我们自定义的校验
        }
    
    
        /**
         * 重写XMLDecoder解析XML
         */
        private Object load(String encoded) {
            BufferedInputStream inputStream = null;
            XMLDecoder decoder = null;
            try {
                inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XMLConstants.XML_CHARSET)));
                decoder = new XMLDecoder(new BufferedInputStream(inputStream, XMLConstants.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;
        }
    
    }
    
    

    前面的所有可以说都是为了整个流程在铺垫,现在开始是真正开始生成License证书的代码

    LicenseCreator.java

    import cn.genm.license.dto.CustomKeyStoreParam;
    import cn.genm.license.dto.CustomLicenseManager;
    import cn.genm.license.model.License;
    import de.schlichtherle.license.*;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    import javax.security.auth.x500.X500Principal;
    import java.io.File;
    import java.text.MessageFormat;
    import java.util.prefs.Preferences;
    
    /**
     * License生成类 -- 用于license生成
     */
    public class LicenseCreator {
    
        private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=GENMER, OU=GENM, O=GENM, L=FUZHOU, ST=FUJIAN, C=CHINA");
    
        private static Logger logger = LogManager.getLogger(LicenseCreator.class);
    
        private License license;
    
        public LicenseCreator(License license) {
            this.license = license;
        }
    
        /**
         * 生成License证书
         */
        public boolean generateLicense() {
            try {
                LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());
                LicenseContent licenseContent = initLicenseContent();
                licenseManager.store(licenseContent, new File(license.getLicensePath()));
                return true;
            } catch (Exception e) {
                logger.error(MessageFormat.format("证书生成失败:{0}", license), e);
                return false;
            }
        }
    
        /**
         * 初始化证书生成参数
         */
        private LicenseParam initLicenseParam() {
            Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);
    
            //设置对证书内容加密的秘钥
            CipherParam cipherParam = new DefaultCipherParam(license.getStorePass());
    
            KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class
                    , license.getPrivateKeysStorePath()
                    , license.getPrivateAlias()
                    , license.getStorePass()
                    , license.getKeyPass());
    
            return new DefaultLicenseParam(license.getSubject()
                    , preferences
                    , privateStoreParam
                    , cipherParam);
        }
    
        /**
         * 设置证书生成正文信息
         */
        private LicenseContent initLicenseContent() {
            LicenseContent licenseContent = new LicenseContent();
            licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER);
            licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER);
    
            licenseContent.setSubject(license.getSubject());
            licenseContent.setIssued(license.getIssuedTime());
            licenseContent.setNotBefore(license.getIssuedTime());
            licenseContent.setNotAfter(license.getExpiryTime());
            licenseContent.setConsumerType(license.getConsumerType());
            licenseContent.setConsumerAmount(license.getConsumerAmount());
            licenseContent.setInfo(license.getDescription());
    
            //扩展校验,这里可以自定义一些额外的校验信息(也可以用json字符串保存)
            if (license.getLicenseExtraModel() != null) {
                licenseContent.setExtra(license.getLicenseExtraModel());
            }
    
            return licenseContent;
        }
    
    }
    
    

    五、测试 - 证书生成

    环境工具类都准备好了,接下来直接开始测试,看看能否生成

     @Test
        void generateLicense() {
            // 生成license需要的一些参数
            License param = new License();
            // 证书授权主体
            param.setSubject("licenseTest");
            // 私钥别名
            param.setPrivateAlias("privateKey");
            // 私钥密码(需要妥善保管,不能让使用者知道)
            param.setKeyPass("123456q");
            // 访问私钥库的密码
            param.setStorePass("123456q");
            // 证书存储地址
            param.setLicensePath("E:\\license2\\license.lic");
            // 私钥库所在地址
            param.setPrivateKeysStorePath("E:\\license\\privateKeys.store");
            // 证书生效时间
            Calendar issueCalendar = Calendar.getInstance();
            param.setIssuedTime(issueCalendar.getTime());
            // 证书失效时间
            Calendar expiryCalendar = Calendar.getInstance();
            // 设置当前时间
            expiryCalendar.setTime(new Date());
            // 往后延长一年 = 授权一年时间
            expiryCalendar.add(Calendar.YEAR,1);
            param.setExpiryTime(expiryCalendar.getTime());
            // 用户类型
            param.setConsumerType("user");
            // 用户数量
            param.setConsumerAmount(1);
            // 描述
            param.setDescription("测试");
            LicenseCreator licenseCreator = new LicenseCreator(param);
            // 生成license
            licenseCreator.generateLicense();
        }
    

    结果
    生成的license证书

    六、代码实现 - 证书安装和校验

    就像证书生成,验证也需要一个专门的类

    LicenseVerify.java

    import cn.genm.license.dto.CustomKeyStoreParam;
    import cn.genm.license.dto.CustomLicenseManager;
    import de.schlichtherle.license.*;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    import java.io.File;
    import java.text.DateFormat;
    import java.text.MessageFormat;
    import java.text.SimpleDateFormat;
    import java.util.prefs.Preferences;
    
    /**
     * License校验类
     */
    public class LicenseVerify {
    
        private static Logger logger = LogManager.getLogger(LicenseVerify.class);
        /**
         * 证书subject
         */
        private String subject;
        /**
         * 公钥别称
         */
        private String publicAlias;
        /**
         * 访问公钥库的密码
         */
        private String storePass;
        /**
         * 证书生成路径
         */
        private String licensePath;
        /**
         * 密钥库存储路径
         */
        private String publicKeysStorePath;
        /**
         * LicenseManager
         */
        private LicenseManager licenseManager;
        /**
         * 标识证书是否安装成功
         */
        private boolean installSuccess;
    
        public LicenseVerify(String subject, String publicAlias, String storePass, String licensePath, String publicKeysStorePath) {
            this.subject = subject;
            this.publicAlias = publicAlias;
            this.storePass = storePass;
            this.licensePath = licensePath;
            this.publicKeysStorePath = publicKeysStorePath;
        }
    
        /**
         * 安装License证书,读取证书相关的信息, 在bean加入容器的时候自动调用
         */
        public void installLicense() {
            try {
                Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);
    
                CipherParam cipherParam = new DefaultCipherParam(storePass);
    
                KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class,
                        publicKeysStorePath,
                        publicAlias,
                        storePass,
                        null);
                LicenseParam licenseParam = new DefaultLicenseParam(subject, preferences, publicStoreParam, cipherParam);
    
                licenseManager = new CustomLicenseManager(licenseParam);
                licenseManager.uninstall();
                LicenseContent licenseContent = licenseManager.install(new File(licensePath));
                DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                installSuccess = true;
                logger.info("------------------------------- 证书安装成功 -------------------------------");
                logger.info(MessageFormat.format("证书有效期:{0} - {1}", format.format(licenseContent.getNotBefore()), format.format(licenseContent.getNotAfter())));
            } catch (Exception e) {
                installSuccess = false;
                logger.error("------------------------------- 证书安装成功 -------------------------------");
                logger.error(e);
            }
        }
    
        /**
         * 卸载证书,在bean从容器移除的时候自动调用
         */
        public void unInstallLicense() {
            if (installSuccess) {
                try {
                    licenseManager.uninstall();
                } catch (Exception e) {
                    // ignore
                }
            }
        }
    
        /**
         * 校验License证书
         */
        public boolean verify() {
            try {
                LicenseContent licenseContent = licenseManager.verify();
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    
    }
    
    

    我们之前说了,除了项目许多的配置文件我们一般是需要放在服务器单独的路径下的,除了公钥和私钥库,还有我们验证需要配置的一些参数

    application.yml

    #License相关配置
    license:
     subject: licenseTest #主体 - 注意主体要与生成证书的主体一致一致,不然验证通过不了
     publicAlias: publicCert #公钥别称
     storePass: 123456q #访问公钥的密码
     licensePath: E:\license2\license.lic #license位置
     publicKeysStorePath: E:\license\publicCerts.store #公钥位置
    
    

    这些参数代码获取如下

    LicenseConfig.java

    import cn.genm.license.server.LicenseVerify;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class LicenseConfig {
    
        /**
         * 证书subject
         */
        @Value("${license.subject}")
        private String subject;
    
        /**
         * 公钥别称
         */
        @Value("${license.publicAlias}")
        private String publicAlias;
    
        /**
         * 访问公钥库的密码
         */
        @Value("${license.storePass}")
        private String storePass;
    
        /**
         * 证书生成路径
         */
        @Value("${license.licensePath}")
        private String licensePath;
    
        /**
         * 密钥库存储路径
         */
        @Value("${license.publicKeysStorePath}")
        private String publicKeysStorePath;
    
        @Bean(initMethod = "installLicense", destroyMethod = "unInstallLicense")
        public LicenseVerify licenseVerify() {
            return new LicenseVerify(subject, publicAlias, storePass, licensePath, publicKeysStorePath);
        }
    
    }
    
    

    以上代码是读取yml的配置,以及将LicenseConfig加入Spring容器,在加入Spring容器的同时,执行licenseVerify里的安装方法

    这样,程序就会在启动时,自动安装证书,校验时就可以用了

    至于这个安装方法,我会单独加一篇博客整理阐述Spring里的那些能在服务启动时执行自定义操作的方法

    七、测试 - 证书的安装和校验

    最后的最后,验证
    Tips: 记得测试的方法要创建启动类,因为licenseConfig要加入Spring容器,如果没有
    LicenseApplication.java

    /**
     * @author By genmer
     * @version V1.0.0
     * @date 2021/07/9 13:35
     */
    @SpringBootApplication
    public class LicenseApplication {
        public static void main(String[] args) {
            SpringApplication.run(LicenseApplication.class, args);
        }
    }
    
    

    校验代码

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class LicenseVerifyTest {
    
        private LicenseVerify licenseVerify;
    
        @Autowired
        public void setLicenseVerify(LicenseVerify licenseVerify) {
            this.licenseVerify = licenseVerify;
        }
    
        @Test
        public void licenseVerify() {
           System.out.println("licese是否有效:" + licenseVerify.verify());
        }
    
    }
    
    

    结果
    证书校验结果

    八、执行代码期间遇到的问题

    七月 08, 2021 4:01:39 下午 java.util.prefs.WindowsPreferences <init> WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5. Exception in thread "main" de.schlichtherle.
    注册表中找不到prefs
    

    解决方法:win+R 输入 regedit
    找到这个路径,新建项HKEY_LOCAL_MACHINE\Software\JavaSoft
    在这里插入图片描述
    2. 密码策略要6位置数字与字母

    Exception in thread "main" de.schlichtherle.license.IllegalPasswordException: The password does not match the default policy: At least six characters consisting of letters and digits!
    
    1. certificate is not yet valid
      注意properties里设置的时间要合理就好了,比如我今天7.8 ,我发布和有效都是7.25,有效期时间可能是这个问题的一个产生原因,一开始我认为还是密码为题,一直找

    2. 很明显,找不到这个文件,写带盘符的绝对路径就好了

    Exception in thread "main" java.io.FileNotFoundException: /privateKeys.store
    
    1. 也很明显,不要放在中文路径下 。
    Exception in thread "main" java.io.FileNotFoundException: E:\license许可测试/privateKeys.store
    
    1. 我在执行参考资料1的时候遇到的,虽然网上说只是个读取到结尾的标志,是正常的,可以我遇到了,并且证书不生成了,不太确定是代码问题还是异常引起的生成识别,还没解决
    Exception in thread "main" java.io.EOFException
    
    1. 很明显,就是Subject参数在测试用例的时候的值和yml配置不一样,这样其实就算是验证失败的一种情况。
    de.schlichtherle.license.LicenseContentException: exc.invalidSubject
    

    九、参考资料

    1. javaEE防盗版-License开发
    2. 微信小程序 certificate is not yet valid
    3. TrueLicense实现license验证
    4. Keytool命令详解
    5. TrueLicense简介
    展开全文
  • Java:项目license证书控制

    千次阅读 2020-06-12 13:31:49
    } } CustomLicenseManager /** * 自定义LicenseManager,用于增加额外的服务器硬件信息校验 * */ @Slf4j public class CustomLicenseManager extends LicenseManager { /** * XML编码 */ private static final ...

    学习记录……

    原文:https://www.zifangsky.cn/1277.html

     

    license即版权许可证。实际使用中部署给客户的项目,不能随意被二次使用。那么就需要license证书对项目做出控制。

     

    1 pom:

            <!-- license处理 -->
            <dependency>
                <groupId>de.schlichtherle.truelicense</groupId>
                <artifactId>truelicense-core</artifactId>
                <version>1.33</version>
                <scope>compile</scope>
            </dependency>

    2

    服务端:生成license

    客户端:对license做出验证

     

    3 具体步骤

    1、创建一个服务端项目和一个客户端项目
    2、导入相关依赖及相关类
    3、执行keytool命令,生成密钥库文件
    4、调用服务端接口,以密钥库生成license文件
    5、指定项目配置(如license路径),启动客户端
    6、完

     

    附1:服务端提供API
     

    /**
     * 用于生成证书文件,不能放在给客户部署的代码里
     *
     */
    @Api(tags = "license api")
    @RestController
    @RequestMapping("/license")
    public class LicenseApi {
    
        /**
         * 证书生成路径
         */
        @Value("${license.licensePath}")
        private String licensePath;
    
        /**
         * 获取服务器硬件信息
         */
        @ApiOperation(value = "获取服务器硬件信息")
        @GetMapping(value = "/serverInfo")
        public ResponseResult<LicenseCheckModel> getServerInfos(@RequestParam(value = "osName", required = false) String osName) {
            //操作系统类型
            if (StrUtil.isBlank(osName)) {
                osName = System.getProperty("os.name");
            }
            osName = osName.toLowerCase();
            AbstractServerInfos abstractServerInfos = null;
            //根据不同操作系统类型选择不同的数据获取方法
            if (osName.startsWith("windows")) {
                abstractServerInfos = new WindowsServerInfos();
            } else {
                //其他服务器类型
                abstractServerInfos = new LinuxServerInfos();
            }
            return ResponseResult.ok(abstractServerInfos.getServerInfos());
        }
    
        /**
         * 生成证书
         */
        @ApiOperation(value = "生成证书")
        @PostMapping(value = "/generateLicense")
        public ResponseResult generateLicense(@RequestBody(required = true) LicenseCreatorParam param) {
            if (StrUtil.isBlank(param.getLicensePath())) {
                param.setLicensePath(licensePath);
            }
            LicenseCreator licenseCreator = new LicenseCreator(param);
            boolean result = licenseCreator.generateLicense();
            if (result) {
                return ResponseResult.ok("证书文件生成成功");
            } else {
                return ResponseResult.be("证书文件生成失败");
            }
        }
    
    }
    
    

    生成证书示例

    {
    	"subject": "custom_license",
    	"privateAlias": "privateKey",
    	"keyPass": "private_password1234",
    	"storePass": "public_password1234",
    	"licensePath": "D:/temp/keytool/license.lic",
    	"privateKeysStorePath": "D:/temp/keytool/privateKeys.keystore",
    	"issuedTime": "2020-06-03 00:00:01",
    	"expiryTime": "2020-06-03 23:59:59",
    	"consumerType": "User",
    	"consumerAmount": 1,
    	"description": "这是证书描述信息",
    	"licenseCheckModel": {
    		  "ipAddress": [
    		    "1.1.1.1"
    		  ],
    		  "macAddress": [
    		    "A#-D5-D3-12-1F-D2"
    		  ],
    		  "cpuSerial": "ADADADADA000239",
    		  "mainBoardSerial": "199023140001"
    	}
    }

     

     

    附2:keytool命令

    ### license生成证书命令,JDK自带keytool工具
    
    #生成:在当前目录下,生成一个名为privateKeys.keystore的密钥库,同时指定密钥库密码为public_password1234,第一个条目为privateKey,指定条目密码为private_password1234
    keytool -genkeypair -keysize 1024 -validity 1 -alias "privateKey" -keystore "privateKeys.keystore" -storepass "public_password1234" -keypass "private_password1234" -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN"
    
    #导出:将密钥库中privateKey的条目,导出到一个名为certfile.cer的数字文件
    keytool -exportcert -alias "privateKey" -keystore "privateKeys.keystore" -storepass "public_password1234" -file "certfile.cer"
    
    #导入:将刚刚导出的数字文件,导入为一个新的密钥库publicCerts.keystore
    keytool -import -alias "publicCert" -file "certfile.cer" -keystore "publicCerts.keystore" -storepass "public_password1234"
    
    
    参数释义:
    alias:第一个条目别名
    validity:证书有效期,单位:天
    keystore:指定密钥库名称
    storepass:指定密钥库密码
    keypass:指定别名对应密码(私钥密码)
    
    此三命令执行完之后,会生成三个文件:privateKeys.keystore,publicCerts.keystore,certfile.cer。
    privateKeys.keystore:用于服务端生产license文件
    publicCerts.keystore:随客户端项目部署
    certfile.cer:暂存文件,删除即可

     

    附3:代码

    先说明服务端和客户端都需要通用的业务类:

    AbstractServerInfos

    /**
     * 用于获取客户服务器的基本信息,如:IP、Mac地址、CPU序列号、主板序列号等
     *
     */
    @Data
    @Slf4j
    public abstract class AbstractServerInfos {
    
        /**
         * 组装需要额外校验的License参数
         */
        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) {
                log.error("获取服务器硬件信息失败", e);
            }
    
            return result;
        }
    
        /**
         * 获取IP地址
         */
        protected abstract List<String> getIpAddress() throws Exception;
    
        /**
         * 获取Mac地址
         */
        protected abstract List<String> getMacAddress() throws Exception;
    
        /**
         * 获取CPU序列号
         */
        protected abstract String getCPUSerial() throws Exception;
    
        /**
         * 获取主板序列号
         */
        protected abstract String getMainBoardSerial() throws Exception;
    
        /**
         * 获取某个网络接口的Mac地址
         */
        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) {
                log.info("net:{}", JSON.toJSONString(inetAddr));
                log.error("获取网络接口的Mac地址", e);
            }
            return null;
        }
    
        /**
         * 获取当前服务器所有符合条件的InetAddress
         */
        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地址
                    /*&& !inetAddr.isSiteLocalAddress()*/
                    if (!inetAddr.isLoopbackAddress()
                            && !inetAddr.isLinkLocalAddress()
                            && !inetAddr.isMulticastAddress()) {
                        result.add(inetAddr);
                    }
                }
            }
            return result;
        }
    
    }
    
    

     

    CustomKeyStoreParam

    /**
     * 自定义KeyStoreParam,用于将公私钥存储文件存放到其他磁盘位置而不是项目中
     *
     */
    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;
        }
    
        /**
         * 复写de.schlichtherle.license.AbstractKeyStoreParam的getStream()方法
         * <br/>
         * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中
         */
        @Override
        public InputStream getStream() throws IOException {
            final InputStream in = new FileInputStream(new File(storePath));
            if (null == in) {
                throw new FileNotFoundException(storePath);
            }
            return in;
        }
    }
    

     

    CustomLicenseManager

    /**
     * 自定义LicenseManager,用于增加额外的服务器硬件信息校验
     *
     */
    @Slf4j
    public class CustomLicenseManager extends LicenseManager {
    
        /**
         * XML编码
         */
        private static final String XML_CHARSET = "UTF-8";
        /**
         * 默认 buf size
         */
        private static final int DEFAULT_BUFSIZE = 8 * 1024;
    
        public CustomLicenseManager() {
        }
        public CustomLicenseManager(LicenseParam param) {
            super(param);
        }
    
        /**
         * 复写create方法
         */
        @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);
        }
    
        /**
         * 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息
         */
        @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;
        }
    
        /**
         * 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息
         */
        @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;
        }
    
        /**
         * 校验生成证书的参数信息
         */
        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("用户类型不能为空");
            }
        }
    
        /**
         * 复写validate方法,增加IP地址、Mac地址等其他信息校验
         */
        @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) {
                //校验主板序列号
                if (!checkSerial(expectedCheckModel.getMainBoardSerial(), serverCheckModel.getMainBoardSerial())) {
                    throw new LicenseContentException("当前服务器的主板序列号没在授权范围内");
                }
                //校验IP地址
                if (!checkIpAddress(expectedCheckModel.getIpAddress(), serverCheckModel.getIpAddress())) {
                    throw new LicenseContentException("当前服务器的IP没在授权范围内");
                }
                //校验Mac地址
                if (!checkIpAddress(expectedCheckModel.getMacAddress(), serverCheckModel.getMacAddress())) {
                    throw new LicenseContentException("当前服务器的Mac地址没在授权范围内");
                }
                //校验CPU序列号
                if (!checkSerial(expectedCheckModel.getCpuSerial(), serverCheckModel.getCpuSerial())) {
                    throw new LicenseContentException("当前服务器的CPU序列号没在授权范围内");
                }
            } else {
                throw new LicenseContentException("不能获取服务器硬件信息");
            }
        }
    
        /**
         * 重写XMLDecoder解析XML
         */
        private Object load(String encoded) {
            BufferedInputStream inputStream = null;
            XMLDecoder decoder = null;
            try {
                // 替换自定义license参数类:以客户端定义的LicenseCheckModel,替换xml中服务端的LicenseCheckModel路径
                String clazzName = LicenseCheckModel.class.getName();
                encoded = encoded.replace("cn.test.license.LicenseCheckModel", clazzName);
                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) {
                log.error("解析XML异常", e);
            } finally {
                try {
                    if (decoder != null) {
                        decoder.close();
                    }
                    if (inputStream != null) {
                        inputStream.close();
                    }
                } catch (Exception e) {
                    log.error("XMLDecoder解析XML失败", e);
                }
            }
            return null;
        }
    
        /**
         * 获取当前服务器需要额外校验的License参数
         */
        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();
        }
    
        /**
         * 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内
         * <br/>
         * 如果存在IP在可被允许的IP/Mac地址范围内,则返回true
         */
        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;
            }
        }
    
        /**
         * 校验当前服务器硬件(主板、CPU等)序列号是否在可允许范围内
         */
        private boolean checkSerial(String expectedSerial, String serverSerial) {
            if (StrUtil.isNotBlank(expectedSerial)) {
                if (StrUtil.isNotBlank(serverSerial)) {
                    return expectedSerial.equals(serverSerial);
                }
                return false;
            } else {
                return true;
            }
        }
    
    }
    

     

    LicenseCheckModel

    /**
     * 自定义需要校验的License参数
     *
     */
    @Data
    @ApiModel("服务器校验信息")
    public class LicenseCheckModel implements Serializable {
    
        /**
         * 可被允许的IP地址
         */
        @ApiModelProperty("可被允许的IP地址")
        private List<String> ipAddress;
        /**
         * 可被允许的MAC地址
         */
        @ApiModelProperty("可被允许的MAC地址")
        private List<String> macAddress;
        /**
         * 可被允许的CPU序列号
         */
        @ApiModelProperty("可被允许的CPU序列号")
        private String cpuSerial;
        /**
         * 可被允许的主板序列号
         */
        @ApiModelProperty("可被允许的主板序列号")
        private String mainBoardSerial;
    
    }
    

     

    LinuxServerInfos

    /**
     * 用于获取客户Linux服务器的基本信息
     *
     */
    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 (StrUtil.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 (StrUtil.isNotBlank(line)) {
                serialNumber = line;
            }
            reader.close();
            return serialNumber;
        }
    }
    
    

     

    WindowsServerInfos

    /**
     * 用于获取客户Windows服务器的基本信息
     *
     */
    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;
        }
    
    }
    

     

    服务端

    yml

    #License相关配置
    license:
      licensePath: license/license.lic

    LicenseCreator

    /**
     * License生成类
     */
    @Slf4j
    public class LicenseCreator {
    
        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;
        }
    
        /**
         * 生成License证书
         */
        public boolean generateLicense() {
            try {
                LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());
                LicenseContent licenseContent = initLicenseContent();
                licenseManager.store(licenseContent, new File(param.getLicensePath()));
                return true;
            } catch (Exception e) {
                log.error(MessageFormat.format("证书生成失败:{0}", param), e);
                return false;
            }
        }
    
        /**
         * 初始化证书生成参数
         */
        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;
        }
    
        /**
         * 设置证书生成正文信息
         */
        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;
        }
    
    }
    

     

    LicenseCreatorParam

    /**
     * License生成类需要的参数
     *
     */
    @Data
    @ApiModel("License需要的参数")
    public class LicenseCreatorParam implements Serializable {
    
        /**
         * 证书subject
         */
        @ApiModelProperty("证书subject")
        private String subject;
        /**
         * 密钥别称
         */
        @ApiModelProperty("密钥别称")
        private String privateAlias;
        /**
         * 密钥密码(需要妥善保管,不能让使用者知道)
         */
        @ApiModelProperty("密钥密码")
        private String keyPass;
        /**
         * 访问秘钥库的密码
         */
        @ApiModelProperty("访问秘钥库的密码")
        private String storePass;
        /**
         * 证书生成路径
         */
        @ApiModelProperty("证书生成路径")
        private String licensePath;
        /**
         * 密钥库存储路径
         */
        @ApiModelProperty("密钥库存储路径")
        private String privateKeysStorePath;
        /**
         * 证书生效时间
         */
        @ApiModelProperty("证书生效时间")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date issuedTime = new Date();
        /**
         * 证书失效时间
         */
        @ApiModelProperty("证书失效时间")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date expiryTime;
        /**
         * 用户类型
         */
        @ApiModelProperty("用户类型")
        private String consumerType = "User";
        /**
         * 用户数量
         */
        @ApiModelProperty("用户数量")
        private Integer consumerAmount = 1;
        /**
         * 描述信息
         */
        @ApiModelProperty("描述信息")
        private String description = "";
        /**
         * 额外的服务器硬件校验信息
         */
        @ApiModelProperty("服务器校验信息")
        private LicenseCheckModel licenseCheckModel;
    
    }
    

     

    客户端

    yml:

    ##License相关配置
    license:
      subject: custom_license
      publicAlias: publicCert
      storePass: 1234567890asdfghjkl
      licensePath: D:\temp\keytool\license.lic
      publicKeysStorePath: D:\temp\keytool\publicCerts.keystore

    LicenseManagerHolder

    /**
     * de.schlichtherle.license.LicenseManager的单例
     *
     */
    public class LicenseManagerHolder {
    
        private static volatile LicenseManager LICENSE_MANAGER;
        public static LicenseManager getInstance(LicenseParam param) {
            if (LICENSE_MANAGER == null) {
                synchronized (LicenseManagerHolder.class) {
                    if (LICENSE_MANAGER == null) {
                        LICENSE_MANAGER = new CustomLicenseManager(param);
                    }
                }
            }
    
            return LICENSE_MANAGER;
        }
    
    }

     

    LicenseVerify

    /**
     * License校验类
     *
     */
    @Slf4j
    public class LicenseVerify {
    
        /**
         * 安装License证书
         */
        public synchronized LicenseContent install(LicenseVerifyParam param) {
            LicenseContent result = null;
            //1. 安装证书
            try {
                LicenseManager licenseManager = LicenseManagerHolder.getInstance(initLicenseParam(param));
                licenseManager.uninstall();
                result = licenseManager.install(new File(param.getLicensePath()));
                log.info("证书安装成功,证书有效期:{} - {}",
                        DateUtil.formatDateTime(result.getNotBefore()),
                        DateUtil.formatDateTime(result.getNotAfter()));
            } catch (Exception e) {
                log.error("证书安装失败!", e);
                throw new BusinessException("证书安装失败");
            }
            return result;
        }
    
        /**
         * 校验License证书
         */
        public boolean verify() {
            LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);
            //2. 校验证书
            try {
                LicenseContent licenseContent = licenseManager.verify();
                log.info("证书校验通过,证书有效期:{} - {}",
                        DateUtil.formatDateTime(licenseContent.getNotBefore()),
                        DateUtil.formatDateTime(licenseContent.getNotAfter()));
                return true;
            } catch (Exception e) {
                log.error("证书校验失败!", e);
                return false;
            }
        }
    
        /**
         * 初始化证书生成参数
         */
        private LicenseParam initLicenseParam(LicenseVerifyParam param) {
            Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);
            CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
            KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class
                    , param.getPublicKeysStorePath()
                    , param.getPublicAlias()
                    , param.getStorePass()
                    , null);
            return new DefaultLicenseParam(param.getSubject()
                    , preferences
                    , publicStoreParam
                    , cipherParam);
        }
    
    }
    
    

     

    LicenseVerifyParam

    /**
     * License校验类需要的参数
     *
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class LicenseVerifyParam {
    
        /**
         * 证书subject
         */
        private String subject;
    
        /**
         * 公钥别称
         */
        private String publicAlias;
    
        /**
         * 访问公钥库的密码
         */
        private String storePass;
    
        /**
         * 证书生成路径
         */
        private String licensePath;
    
        /**
         * 密钥库存储路径
         */
        private String publicKeysStorePath;
    
    }
    

     

    LicenseCheckListener

    /**
     * 在项目启动时安装证书
     *
     */
    @Slf4j
    @Component
    public class LicenseCheckListener implements ApplicationListener<ContextRefreshedEvent> {
    
        /**
         * 证书subject
         */
        @Value("${license.subject}")
        private String subject;
        /**
         * 公钥别称
         */
        @Value("${license.publicAlias}")
        private String publicAlias;
        /**
         * 访问公钥库的密码
         */
        @Value("${license.storePass}")
        private String storePass;
        /**
         * 证书生成路径
         */
        @Value("${license.licensePath}")
        private String licensePath;
        /**
         * 密钥库存储路径
         */
        @Value("${license.publicKeysStorePath}")
        private String publicKeysStorePath;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            //root application context 没有parent
            ApplicationContext context = event.getApplicationContext().getParent();
            if (context == null) {
                if (StrUtil.isNotBlank(licensePath)) {
                    log.info("++++++++ 开始安装证书 ++++++++");
                    LicenseVerifyParam param = new LicenseVerifyParam();
                    param.setSubject(subject);
                    param.setPublicAlias(publicAlias);
                    param.setStorePass(storePass);
                    param.setLicensePath(licensePath);
                    param.setPublicKeysStorePath(publicKeysStorePath);
                    LicenseVerify licenseVerify = new LicenseVerify();
                    //安装证书
                    licenseVerify.install(param);
                    log.info("++++++++ 证书安装结束 ++++++++");
                }
            }
        }
    }
    

     

    LicenseCheckInterceptor

    /**
     * 拦截器校验证书是否合法
     *
     */
    @Slf4j
    @Component
    public class LicenseCheckInterceptor extends HandlerInterceptorAdapter {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            LicenseVerify licenseVerify = new LicenseVerify();
            // 校验证书是否有效
            boolean verifyResult = licenseVerify.verify();
            if (verifyResult) {
                return true;
            } else {
                log.warn("您的证书无效,请核查服务器是否取得授权或重新申请证书!");
                throw new BusinessException("您的证书无效,请核查服务器是否取得授权或重新申请证书!");
            }
        }
    
    }
    

     

     

     

     

     

     

     

    完。

     

    展开全文
  • publicabstractclassMyRSACoder{publicstaticfinalStringKEY_ALGORITHM="RSA";publicstaticfinalStringKEY_PROVIDER="BC";publicstaticfinalStringSIGNATURE_ALGORITHM="SHA1WithRSA";/***初始...

    public abstract class MyRSACoder {

    public static final String KEY_ALGORITHM = "RSA";

    public static final String KEY_PROVIDER = "BC";

    public static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";

    /**

    * 初始化密钥对

    */

    public static Map initKeys(String seed) throws Exception {

    Map keyMap = new HashMap();

    Security.addProvider(new BouncyCastleProvider());

    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM,KEY_PROVIDER);

    keyPairGenerator.initialize(1024,new SecureRandom(seed.getBytes()));

    KeyPair pair = keyPairGenerator.generateKeyPair();

    RSAPublicKey rsaPublicKey = (RSAPublicKey) pair.getPublic();

    RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) pair.getPrivate();

    KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM,KEY_PROVIDER);

    RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(rsaPublicKey.getModulus().toString()),new BigInteger(rsaPublicKey.getPublicExponent().toString()));

    RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(rsaPrivateKey.getModulus().toString()),new BigInteger(rsaPrivateKey.getPrivateExponent().toString()));

    PublicKey publicKey = factory.generatePublic(pubKeySpec);

    PrivateKey privateKey = factory.generatePrivate(priKeySpec);

    System.out.println("公钥:" + pubKeySpec.getModulus() + "----" + pubKeySpec.getPublicExponent());

    System.out.println("私钥:" + priKeySpec.getModulus() + "----" + priKeySpec.getPrivateExponent());

    keyMap.put("publicKey", publicKey);

    keyMap.put("privateKey", privateKey);

    return keyMap;

    }

    /**

    * 私钥加密

    * */

    public static byte[] encryptRSA(byte[] data,PrivateKey privateKey) throws Exception {

    Cipher cipher = Cipher.getInstance(KEY_ALGORITHM,KEY_PROVIDER);

    cipher.init(Cipher.ENCRYPT_MODE, privateKey);

    int dataSize = cipher.getOutputSize(data.length);

    int blockSize = cipher.getBlockSize();

    int blockNum = 0;

    if (data.length % blockSize == 0) {

    blockNum = data.length / blockSize;

    } else {

    blockNum = data.length / blockSize + 1;

    }

    byte[] raw = new byte[dataSize * blockNum];

    int i = 0;

    while (data.length - i * blockSize > 0) {

    if (data.length - i * blockSize > blockSize) {

    cipher.doFinal(data, i * blockSize, blockSize, raw, i * dataSize);

    } else {

    cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * dataSize);

    }

    i++;

    }

    return raw;

    }

    /**

    * 生成数字签名

    * */

    public static String sign(byte[] encoderData,PrivateKey privateKey) throws Exception {

    Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM,KEY_PROVIDER);

    sig.initSign(privateKey);

    sig.update(encoderData);

    return new String(Base64.encode(sig.sign()));

    }

    /**

    * 校验数字签名

    * */

    public static boolean verify (byte[] encoderData,String sign,PublicKey publicKey) throws Exception {

    Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM,KEY_PROVIDER);

    sig.initVerify(publicKey);

    sig.update(encoderData);

    return sig.verify(Base64.decode(sign));

    }

    展开全文
  • javalicense控制

    千次阅读 2018-03-28 18:54:15
    https://blog.csdn.net/zhangdaiscott/article/details/74344925
  • 基于Java软件产品一般都是由编译文件.class组成的发布包,源代码很容易被诸如JAD之类的工具反编译出来,据此hacker们可以很轻松的重新实现部分代码来跳过license验证。 签 于此,我们可以在编译或者类装载环节下些...
  • 在我们做系统级框架的时候,我们要一定程度上考虑系统的使用版权,不能随便一个人拿去在任何环境都能用,...1. License,通过 smart-license 生成的授权文件,导入至要授权使用的软件产品中。 2. 源数据,需要进行 L
  • 对于java程序来说,class文件很容易被反编译,所以理论上而言,对java程序做license限制,无法真正起到保护软件被盗窃的作用。但是,如果增加被反编译的成本,或者增加被反编译后能读懂源码的成本,也能从一定程度上...
  • 但是,许可证管理可能会增加非法使用程序的难度,从而可能促使用户成为客户。 许可证管理的另一个影响是法律方面。 如果有足够的许可管理,非法用户成功声称他们的使用是基于缺乏许可条件或错误了解许可条件的可能性...
  • 一般通过一个或多个唯一标识来标识软件使用方,比如(计算机Mac地址、主板序列号和CPU序列号,或者是软件安装序列号、用户UUID)。 当然License也不是一定安全,破解伪造License也是最大的安全问题。 二、流程思路 ...
  • I am having a very big ... That is "How to Create License for my software".OK, Think this is my License key - 12345YWwhen the user enter this license key , the software should allow him to use th...
  • 中国是个盗版软件横行泛滥的国度,也许这里面有我们社会主义初级阶段这个国情的因素,我们确实离不开盗版软件。... 通常可以通过license机制来保护软件产品的安全,我们可以限定只有购买了license的用户在特定...
  • 软件License设计思路与实现方案

    千次阅读 2021-04-29 09:30:36
    最近接到一个需求,需要给我们的软件系统增加一个License授权的机制,可以把软件系统部署到客户的服务器供客户试用,到期后系统就不能使用了获取产生一些限制,需要我们提供给他新的License以后才可以继续使用。...
  • if (verifyResult.getResult()) { /** 增加业务系统监听,是否自定义验证 */ List customListenerList = ACustomVerifyListener.getCustomListenerList(); boolean compare = true; for (ACustomVerifyListener ...
  • package sdk; import org.springframework.security.authentication.encoding.Md5PasswordEncoder; import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto....import java.net.
  • SpringBoot整合License实现认证

    千次阅读 2021-04-05 21:43:23
    SpringBoot整合License实现认证简介使用 ...获取客户Windows服务器的基本信息自定义LicenseManager,用于增加额外的服务器硬件信息校验License生成类,用于生成License证书自定义KeyStoreParam,用于将公私钥存储文件
  • 再或者用过一些APM工具,在JVM启动脚本上增加了-javaagent:/path/to/apm-agent.jar,就可以自动进行追踪。再或者用过Arthas之类的JVM诊断工具,这些工具都是通过Java Agent的技术去实现的。 比如上面说的“免费激活...
  • 刚开始学Java的时候,挺郁闷的,写出来的java类文件,需要dos下编译,然后再dos下运行看效果。这使初学者常常觉得麻烦,学习的...渐渐的想让java文件的执行更痛快点,不想给其他人演示个小软件还要在IDE里看效果,十...
  • Java局域网通信——飞鸽传书源代码 28个目标文件 内容索引:JAVA源码,媒体网络,飞鸽传书 Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java...
  • 常用的10种开源协议的限制条件集合,让你了解他们的限制,不再无意中侵犯版权。
  • java开源包101

    2016-07-13 10:11:08
    Java Remote Desktop 是一个Java 的远程桌面软件,支持很多特性例如文件传输、数据压缩、颜色转换、键盘鼠标事件转换等等。 最短路径算法实现 k-shortest-paths 这是一个实现了 Yen 的排名算法的无环路径的项目 ...
  • 各种License介绍

    万次阅读 2017-03-03 17:49:46
    各种license的介绍,比较全面。原文链接在https://web.liferay.com/community/forums/-/message_boards/message/4775410 什么是License 许多混乱就始于你不知道License到底是什么,到底有什么含义。当你对你的...
  • JAVA防反编译&许可授权管理

    千次阅读 2017-02-08 11:44:57
    由于项目需要,需要对发布的程序进行管理,...首先要明确的是:防反编译只是增加反编译的难度,没有办法做到完全防止。商业上要用的话可以考虑商业加密软件狗。 JAVA防反编译技术介绍 根据《两种防反编译工具》找到两
  • 在我们做系统级框架的时候,我们要一定程度上考虑系统...生成机器码我们首先要做的就是对软件部署的环境的唯一性进行限制,这里使用的是macadderss,当然你也可以换成cpu序列编号,并无太大影响,先上代码private st...
  • 关于开源软件License的说明

    千次阅读 2012-07-10 13:25:40
    Soa 竞赛中对于开源软件有如下要求: 关于开源软件(Open Source ...禁止使用任何版本的GPL/LGPL license的开源软件; 可以使用Eclipse, Apache, CPL, BSD等license的开源软件; 对使用到的任何开源软件,都需

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,952
精华内容 5,580
关键字:

java软件增加license

java 订阅