JAVA生成序列号(制作定向软件许可,许可专机专用)

一、生成序列号思路简述

使用非对称加密技术RSA实现

对RSA不了解的可先看上一篇:Java对称与非对称加密解密(AES与RSA)。

第一步:使用RSA生成一对密钥对;

第二步:将第一步生成的私钥保存至服务端,此私钥和客户端一一对象。公钥提供至客户端;

第三步:服务端使用第一步生成的私钥、客户端MAC地址、有效结束时间 三个信息生成序列号;

第四步:服务端将第三步生成的序列号提供至客户端;

第五步:客户端将序列号配置进系统;

第六步:访问客户端系统时,客户端使用公钥对序列号进行解释,解释出来的MAC地址和有效结束时间,之后使用解释出来的MAC地址和本机MAC地址做校验,将有效结束时间和当前时间做比较。当MAC地址和本机地址匹配并且有效结束时间大于当前时间时允许访问系统和访问资源,否则禁止访问并提示序列号过期。

二、JAVA实现方式

RSA加密工具类封装

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import org.apache.commons.codec.binary.Base64;
import java.security.KeyPair;

/**
 * 〈Function overview〉<br>
 *
 * @className: RSA
 * @package: com.soft.team.base.encryption
 * @author: yuanzf
 * @date: 2022/3/16 15:02
 */

public class RSAUtil {
    private String publicKey;
    private String privateKey;

    /*有参构造方法,加解密时使用。公钥加密需用私钥解密,私钥加密需用公钥解密*/
    public RSAUtil(String publicKey,String privateKey){
        this.publicKey = publicKey;
        this.privateKey = privateKey;
    }

    /*无参构造方法,新建密钥对时使用*/
    public RSAUtil(){
        KeyPair pair = SecureUtil.generateKeyPair("RSA");
        publicKey = new String(Base64.encodeBase64(pair.getPublic().getEncoded()));
        privateKey= new String(Base64.encodeBase64((pair.getPrivate().getEncoded())));
    }

    /**
     *
     * @param str 加密前数据
     * @return 返回加密后数据
     */
    public String encrypt(String str){
        return SecureUtil.rsa(privateKey,publicKey).encryptBcd(str, privateKey!=null?KeyType.PrivateKey:KeyType.PublicKey);
    }

    /**
     *
     * @param str 加密后的数据
     * @return 返回解密后数据
     */
    public String decrypt (String str){
        return SecureUtil.rsa(privateKey,publicKey).decryptStrFromBcd(str, privateKey!=null?KeyType.PrivateKey:KeyType.PublicKey);
    }

    public String getPublicKey() {
        return publicKey;
    }
    public String getPrivateKey() {
        return privateKey;
    }
}

序列号属性实体类

import lombok.Data;

/**
 * 序列号属性实体类
 *
 * @author yuanzf
 */
@Data
public class Serial {
    /*生成序列号时使用*/
    public Serial(String privateKey,String mac,String effectiveEndTime){
        this.privateKey = privateKey;
        this.effectiveEndTime = effectiveEndTime;
        this.mac = mac;
    }
    /*序列号解密时使用*/
    public Serial(String publicKey,String serialNumber){
        this.publicKey = publicKey;
        this.serialNumber = serialNumber;
    }
    /*序列号*/
    private String serialNumber;
    /*公钥*/
    private String publicKey;
    /*私钥*/
    private String privateKey;
    /*有结束时间(13位时间戳)*/
    private String effectiveEndTime;
    /*MAC地址*/
    private String mac;
}

序列号生成、解释工具类封装

import com.soft.team.base.encrypt.RSAUtil;
import com.soft.team.entity.Serial;

/**
 * 序列号工具类
 * @author yuanzf
 *
 */
public class SerialUtil {
    /**
     * 根据私钥、MAC地址、有效期生成序列号
     * @param serial 需有privateKey、mac、effectiveEndTime(13位时间戳)值
     * @return serial 含有privateKey、mac、effectiveEndTime、serialNumber值
     * @throws Exception
     */
    public static Serial generateSerialNumber(Serial serial) {
        RSAUtil rsa = new RSAUtil(null,serial.getPrivateKey());
        String serialNumber = rsa.encrypt(serial.getMac()+","+serial.getEffectiveEndTime());
        serial.setSerialNumber(serialNumber);
        return serial;

    }

    /**
     * 根据序列号、公钥校验序列号是否有效
     * @param serial 需有publicKey、serialNumber
     * @return serial 含有publicKey、mac、effectiveEndTime、serialNumber值
     * @throws Exception
     */
    public static Serial explainSerialNumber(Serial serial) {
        RSAUtil rsa = new RSAUtil(serial.getPublicKey(),null);
        String[] serialList = rsa.decrypt(serial.getSerialNumber()).split(",");
        serial.setMac(serialList[0]);
        serial.setEffectiveEndTime(serialList[1]);
        return serial;

    }
}

测试入口类

import com.soft.team.base.encrypt.LocalMac;
import com.soft.team.base.encrypt.RSAUtil;
import com.soft.team.base.util.SerialUtil;
import com.soft.team.entity.Serial;
import org.junit.Test;
import java.net.InetAddress;
import java.sql.Timestamp;

/**
 * 序列号生成、验证入口类
 * @author: yuanzf
 * @date: 2022/3/16 11:37
 */
public class SerialUtilTest {
    /**
     * 测试方法
     * 生成rsa密钥对,需保存密钥对,后边生成序列号和解释序列号需要用到
     */
    @Test
    public void getKeyPairStart(){
        /*提前生成好rsa密钥对,私钥保存至服务端,公钥写入至客户端*/
        RSAUtil rsa = new RSAUtil();
        System.out.println("我是rsa公钥,请将我保存:"+rsa.getPublicKey());
        System.out.println("我是rsa私钥,请将我保存:"+rsa.getPrivateKey());
    }
    /**
     * 测试方法
     * 生成序列号,生成后将序列号提供至客户端进行配置
     */
    @Test
    public void generateStart(){
        /*new一个序列类,构造时传入私钥、MAC地址、有效结束时间*/
        Serial serial = new Serial("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAId/DMsz04nlSLLSfppYTXRuw2j+H1xo2r65/SbvR4TwtI8kRL8vsSdBcYh3tqtoGZqFG044poYY6PWsOUisWqJNbbTuwqWfIJidnUpzjaQP8Z8RuTxr332AOK0H6/apziqK8kmVH/893N1R1V8fYbJorr83WPTeBCmE7fZWAXpHAgMBAAECgYAreZR9Tq+9OxhMaEW++D5B8Zg5g/BEElC9iae0amokOad1lkmInqDU26a2BtNRxCES90p/mqWzuSJmUVBABS0/Fl6zb39MoikRJ0fh5him35LJiFANI9GxxGnkbiahny0RKnMj42vr9id3s0ipXjHYuS8YXD9MLKVRNPUQCw47oQJBAMvppDmUTRPGyHQvoRpvhffrZa0dHCxVqojvfDtCxUmHWI9EdCHyYxR8OVQHDDif3ZYta2CkT6KtprD/4wV+kbsCQQCqG32qMOBkIQpyp+/7vAewxDnwi7KJFcKHh83+JgGInRtsjg4NpbRKQUHVYhABc+FyVLPHnR6iUFpIxmyGonrlAkEAq0e422i3iZoavIVZdIQi6sl+4XenN5JJqbZICtseLpISkFz2k6EvGoDyAqPc3x9hmIjUPhwmjEYC04BNKEtViwI/EfDUUB7Xi7fwYidUKDislvgbJEOXkN26ppCsKSHZB4+KVOimksnnOe2oA9lT1tNh86z7SRJJKNVQsFw9FfYNAkEAsvcLtghgOhgVafOpA2u9E0nYqOk7vA5oXTF8tONvJ5n4MKw52bacnX6qMQBmsJElxjvYd2zSd8XD0FzIthdw3g==","20-79-18-29-2C-B4","1650244811000");
        /*生成序列号,参数是引用类型,调用生成序列号方法后可直接从对象中获取序列号*/
        SerialUtil.generateSerialNumber(serial);
        /*获取序列号*/
        String serialNumber = serial.getSerialNumber();
        System.out.println("我是生成的序列号,请发送至客户端:"+serialNumber);
    }
    /**
     * 测试方法
     * 校验序列号是否有效
     */
    @Test
    public void explainStart(){
        try {
            /*new一个序列类,构造时传入公钥、序列号*/
            Serial serial = new Serial("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHfwzLM9OJ5Uiy0n6aWE10bsNo/h9caNq+uf0m70eE8LSPJES/L7EnQXGId7araBmahRtOOKaGGOj1rDlIrFqiTW207sKlnyCYnZ1Kc42kD/GfEbk8a999gDitB+v2qc4qivJJlR//PdzdUdVfH2GyaK6/N1j03gQphO32VgF6RwIDAQAB","13935EF2BE2C07D50FD7C521E682FD937FAF563C4374336F5D5388B502F7D5EBA2E63FCAAB280B6FA9040AAACC00D2FB035A1D6BAEEACDD7EF9B53A96CC0C8105CA2FCEC5071C65307544E4F4E1A785A27C74EE08A4325011A9919968C18DEB673DA9F3604830B5A54895B0B51E23657111BB0F16D47E39FD1D038DA14CD5C66");
            /*解释序列号,参数是引用类型,调用解释序列号方法后可直接从对象中获取MAC地址、有效结束时间*/
            SerialUtil.explainSerialNumber(serial);
            /*获取MAC地址*/
            String mac = serial.getMac();
            /*获取有效结束时间*/
            String effectiveEndTime = serial.getEffectiveEndTime();
            /*获取本机MAC地址*/
            String localMac = LocalMac.getLocalMac(InetAddress.getLocalHost());
            /*获取当前时间戳*/
            long time = new Timestamp(System.currentTimeMillis()).getTime();
            /*判断有效结束时间是否大于当前时间*/
            if(mac.equals(localMac)&&Long.parseLong(effectiveEndTime)>time){
                System.out.println("当前序列号有效,可正常使用系统与访问资源");
            }else{
                System.out.println("当前序列号无效,请向厂商购买新的序列号");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public synchronized String nextId() { long timestamp = timeGen(); //获取当前毫秒数 //如果服务器时间有问题(时钟后退) 报错。 if (timestamp < lastTimestamp) { throw new RuntimeException(String.format( "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } //如果上次生成时间和当前时间相同,在同一毫秒内 if (lastTimestamp == timestamp) { //sequence自增,因为sequence只有12bit,所以和sequenceMask相与一下,去掉高位 sequence = (sequence + 1) & sequenceMask; //判断是否溢出,也就是每毫秒内超过4095,当为4096时,与sequenceMask相与,sequence就等于0 if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); //自旋等待到下一毫秒 } } else { sequence = 0L; //如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加 } lastTimestamp = timestamp; long suffix = (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; String datePrefix = DateFormatUtils.format(timestamp, "yyyyMMddHHMMssSSS"); return datePrefix + suffix; } protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } protected long timeGen() { return System.currentTimeMillis(); } private byte getLastIP(){ byte lastip = 0; try{ InetAddress ip = InetAddress.getLocalHost(); byte[] ipByte = ip.getAddress(); lastip = ipByte[ipByte.length - 1]; } catch (UnknownHostException e) { e.printStackTrace(); } return lastip; }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值