解密与加密设计思路

加密:
采用AES对称加密、解密
7位数: 32进制序列(4位) + 密钥类别(2位)+ 有效时长(1位)
加密后密钥为11位
4位数:前三位,先获取一个(0到2500)的随机数,然后再乘11,接着转换为三位的32进制数,然后最后一位是(机器版本号),
最后 3位+1位 生成4位数
预想15位密钥
11位+4位
接着密钥打乱顺序混淆

混淆策略:先分别获取激活码的奇数位和偶数位,然后将奇数位和偶数位拼接获得混淆后的激活码
奇数位+偶数位

解密:
(1) 解除混淆(将混淆后的激活码进行重组复原)
(2) 校验密钥后四位;校验成功继续下一步操作,校验失败密钥无效
(3) 只有校验成功才能对前十一位密钥进行解密;校验失败密钥无效
(4) 解密成功,说明是有效密钥,获取密钥信息,根据信息对客户端进行相应操作;解密失败,说明密钥无效
(5) 无论解密成功与否给服务端发请求,通知服务端,然后进行相应的操作和记录

其中:密钥类别(2位)可以用来表示该激活码用来激活哪些设备或者哪些平台(如01表示某个平台,02表示某个app),时长(1位)用来表示该激活码的有效时长(如0表示永久、1表示7天、2表示30天等)
注意:前7位数加密后为11位,表示该激活码可以生成的个数;后4位数为随机数 * 11转32进制和混淆策略是为了激活码的加密性,用来校验该激活码是否有效

因此,该激活码的加密主要体现在三个地方:

  • 混淆策略

  • 32禁止转18进制后能否被11整除

  • AES对称加密、解密

解密与加密工具类

CDKeyUtil.java

import java.util.Random; 
 
/** 
 * Created by tao. 
 * Date: 2021/6/28 16:43 
 * 描述: 
 */ 
public class CDKeyUtil { 
 
    //机器版本号 
 
    /** 
     * 激活码生成方法 
     * 
     * @param category 密钥类别(固定两位数字) 
     * @param deadline 使用期限(固定一位字符) 
     * @return 返回的激活码 
     */ 
    public static String createCDkey(String category, String deadline, String machineVersion) throws Exception { 
        String CDKey = ""; 
        //1. 获取前四位 
        String sequence = getSequence(); 
        //2. 生成前七位 
        String plaintext = sequence + category + deadline; 
        //3.对明文进行加密 
        CDKey = CDKeyEncryptUtils.AESencrypt(plaintext).substring(0, 11); 
        //4.获取后四位 
        String rulesSequence = CDKeyUtil.getRulesSequence(machineVersion); 
        //5.混淆操作 
        CDKey = CDKey + rulesSequence; 
        CDKey = confusion(CDKey); 
        //6.得到激活码 
        return CDKey; 
    } 
 
 
    /** 
     * 激活码解码方法 
     * 
     * @param CDKey 激活码 
     * @return 返回激活码明文 
     */ 
    public static String deCDkey(String CDKey, String machineVersion) throws Exception { 
        //1. 解除混淆 
        String deConfusion = deConfusion(CDKey); 
        //2. 提取后四位序列(第1位版本号,后三位校验其规则) 
        String sequence = deConfusion.substring(deConfusion.length() - 4); 
        //3. 获取后三位序列并且转为10进制,和版本号 
        String randomInt = sequence.substring(1); 
        String version = sequence.substring(0, 1); 
        int to10 = Integer.parseInt(change32To10(randomInt)); 
        //4. 根据既定规则校验激活码是否正确 
        if (to10 % 11 == 0 && version.equals(machineVersion)) { 
            //1. 如果后四位序列校验正确,则对激活码进行解密操作 
            String secretKey = deConfusion.substring(0, 11); 
            String code = ""; 
            try { 
                code = CDKeyEncryptUtils.AESdecrypt(secretKey); 
            } catch (Exception e) { 
                e.printStackTrace(); 
                return "激活码错误"; 
            } 
            return code; 
        } else { 
            return "激活码错误"; 
        } 
 
    } 
 
 
    /** 
     * 获得激活码前四位序列方法 
     * 
     * @return 返回激活码前四位序列 
     */ 
    public static String getSequence() { 
        String sequence = ""; 
        //1. 获取随机数 
        int randomInt = getRandomInt(); 
        //2. 转32进制 
        String to32 = change10To32(randomInt + ""); 
        //3. 补全四位 
        int len = to32.length(); 
        if (len < 4) { 
            for (int i = 0; i < 4 - len; i++) { 
                to32 = "0" + to32; 
            } 
        } 
        sequence = to32; 
        return sequence; 
    } 
 
    /** 
     * 获得激活码后四位规则序列方法 
     * 
     * @param machineVersion 机器版本号 
     * @return 返回激活码后四位规则序列 
     */ 
    public static String getRulesSequence(String machineVersion) { 
        String rulesSequence; 
        //1. 按照规则获取前三位 
        /*int randomInt = new Random().nextInt(8); 
        String randomStr = randomInt + "" + (randomInt + 1) + (randomInt + 2);*/ 
 
        //1. 按照规则获取前三位 
        int randomInt = new Random().nextInt(2500); 
        String randomStr = (randomInt * 11) + ""; 
        //2. 转32进制 
        String to32 = change10To32(randomStr); 
        //3. 补全三位 
        int len = to32.length(); 
        if (len < 3) { 
            for (int i = 0; i < 3 - len; i++) { 
                to32 = "0" + to32; 
            } 
        } 
        //4.拼接第四位 
        rulesSequence = machineVersion + to32; 
        return rulesSequence; 
    } 
 
    /** 
     * 激活码混淆方法 
     * 奇数位+偶数位 
     * 
     * @return 返回激活码混淆后的序列 
     */ 
    public static String confusion(String CDKey) { 
        String deCDKey = ""; 
        //1.获取奇数位字串 
        String odd = ""; 
        for (int i = 0; i < CDKey.length(); i = i + 2) { 
            odd = odd + CDKey.charAt(i); 
        } 
        //2.获取偶数位字串 
        String even = ""; 
        for (int i = 1; i < CDKey.length(); i = i + 2) { 
            even = even + CDKey.charAt(i); 
        } 
        //3.拼接 
        deCDKey = odd + even; 
        return deCDKey; 
    } 
 
    /** 
     * 激活码解除混淆方法 
     * 
     * @return 返回激活码解除混淆后的序列 
     */ 
    public static String deConfusion(String deCDKey) { 
        String CDKey = ""; 
        //1. 拆分 
        int oddCount = (deCDKey.length() / 2) + (deCDKey.length() % 2); 
        String odd = deCDKey.substring(0, oddCount); 
        String even = deCDKey.substring(oddCount); 
        //2. 复原激活码 
        if (odd.length() == even.length()) { 
            for (int i = 0; i < odd.length(); i++) { 
                CDKey = CDKey + odd.charAt(i) + even.charAt(i); 
            } 
        } else { 
            for (int i = 0; i < even.length(); i++) { 
                CDKey = CDKey + odd.charAt(i) + even.charAt(i); 
            } 
            CDKey = CDKey + odd.charAt(odd.length() - 1); 
        } 
        return CDKey; 
    } 
 
    /** 
     * 10进制转32进制的方法 
     * num 要转换的数 from源数的进制 to要转换成的进制 
     * 
     * @param num 10进制(字符串) 
     * @return 转换结果的32进制字符串 
     */ 
    public static String change10To32(String num) { 
        int from = 10; 
        int to = 32; 
        return new java.math.BigInteger(num, from).toString(to); 
    } 
 
    /** 
     * 32进制转10进制的方法 
     * num 要转换的数 from源数的进制 to要转换成的进制 
     * 
     * @param num 10进制(字符串) 
     * @return 转换结果的10进制字符串 
     */ 
    public static String change32To10(String num) { 
        int f = 32; 
        int t = 10; 
        return new java.math.BigInteger(num, f).toString(t); 
    } 
 
    /** 
     * 生成[min, max]之间的随机整数 
     * min 最小整数(固定0) 
     * max 最大整数(固定1000000) 
     * 
     * @return 返回min———max之间的随机数 
     * @author tao 
     */ 
    public static int getRandomInt() { 
        int min = 0; 
        int max = 1000000; 
        return new Random().nextInt(max) % (max - min + 1) + min; 
    } 
 
 
    /* 
     * 枚举日期,返回天数 
     */ 
    public static int duetimeEnum(String code) { 
        switch (code) { 
            case "0": 
                return 36500; 
            case "1": 
                return 7; 
            case "2": 
                return 30; 
            case "3": 
                return 90; 
            case "4": 
                return 180; 
            case "5": 
                return 365; 
            default: 
                return 30; 
        } 
    } 
}

其中用到AES加密和解密:CDKeyEncryptUtils.java

import org.apache.commons.codec.binary.Base64; 
 
import javax.crypto.Cipher; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 
 
/** 
 * Created by tao. 
 * Date: 2021/6/28 16:37 
 * 描述: 
 */ 
public class CDKeyEncryptUtils { 
	    //--------------AES--------------- 
	    private static final String KEY = "12055296";  // 密匙,必须16位 
	    private static final String OFFSET = "12055296"; // 偏移量 
	    private static final String ENCODING = "UTF-8"; // 编码 
	    private static final String ALGORITHM = "DES"; //算法 
	    private static final String CIPHER_ALGORITHM = "DES/CBC/PKCS5Padding"; // 默认的加密算法,CBC模式 
 
 
	    public static String AESencrypt(String data) throws Exception { 
	        //指定算法、获取Cipher对象(DES/CBC/PKCS5Padding:算法为,工作模式,填充模式) 
	        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); 
	        //根据自定义的加密密匙和算法模式初始化密钥规范 
	        SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM); 
	        //CBC模式偏移量IV 
	        IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes()); 
	        //初始化加密模式 
	        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); 
	        //单部分加密结束,重置Cipher 
	        byte[] encrypted = cipher.doFinal(data.getBytes(ENCODING)); 
	        //加密后再使用BASE64做转码 
	        return new Base64().encodeToString(encrypted); 
	    } 
 
	    /** 
	     * AES解密 
	     * 
	     * @param data 
	     * @return String 
	     * @author tao 
	     * @date 2021-6-15 16:46:07 
	     */ 
	    public static String AESdecrypt(String data) throws Exception { 
	        //指定算法、获取Cipher对象(DES/CBC/PKCS5Padding:算法为,工作模式,填充模式) 
	        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); 
	        //根据自定义的加密密匙和算法模式初始化密钥规范 
	        SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM); 
	        //CBC模式偏移量IV 
	        IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes()); 
	        //初始化解密模式 
	        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); 
	        //先用base64解码 
	        byte[] buffer = new Base64().decode(data); 
	        //单部分加密结束,重置Cipher 
	        byte[] encrypted = cipher.doFinal(buffer); 
	        return new String(encrypted, ENCODING); 
	    } 
}

其中AES的key为12055296,设置为8位,则机密后的密文则为11位,加密算法为 “DES”

激活码生成测试

 public static void main(String[] args) throws Exception { 
        for (int i = 0; i < 10; i++) { 
            String CDKey = CDKeyUtil.createCDkey("01", "0", "1"); 
            System.out.println("激活码:" + CDKey); 
            String deCDkey = CDKeyUtil.deCDkey(CDKey, "1"); 
            System.out.println("激活码解密:" + deCDkey); 
        } 
 
    }

执行结果:

怎么使用java生成激活码和密钥


本文参考链接:https://www.yisu.com/zixun/696257.html
评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

Java怎么实现简单的五子棋游戏