IDEA2022.2
JDK18
⽬前⼤部分商业软件在提供注册码时,基本上采⽤了以下⼏种机制来实现:
- 远程联⽹激活。在软件每次启动时,都会联⽹检查软件使⽤情况,检查使⽤时间是否 到期,是否是多设备使⽤等。这种⽅式控制是⾮常棒的,动态性、实时性都⾮常好, 但劣势也⾮常明显,就是客户机软件要联⽹,如若没有联⽹就⽆法控制了。
- 本地⽣成注册。这种⽅式是根据客户机的环境,获取客户机的信息,⽐如硬盘、 MAC 地址 、 CPU 等硬件信息,根据⼀定的算法将这些信息⽣成⼀个注册码。⽬前超过⼀半 的软件都是采⽤这种⽅式来实现的,这种⽅式的缺点是不能⾃由地控制软件的其它参 数,⽐如软件中可添加设备的数量。
- 配套密钥⽂件。在软件发⾏的过程中,⽤软件运⾏到期时间、运⾏数量限制和已运⾏ 时间等参数⽣成⼀个密钥⽂件,配套发送给⽤户使⽤。在软件启动时,直接加载这个 密钥⽂件进⾏检查。这种⽅式的缺点在于密钥⽂件的参数选择上不好把控,若只仅仅 设置运⾏到期时间,⽤户可以轻松修改电脑时间来获取更⻓使⽤时间(在不联⽹同步 时间的情况下)。
而本项目采用的方法是本地生成注册, 即一机一码注册方式, 根据唯一的机器码对应生成注册码。
项目可以按应用端分成两个部分:
- 客户端(提供机器码的部分)
- 注册机(根据机器码计算其对应注册码)
核心技术是通过JAVA类库获取硬盘, CPU, 主板SN码 和 MAC 地址, 分别截取其中一部分字符拼接起来得到该机器的机器码。注册码则是在该机器机器码后面加上长度为8位的日期(如20221206)作为过期时间。验证过程是将接受到的注册码分离出机器码+日期两部分, 比对机器码与本机机器码是否一致, 若一致则注册码成立。
若采用明文传输, 用户可以很简单的修改后面的日期以达到无限注册, 所以需要采取加密手段保证验证码的保密性。我们采取RSA+BASE64加密来实现。
而对于如何实现在打开时自动读取之前的注册状态, 不需要重复注册的功能, 我们采用生成密钥配置文件, 即在软件目录下生成config.properties
文件, 在软件启动时读取其中保存的注册码, 再次进行验证操作。
.
├── main/java/javaproject
│ ├── authen // 验证模块
│ ├── machine // 获取机器码模块
│ ├── menu // 前端模块
├── resources // 存放资源文件, 和验证状态保存
├── pom.xml
├──authen // 验证模块
│ ├── client // 客户端验证方法
│ ├── server // 注册机验证方法
该类中存放客户端会用到的静态方法。
包含以下内容:
- 将传入的密文注册码用公钥解密并返回明文注册码(机器码+日期)
public static String decodeAthuncode(String authencode)
- 传入一个字符串日期"20221023", 判断是否过期, 即在当天之前
public static boolean isExpired(String date)
- 将从注册码中提取的机器码与本机机器码进行比对
public static boolean isValid(String deCodeMachineCode, String machineCode)
- 得到明文注册码的机器码部分
public static String getMachineCode(String deCode)
- 得到明文注册码的日期部分
public static String getDate(String deCode)
- 将字符串形式的日期转换成
Date
对象
public static Date stringToDate(String date)
- 得到两个日期之间差距天数, 用于实现显示“注册剩余时间"
public static int getTimeDistance(Date beginDate , Date endDate )
使用关系如下:
该类存放注册机所需要的静态方法。
包含以下内容:
- 将明文通过私钥加密为密文
public String getAuthencode(String orignal_data)
- 将输入的日期和机器码拼接为注册码
public String getOrignal_data(int year, int month, int day, String machine)
使用关系如下:
├── machine // 验证模块
│ ├── CPU // 获取CPU SN码
│ ├── DiskUils // 获取硬盘 SN码
│ ├── MotherboardSN // 获取主板 SN码
│ ├── MacTools // 获取MAC码
│ ├── Machine // 将获取到的码汇总截取一部分组成机器码
通过Runtime.getRuntime().exec()
方法, 执行CMD命令 wmic cpu get ProcessorId
之类并通过Scanner
读取process.getInpuStream
流的结果。
该CMD命令效果:
BFEBFBFF000806D1
就是我们需要的SN码。
采用运行vbs脚本的方法来获取。创建一个临时的vbs文件如下:
Set objFSO = CreateObject("Scripting.FileSystemObject") '利用FSO对象操作
Set colDrives = objFSO.Drives '返回可用的驱动器列表
Set objDrive = colDrives.item(drive) '获取drive驱动器的信息
Wscript.Echo objDrive.SerialNumber '控制台输出SN码
然后通过上一步类似的方法Runtime.getRuntime().exec()
, 来进行调用并获取结果。
一样使用vbs脚本来获取。
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * from Win32_BaseBoard")
For Each objItem in colItems
Wscript.Echo objItem.SerialNumber
exit for ' do the first cpu only!
Next
然后通过上一步类似的方法Runtime.getRuntime().exec()
, 来进行调用并获取结果。
NetworkInterface是在JDK1.4是添加的一个类,使用 getNetworkInterfaces
方法即可得到当前机器上所有的网络接口。
细节请查看docx目录下的MAC physical address.md文件。
- 获取机器信息并拼接成一个机器码返回
public String getMachineCode()
使用关系如下:
├── menu // 前端模块
│ ├── clientMenu // 客户端功能实现
│ ├── css // 设定窗口大小
│ ├── serverMenu // 注册机功能实现
使用JFrame类库实现GUI界面, 采用Group对齐方式排版, 同时使用FlatLaf类库来美化界面。
运行流程如图:
根据机器码和设定的到期日期来计算出注册码