Skip to main content
头部广告图片
  主页 > 体育热点

迷你世界激活码生成器(常用编码方式、加密算法以及token生成)

2022-12-07 浏览:

1. 常用加密算法

这部分会分别介绍消息摘要算法、对称加密算法、非对称加密算法,关于具体的密码学原理不做说明,而是根据java提供的API看看如何使用这些加密算法。关于这些加密算法相信你的项目可能或多或少会用到,比如保存用户的密码肯定会用到,小点的公司至少会直接用到消息摘要算法。大公司可能专门提供加密功能的服务,应用服务器来调用该加密服务来对信息进行加密。

1.0. Base64/Hex

Base64编码 : 网上一搜Base64就会出现大量的Base64加密解密字眼,其实Base64只是一种编码方式,用以将字节数组转换为易读的字符串,并不是什么加密算法。只是Base64会经常配合加密算法一起使用,使用加密算法得到的都是字节数组,如果使用new String(bytes)十有八九会得到这样����р�j���8l[难以阅读和保存的字符串,如果使用Base64对字节数组进行编码就会得到这样的一串字符串XrY7u Ae7tCTyyK7j1rNww==, 推荐使用commons-codec库,依赖如下:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>
private static String base64Encode(byte[] data) throws Exception {
    String base64String = Base64.encodeBase64String(data);
    return base64String; // 比如"hello world" -> aGVsbG8gd29ybGQ=
}

private static byte[] base64Decoder(String base64Data) throws Exception{
    byte[] bytes = Base64.decodeBase64(base64Data);
    return bytes;
}

// Hex是16进制编码方式,和Base64类似  6c91ad368187c02bbbddc2f09ff2a87e
private static String hex(byte[] data) throws Exception{
    return Hex.encodeHexString(data);
}

1.1. 消息摘要

​ 消息摘要算法使用HASH函数对消息进行加密后,会产生长度固定的加密串,称为摘要。接收者会对接收到的消息使用相同的HASH函数重新计算,将产生的新的摘要和原来的摘要进行对比,如果不一致说明消息在传递过程中被篡改了。


迷你世界激活码生成器(常用编码方式、加密算法以及token生成)


1.1.1. MD5/SHA-1

MD5即Message Digest Algorithm 5,为消息摘要的一种实现,摘要长度为128位,目前使用广泛; SHA-1, Secure Hash Algorithm,为消息摘要的一种实现,摘要长度为160位,相比MD5更安全,相对的计算速度更慢。

MD5/sha-1的使用:

public static String md5(byte[] data) throws Exception {
    MessageDigest alg = MessageDigest.getInstance("md5"); // 或者传入sha-1
    byte[] content = alg.digest(data);
    return base64Encode(content);
}

​ 一般来说MD5通过彩虹表(一张使用各种hash算法生成的明文密文对照表)还是可以破解的,通常会考虑“加盐(salt)”,这个salt可以看作是一个额外的“认证码”,同样的输入,不同的认证码,会产生不同的输出。因此,要验证输出的哈希,必须同时提供“认证码”,也不用自己手动去搞了,使用下面介绍的Hmac算法吧。

1.1.2. Hmac

Hmac全称为Hash-based Message Authentication Code, 就是一种基于密钥的消息认证码算法,也是一种更安全的消息摘要算法。Hmac算法总是和某种哈希算法配合起来用的。例如,我们使用MD5算法,对应的就是HmacMD5算法,它相当于“加盐”的MD5,同时生成密文的长度和使用源HASH算法生成的密文长度一样。比如还有:
HmacMD5/HmacSHA1/HmacSHA256/HmacSHA384/HmacSHA512

private static String hmac = "HmacMD5";
public static String generateHmacSignSign(String textToSign, String key) throws Exception {
    // 传入key和加密算法,也可以使用KeyGenerator生成(后续有使用)这样就不需要自己传入key了,具体得看业务需求
    SecretKey secretKey = new SecretKeySpec(base64Decoder(key), hmac); 
    Mac mac = Mac.getInstance(hmac);
    mac.init(secretKey);

    byte[] data = mac.doFinal(textToSign.getBytes("utf-8"));
    return Hex.encodeHexString(data);
}

1.2. 对称加密算法

​ 数据发送方将明文和加密密钥一起经过特殊加密算法后,生成密文发送给接收方,接收方若想读取明文,则需要使用相同的密钥和逆算法去读取(要求双方事先知道密钥)。计算量小,加密速度快,加密效率高,但是密钥保护对加密信息安全很重要。


迷你世界激活码生成器(常用编码方式、加密算法以及token生成)


​ 常见的有DES,3DES,AESDES的密钥为64位(其实只要56位,8位位检验位),将明文按照64位进行分组,然后与密钥安位进行替代或交换形成密文;由于容易被破解,所以又有了3DES,使用3个长度为56的密钥与明文进行三次运算,为DESAES的过渡算法;使用最为广泛的还是AES,设计有128/192/256位长度的密钥,相比前两种安全性更高。

​ 下面为AES的使用,对于DES也是类似,只需要将对应的字符串aes换成des即可。

private static Key aesKey;

static  {
    try{
        KeyGenerator keyGenerator = KeyGenerator.getInstance("aes"); //构造密钥生成器,指定为AES算法
        keyGenerator.init(128); // 生成一个128位的随机源
        SecretKey secretKey = keyGenerator.generateKey(); // 产生原始对称密钥
        String encoded = base64Encode(secretKey.getEncoded()); // 通常会使用base64编码后保存起来
        aesKey = new SecretKeySpec(base64Decoder(encoded), "aes");
    }catch (Exception e) {

    }
}

    /**
     *  AES加密
     */
    public static String aesEncrypt(String content) throws Exception {
        byte[] bytes = content.getBytes("utf-8");
        Cipher cipher = Cipher.getInstance("aes");
        cipher.init(Cipher.ENCRYPT_MODE, aesKey); // 加密模式
        byte[] result = cipher.doFinal(bytes);
        return base64Encode(result);
    }

    /**
     *  AES解密
     */
    public static String aesDecrypt(String content) throws Exception {
        byte[] bytes = base64Decoder(content);
        Cipher cipher = Cipher.getInstance("aes");
        cipher.init(Cipher.DECRYPT_MODE, aesKey); // 解密模式
        byte[] result = cipher.doFinal(bytes);
        System.out.println(new String(result));
        return base64Encode(result);
    }

1.3. 非对称加密算法

​ 甲方生成一对密钥,公钥向外公开,乙方使用明文和公钥加密产生密文,发送给乙方,乙方使用私钥进行解密。安全性高,但是复杂导致速度慢。


迷你世界激活码生成器(常用编码方式、加密算法以及token生成)


​ 常用的实现为RSA算法

    private static Map<String, String> map;

    static {
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("rsa");
            generator.initialize(512);
            KeyPair keyPair = generator.generateKeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
            String first = base64Encode(publicKey.getEncoded());
            String second = base64Encode(privateKey.getEncoded());
            map = new HashMap<>();
            map.put("first", first);
            map.put("second", second);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *  RSA加密
     */
    public static String rsaEncrypt(String content) throws Exception {
        byte[] publicBytes = base64Decoder(map.get("first"));
        KeyFactory keyFactory = KeyFactory.getInstance("rsa");
        PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicBytes));
        Cipher cipher = Cipher.getInstance("rsa");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(content.getBytes("utf-8"));
        return base64Encode(result);
    }

    /**
     *  RSA解密
     */
    public static String rsaDecrypt(String content) throws Exception {
        byte[] privateKeyByte = base64Decoder(map.get("second"));
        KeyFactory keyFactory = KeyFactory.getInstance("rsa");
        PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyByte));
        Cipher cipher = Cipher.getInstance("rsa");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(base64Decoder(content));
        System.out.println(new String(result));
        return base64Encode(result);
    }

2. 利用加密算法实现一个可用的token令牌

​ 需要进行身份验证的时候,目前比较流行的是服务端来下方令牌token,后续客户端带着这个令牌来请求服务端,服务端再进行身份验证。

​ Token有两个很重要的特性:

一个是随机性,不可预测,当然token会包含一些必要的信息,比如实例信息;另一个是存在过期时间;当然还有再刷新refresh功能,但是首要的是满足上面两个条件,下面就写一个可以实际使用的token。其中,createToken(T tokenBody, int minute)表示生成token,verify为检验token.。
import com.googlemon.base.Joiner;
import com.googlemon.base.Splitter;
import com.google.gson.*;
import org.apachemonsdec.binary.Base64;
import org.apachemons.lang3.RandomStringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
public class TokenUtil {

    private static Key aesKey = getAESKey();
    private static String hmacKey = "wSpJSBADgHgGIjdA9jvLzQ==";

    private static class Status {
        private int code;
        private String message;

        public Status(int code, String message) {
            thisde = code;
            thisssage = message;
        }

        @Override
        public String toString() {
            return "Status{"  
                    "code="   code  
                    ", message='"   message   '\''  
                    '}';
        }
    }

    static class Student {
        int id;
        String name;

        public Student(int id, String name) {
            this.id = id;
            this.name = name;
        }
    }

    /**
     * 校验token,主要是校验两个方面
     * 1、校验签名是否合法
     * 2、校验token是否过期
     * @param token
     * @return
     */
    public static Status verify(String token) {
        List<String> list = Splitter.on('.').splitToList(token);
        if (list.size() != 3) {
            return new Status(5000, "token length is not valid");
        }
        String encryptJson = list.get(1);
        String sign = list.get(2);
        String headerJson = list.get(0);
        try {
            if (!verifySign(encryptJson, sign)) {
                return new Status(5001, "invalid token");
            }

            if (!verifyExpire(headerJson)) {
                return new Status(5002, "token has time out");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Status(200, "success");
    }

    /**
     * 校验token是否过期
     * @param headerJson  请求头的base64编码
     * @return
     */
    private static boolean verifyExpire(String headerJson) {
        byte[] headerBytes = base64Decoder(headerJson);
        String json = new String(headerBytes);
        JsonObject jsonObject = (JsonObject)new JsonParser().parse(json);
        JsonElement element = jsonObject.get("expire");
        long expireTime = element.getAsLong();
        return expireTime > System.currentTimeMillis() / 1000;
    }

    /**
     *校验签名  使用base64解码后的字节数组来生成签名,并与传递来的签名进行比较得出合法性
     * @param encryptJson
     * @param sign
     * @return
     * @throws Exception
     */
    private static boolean verifySign(String encryptJson, String sign) throws Exception{
        byte[] encryptBody = base64Decoder(encryptJson);
        byte[] bytes = generateHmacSign(encryptBody);
        return StringUtils.equals(sign, base64Encoder(bytes));
    }

    private static byte[] AESDecryptBody(byte[] encryptBody) throws Exception{
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, aesKey);
        return cipher.doFinal(encryptBody);
    }

    /**
     *
     * @param tokenBody 实例对象,通常为bean
     * @param minute 过期时间   单位:min
     * @param <T>
     * @return
     */
    public static <T> String createToken(T tokenBody, int minute) {
        long now = System.currentTimeMillis() / 1000;
        Gson gson = new Gson();
        JsonObject jsonBody = new JsonObject();
        jsonBody.addProperty("body", gson.toJson(tokenBody));

        String randomAlphabetic = RandomStringUtils.randomAlphabetic(3);
        JsonObject jsonHeader = new JsonObject();
        jsonHeader.addProperty("now", now);
        jsonHeader.addProperty("rand_num", randomAlphabetic);
        jsonHeader.addProperty("expire", (now   minute * 60));

        String token = null;
        try {
            byte[] encryptContent = generateEncryptBody(jsonHeader.toString(), jsonHeader.toString());
            byte[] signWithEncrypt = generateSignWithEncrypt(encryptContent);
            token = Joiner.on(".").join(new String[]{base64Encoder(
                    jsonHeader.toString().getBytes("utf-8")),
                    base64Encoder(encryptContent),
                    base64Encoder(signWithEncrypt)}
                    );
        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }

    private static byte[] generateEncryptBody(String header, String body) throws Exception{
        byte[] headerBytes = header.getBytes("utf-8");
        byte[] bodyBytes = body.getBytes("utf-8");
        byte[] content = xor(headerBytes, bodyBytes);
        byte[] encryptContent = AESEncrypt(content);
        return encryptContent;
    }

    private static byte[] generateSignWithEncrypt(byte[] encryptContent) throws Exception{
        byte[] result = generateHmacSign(encryptContent);
        return result;
    }

    private static byte[] generateHmacSign(byte[] content) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(hmacKey.getBytes("utf-8"), "HmacMD5");
        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(secretKey);
        return mac.doFinal(content);
    }

    private static String base64Encoder(byte[] data) {
        return Base64.encodeBase64String(data);
    }

    private static byte[] base64Decoder(String content) {
        return Base64.decodeBase64(content);
    }

    private static byte[] xor(byte[] header, byte[] body) {
        byte[] xorData = new byte[body.length];
        for (int i = 0; i < body.length; i   ) {
            int idx = i % header.length;
            xorData[i] = (byte)(body[i] ^ header[idx]);
        }
        return xorData;
    }

    private static Key getAESKey() {
        Key key = null;
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(new SecureRandom());
            byte[] encodedKey = keyGenerator.generateKey().getEncoded();
            key = new SecretKeySpec(encodedKey, "AES");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return key;
    }

    private static byte[] AESEncrypt(byte[] content) throws Exception {
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, aesKey);
        return cipher.doFinal(content);
    }

    private static byte[] AESDecrypt(byte[] content) throws Exception {
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, aesKey);
        return cipher.doFinal(content);
    }
    public static void main(String[] args) throws InterruptedException {
        String token = createToken(new Student(1, "tom"), 1);
        System.out.println(token);
        Status status = verify(token);
        System.out.println(status);
        System.out.println(verify(token   "ab"));
        Thread.sleep(70000);
        System.out.println(verify(token));
    }
}

来验证一下输出:

eyJub3ciOjE1ODU3MTg4NTEsInJhbmRfbnVtIjoia3hIIiwiZXhwaXJlIjoxNTg1NzE4OTExfQ==.bPvihryiPud0Vq0A8spQl2z74oa8oj7ndFatAPLKUJds  KGvKI 53RWrQDyylCXcUvGGQsG4bLHYxHBdn/wbA==.pN4kTCFtceRiPOaxaLlfAQ==
Status{code=200, message='success'}
Status{code=5001, message='invalid token'}
Status{code=5002, message='token has time out'}

当然,在生产环境中key这些固定的东西肯定是离不开配置文件,或者有专门的key相关服务。