whycxzp
2025-03-27 fe09517300727fd547e9926c9ba407386715435d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
package com.whyc.controller;
 
import com.whyc.constant.YamlProperties;
import com.whyc.dto.Response;
import com.whyc.encryption.ByteConvertUtil;
import com.whyc.encryption.SM2;
import com.whyc.pojo.License;
import com.whyc.service.LicenseService;
import com.whyc.util.AESUtil;
import com.whyc.util.ActionUtil;
import com.whyc.util.SerialNumberUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.bouncycastle.math.ec.ECPoint;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.*;
 
import javax.annotation.Resource;
import java.io.*;
import java.math.BigInteger;
 
@RequestMapping("license")
@RestController
@Api(tags = "注册码验证")
public class LicenseController {
    @Resource
    private LicenseService service;
    /**
     * 类加载时初始化sm2的公私钥
     */
    final static ECPoint publicKey = SM2.getPublicKey();
    final static BigInteger privateKey = SM2.getPrivateKey();
 
 
    /**
     * 检验服务器是否注册,是否已存在序列号
     *  目前需求,校验码发放带上有效期
     *  在校验的时候,需要校验有效期,首次通过的时候存储到数据库,存储解析后的序列号和有效时长(示例:30天),预留字段已经使用时长
     *  因为凭证是针对应用的,所以一个项目数据库中只会保存有一个凭证
     */
    @GetMapping("/checkRegistered")
    @ApiOperation(value = "校验服务器是否注册")
    public Response checkRegistered(){
        if(YamlProperties.profileType.contains("dev")){
            return new Response().set(1, true);    //测试环境
        }
        return service.checkLicenseExpired();
    }
 
    /**
     * 方式一: 获取 序列号的时候,需要在5分钟内输入注册码
     * 方式二: 每次注册码生成,校验通过,放置到数据库中,最新存入数据库的生效,其余都失效,license表存在一个flag字段
     *
     * 当前采用:方式一
     */
 
    /**
     * 获取序列号license
     *
     */
    @GetMapping("/getSerialNumberLicense")
    @ApiOperation(value = "获取序列号license")
    public Response getSerialNumberLicense(){
        //序列号加密 license
        Response model=LicenseController.createLicense(System.currentTimeMillis()+"createTime"+ SerialNumberUtil.getSerialNumber());
        //同时,将序列号生成时间记录到application域中
        //getApplication().setAttribute("serialNumberLicenseTime",System.currentTimeMillis());
        //ActionUtil.getApplication().setAttribute("serialNumberLicenseTime",System.currentTimeMillis());
        return model;
    }
 
    //获取一个license
    public static Response createLicense(String serialNumber){
        //初始化sm2参数x
        SM2 x = new SM2();
        /*String realPath = ClassUtils.getDefaultClassLoader().getResource("").getPath();
        System.out.println("realPath: "+realPath);
        BigInteger privKey = x.importPrivateKey(realPath+"config/pri_key.ksm");
        ECPoint pubKey = x.importPublicKey(realPath+"config/pub_key.ksm");*/
 
        //旧版本
        /*String fileDirName=LicenseController.getRealPath("pub_key.ksm");
        ECPoint pubKey = x.importPublicKey(fileDirName+"/pub_key.ksm");*/
        //System.out.println("pubKey "+pubKey);
        /*String origin = "Company: Fuguang Electronic\n"
                + "Project:BTS monitor platform\n"
                + "Licence type:Permanent";*/
        //获取加密列表
        //System.out.println("origin "+origin);
        //byte[] encryptResult = x.encrypt(serialNumber, pubKey);
        byte[] encryptResult = x.encrypt(serialNumber, publicKey);
        String encrypt = ByteConvertUtil.bytesToHexString(encryptResult);
        //System.out.println("encrypt:"+encrypt);
        return new Response().set(1,encrypt);
    }
 
    /**
     * 校验license
     * 验证成功添加到数据库web_site.tb_license中
     */
    @GetMapping("/checkSerialNumberLicense")
    @ApiOperation(value = "校验license")
    public Response checkSerialNumberLicense(@RequestParam String license){
        Response model = new Response();
 
        //获取macid是否一致
        boolean res = false;
        //初始化sm2参数x
        SM2 x = new SM2();
        //String realPath = ClassUtils.getDefaultClassLoader().getResource("").getPath();
        //ECPoint pubKey = x.importPublicKey(realPath + "config/pub_key.ksm");
        /*//旧版本
        String fileDirName=LicenseController.getRealPath("pri_key.ksm");
        BigInteger privKey = x.importPrivateKey(fileDirName + "/pri_key.ksm");*/
        String origin = "Company: Fuguang Electronic\n"
                + "Project:BTS monitor platform\n"
                + "Licence duration:";
        //获取解密后license,附带校验license编码格式
        String decryptResult = null;
        byte[] bytes = ByteConvertUtil.hexToByteArray(license);
        decryptResult = x.decrypt(bytes, privateKey);
        //用户只能往小调时间
        String[] split1 = decryptResult.split("machineCode:");
        Long registerCodeTime = Long.valueOf(split1[0]);
        Long machineCodeCreateTime = Long.valueOf(split1[1].split("createTime")[0]);
        //检验解码后license的值是否正确
        String serialNumberStr = SerialNumberUtil.getSerialNumber() + "@@" + origin;
        res = decryptResult.contains(serialNumberStr);
        if (res) {
            //校验license输入时间是否超过5分钟,5分钟内则存储到数据库中
            long currentTimeMillis = System.currentTimeMillis();
            //long serialNumberLicenseTime = (long) getApplication().getAttribute("serialNumberLicenseTime");
            //if(currentTimeMillis-serialNumberLicenseTime>=5*60*1000){
            if (currentTimeMillis - machineCodeCreateTime >= 5 * 60 * 1000) {
                model.setCode(1);
                model.setData(false);
                model.setMsg("注册码获取及输入超过5分钟,请重新获取注册码");
            } else if (registerCodeTime > currentTimeMillis) {
                model.setCode(1);
                model.setData(false);
                model.setMsg("系统时间不正常,请检查");
            } else {
                model.setCode(1);
                model.setData(true);
                model.setMsg("注册码校验成功");
            }
            //model.setData(decryptResult);
            //model.setMsgN(serialNumberStr);
            double duration = Integer.parseInt(decryptResult.split("duration:")[1]);
            License data = new License();
            data.setSerialNumber(SerialNumberUtil.getSerialNumber());
            //duration和TimeInUse加密存入数据库
            String durationEncrypt = AESUtil.aesEncrypt(String.valueOf(duration));
            data.setDuration(durationEncrypt);
 
            String timeInUseEncrypt = AESUtil.aesEncrypt(String.valueOf(0));
            data.setTimeInUse(timeInUseEncrypt);
            //将序列号,有效期 添加入license表
            service.add(data);
        } else {
            model.setCode(1);
            model.setData(false);
            model.setMsg("验证失败");
            //model.setData(decryptResult);
            //model.setMsgN(serialNumberStr);
        }
       /* model.setCode(1);   //测试环境
        model.setData(true);*/
        return model;
    }
 
    @PostMapping("/time2DeadLine")
    @ApiOperation(value = "有效期检验")
    public Response time2DeadLine(){
        return service.time2DeadLine();
    }
 
 
    //将pri_key.ksm。pub_key.ksm文件拷贝至ksm文件下然后读取fileName:/config/pri_key.ksm
    public static String getRealPath(String fileName){
        //过滤特殊字符,避免路径遍历攻击
        fileName = ActionUtil.filterFileName(fileName);
        ClassPathResource classPathResource = new ClassPathResource("/config/"+fileName);
        InputStream inputStream_pub = null;
        FileOutputStream fos=null;
        ApplicationHome applicationHome = new ApplicationHome(LicenseController.class);
        File jarFile = applicationHome.getDir();
        String fileDirName = jarFile.getParentFile().toString()+ File.separator+"ksm";
        //String fileDirName = jarFile.toString()+File.separator+"ksm";//打包版本
        createFile(fileDirName);//创建文件夹ksm
        try {
            inputStream_pub = classPathResource.getInputStream();
            fos = new FileOutputStream(fileDirName+"/"+fileName);
            byte[] b = new byte[1024];
            int length;
            while((length = inputStream_pub.read(b))>0){
                fos.write(b,0,length);
            }
 
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(inputStream_pub!=null){
                try {
                    inputStream_pub.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return fileDirName;
    }
    public static void createFile(String pathName) {
        //过滤特殊字符,避免路径遍历攻击 
        pathName = ActionUtil.filterFileName(pathName);
        File dir = new File(pathName);
        if (!dir.exists()) {// 判断目录是否存在
            dir.mkdir();
        }
    }
 
}