百味皆苦 java后端开发攻城狮

数据加解密工具类

2020-08-28
百味皆苦

BASE64

  • Base64是一种根据ASCII码来进行加密的可逆算法,这类算法简单来说,就等于将每个字符对应一个特定的字符(常常是根据进制进行匹配),一一对应,特别像电台密码本,因为每个字符都必须要加密,加密比较笨重。

  • 加密

    • 获取字符串中每个字符的ASCII码
    • 按照每3个8bit的字符为一组来分组,即每组24bit
    • 将这24bit划分成4个6bit的4个单位,每个单位前面添加2个0,得到4个8bit的单位
    • 将每个8bit的单位转换成十进制数字,对照Base64编码表找到对应的字符进行拼接,得到最终的加密后的字符串
  • 解密

    • 读入4个字符,对照Base64编码表找到字符对应的索引,生成4个6为的值
    • 将这4个6为的值拼接起来,形成一个24为的值
    • 将这个24位的值按照8位一组截断成3个8位的值
    • 对照ASCII表找到这三个8位的值对应的字符,即解码后的字符
  • /** 工具类实现代码  **/
    public class BASE64Util {
        private static final Map<Integer, Character> base64CharMap = new HashMap<>();
        private static final String base64CharString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
       
        private static BASE64Util instance;
       
        private BASE64Util() {
            for (int i = 0; i < base64CharString.length(); i++) {
                char c = base64CharString.charAt(i);
                base64CharMap.put(new Integer(i), new Character(c));
            }
        }
       
        public static BASE64Util getInstance() {
            if (instance == null) {
                synchronized (BASE64Util.class) {
                    if (instance == null) {
                        instance = new BASE64Util();
                    }
                }
            }
            return instance;
        }
       
        /**
         * This method is used to encode a normal string to base64 string @param
         * origin The String to be encoded @return The String after encoded.
         */
        public String encode(String origin) {
            if (origin == null) {
                return null;
            }
            if (origin.length() == 0) {
                return "";
            }
            int length = origin.length();
            String binaryString = "";
            // to binary String
            for (int i = 0; i < length; i++) {
                int ascii = origin.charAt(i);
                String binaryCharString = Integer.toBinaryString(ascii);
                while (binaryCharString.length() < 8) {
                    binaryCharString = "0" + binaryCharString;
                }
                binaryString += binaryCharString;
            }
       
            // to base64 index
            int beginIndex = 0;
            int endIndex = beginIndex + 6;
            String base64BinaryString = "";
            String charString = "";
            while ((base64BinaryString = binaryString.substring(beginIndex, endIndex)).length() > 0) {
                // if length is less than 6, add "0".
                while (base64BinaryString.length() < 6) {
                    base64BinaryString += "0";
                }
                int index = Integer.parseInt(base64BinaryString, 2);
                char base64Char = base64CharMap.get(index);
                charString = charString + base64Char;
                beginIndex += 6;
                endIndex += 6;
                if (endIndex >= binaryString.length()) {
                    endIndex = binaryString.length();
                }
                if (endIndex < beginIndex) {
                    break;
                }
            }
            if (length % 3 == 2) {
                charString += "=";
            }
            if (length % 3 == 1) {
                charString += "==";
            }
            return charString;
        }
       
        public String decode(String encodedString) {
            if (encodedString == null) {
                return null;
            }
            if (encodedString.length() == 0) {
                return "";
            }
            // get origin base64 String
            String origin = encodedString.substring(0, encodedString.indexOf("="));
            String equals = encodedString.substring(encodedString.indexOf("="));
       
            String binaryString = "";
            // convert base64 string to binary string
            for (int i = 0; i < origin.length(); i++) {
                char c = origin.charAt(i);
                int ascii = base64CharString.indexOf(c);
                String binaryCharString = Integer.toBinaryString(ascii);
                while (binaryCharString.length() < 6) {
                    binaryCharString = "0" + binaryCharString;
                }
                binaryString += binaryCharString;
            }
            // the encoded string has 1 "=", means that the binary string has append
            // 2 "0"
            if (equals.length() == 1) {
                binaryString = binaryString.substring(0, binaryString.length() - 2);
            }
            // the encoded string has 2 "=", means that the binary string has append
            // 4 "0"
            if (equals.length() == 2) {
                binaryString = binaryString.substring(0, binaryString.length() - 4);
            }
       
            // convert to String
            String charString = "";
            String resultString = "";
            int beginIndex = 0;
            int endIndex = beginIndex + 8;
            while ((charString = binaryString.substring(beginIndex, endIndex)).length() == 8) {
                int ascii = Integer.parseInt(charString, 2);
                resultString += (char) ascii;
                beginIndex += 8;
                endIndex += 8;
                if (endIndex > binaryString.length()) {
                    break;
                }
            }
            return resultString;
        }
    }
    

MD5

  • MD5码加解密算法Message-Digest Algorithm 5(信息-摘要算法),是一种不可逆的算法,具有很高的安全性。
  • 它对应任何字符串都可以加密成一段唯一的固定长度的代码。(小贴士:为啥MD5加密算法不可逆呢~ 按道理来说有加密方式,就会有解密方式呀?因为MD5加密是有种有损的加密方式,比如一段数据为’123’,我在加密的时候,遇到1和3都直接当做是a,加密后变成了’a2a’,所以解密的时候就出现了4种组合’323’‘121’‘123’‘321’,数据一多,自然找不到原始的数据了,当然这种方式加密的密文也不需要解密)
// MD5码工具类代码  
public class MD5Util {
    public static final String MD5 = "MD5";
    public static final String HmacMD5 = "HmacMD5";
    public static final String charset = null; // 编码格式;默认null为GBK
 
    private static MD5Util instance;
 
    private MD5Util() {
    }
 
    // 单例
    public static MD5Util getInstance() {
        if (instance == null) {
            synchronized (MD5Util.class) {
                if (instance == null) {
                    instance = new MD5Util();
                }
            }
        }
        return instance;
    }
 
    /**
     * 使用 MD5 方法加密(无密码)
     */
    public String encode(String res) {
        try {
            MessageDigest md = MessageDigest.getInstance(MD5);
            byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
            return BASE64Util.getInstance().encode(md.digest(resBytes).toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 使用 MD5 方法加密(可以设密码)
     */
    public String encode(String res, String key) {
        try {
            SecretKey sk = null;
            if (key == null) {
                KeyGenerator kg = KeyGenerator.getInstance(HmacMD5);
                sk = kg.generateKey();
            } else {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                sk = new SecretKeySpec(keyBytes, HmacMD5);
            }
            Mac mac = Mac.getInstance(HmacMD5);
            mac.init(sk);
            byte[] result = mac.doFinal(res.getBytes());
            return BASE64Util.getInstance().encode(result.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
  
  	//解决有些md5前面是0无法显示的问题
   public static String getMD5(String str) throws Exception{
        // 生成一个MD5加密计算摘要
        MessageDigest md = MessageDigest.getInstance("MD5");
        // 计算md5函数
        md.update(str.getBytes());
        return toHexString(md.digest());
    }
	//把数组中的每个字节转换为16进制
    private static String toHexString(byte[] bytes) {       
        Formatter formatter = new Formatter();              
        for (byte b : bytes) {          
            formatter.format("%02x", b);
        }

        String res = formatter.toString();
        formatter.close();

        return res;
   } 
}

DES

public class DESUtil {
    public static final String DES = "DES";
    public static final String charset = null; // 编码格式;默认null为GBK
    public static final int keysizeDES = 0;
 
    private static DESUtil instance;
 
    private DESUtil() {
    }
 
    // 单例
    public static DESUtil getInstance() {
        if (instance == null) {
            synchronized (MD5Util.class) {
                if (instance == null) {
                    instance = new DESUtil();
                }
            }
        }
        return instance;
    }
 
    /**
     * 使用 DES 进行加密
     */
    public String encode(String res, String key) {
        return keyGeneratorES(res, DES, key, keysizeDES, true);
    }
 
    /**
     * 使用 DES 进行解密
     */
    public String decode(String res, String key) {
        return keyGeneratorES(res, DES, key, keysizeDES, false);
    }
 
    // 使用KeyGenerator双向加密,DES/AES,注意这里转化为字符串的时候是将2进制转为16进制格式的字符串,不是直接转,因为会出错
    private String keyGeneratorES(String res, String algorithm, String key, int keysize, boolean isEncode) {
        try {
            KeyGenerator kg = KeyGenerator.getInstance(algorithm);
            if (keysize == 0) {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                kg.init(new SecureRandom(keyBytes));
            } else if (key == null) {
                kg.init(keysize);
            } else {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                kg.init(keysize, new SecureRandom(keyBytes));
            }
            SecretKey sk = kg.generateKey();
            SecretKeySpec sks = new SecretKeySpec(sk.getEncoded(), algorithm);
            Cipher cipher = Cipher.getInstance(algorithm);
            if (isEncode) {
                cipher.init(Cipher.ENCRYPT_MODE, sks);
                byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
                return parseByte2HexStr(cipher.doFinal(resBytes));
            } else {
                cipher.init(Cipher.DECRYPT_MODE, sks);
                return new String(cipher.doFinal(parseHexStr2Byte(res)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    // 将二进制转换成16进制
    private String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }
 
    // 将16进制转换为二进制
    private byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }
}

AES

  • AES也是,采用密钥,意思是传入一个需要加密的对象的同时,约定一个key(可以是字符串),然后解密端,通过这个key进行解密获取对象。AES的实现原理,是先确定密钥,确定方式:算法/模式/补码。
public class AESUtil {
 
    /**
     * 加密
     * @return 加密后的字符串
     */
    public static String Encrypt(String src, String key) throws Exception {
        // 判断密钥是否为空
        if (key == null) {
            System.out.print("密钥不能为空");
            return null;
        }
 
        // 密钥补位
        int plus= 16-key.length();
        byte[] data = key.getBytes("utf-8");
        byte[] raw = new byte[16];
        byte[] plusbyte={ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
        for(int i=0;i<16;i++)
        {
            if (data.length > i)
                raw[i] = data[i];
            else
                raw[i] = plusbyte[0];
        }
 
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");    // 算法/模式/补码方式
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(src.getBytes("utf-8"));
 
        //return new Base64().encodeToString(encrypted);//base64
        return binary(encrypted, 16); //十六进制
    }
 
    /**
     * 解密
     * @return 解密后的字符串
     */
    public static String Decrypt(String src, String key) throws Exception {
        try {
            // 判断Key是否正确
            if (key == null) {
                System.out.print("Key为空null");
                return null;
            }
 
            // 密钥补位
            int plus= 16-key.length();
            byte[] data = key.getBytes("utf-8");
            byte[] raw = new byte[16];
            byte[] plusbyte={ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
            for(int i=0;i<16;i++)
            {
                if (data.length > i)
                    raw[i] = data[i];
                else
                    raw[i] = plusbyte[0];
            }
 
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
 
            //byte[] encrypted1 = new Base64().decode(src);//base64
            byte[] encrypted1 = toByteArray(src);//十六进制
 
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original,"utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }
 
    /**
     * 将byte[]转为各种进制的字符串
     * @param bytes byte[]
     * @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制
     * @return 转换后的字符串
     */
    public static String binary(byte[] bytes, int radix){
        return new BigInteger(1, bytes).toString(radix);   // 这里的1代表正数
    }
 
    /**
     * 16进制的字符串表示转成字节数组
     *
     * @param hexString 16进制格式的字符串
     * @return 转换后的字节数组
     **/
    public static byte[] toByteArray(String hexString) {
        if (hexString.isEmpty())
            throw new IllegalArgumentException("this hexString must not be empty");
 
        hexString = hexString.toLowerCase();
        final byte[] byteArray = new byte[hexString.length() / 2];
        int k = 0;
        for (int i = 0; i < byteArray.length; i++) {//因为是16进制,最多只会占用4位,转换成字节需要两个16进制的字符,高位在先
            byte high = (byte) (Character.digit(hexString.charAt(k), 16) & 0xff);
            byte low = (byte) (Character.digit(hexString.charAt(k + 1), 16) & 0xff);
            byteArray[i] = (byte) (high << 4 | low);
            k += 2;
        }
        return byteArray;
    }
 
}

RSA

引用:https://www.cnblogs.com/pcheng/p/9629621.html

RSA加密是一种非对称加密。可以在不直接传递密钥的情况下,完成解密。这能够确保信息的安全性,避免了直接传递密钥所造成的被破解的风险。是由一对密钥来进行加解密的过程,分别称为公钥和私钥。两者之间有数学相关,该加密算法的原理就是对一极大整数做因数分解的困难性来保证安全性。通常个人保存私钥,公钥是公开的(可能同时多人持有)。

RSA加密对明文的长度有所限制,规定需加密的明文最大长度=密钥长度-11(单位是字节,即byte),所以在加密和解密的过程中需要分块进行。而密钥默认是1024位,即1024位/8位-11=128-11=117字节。所以默认加密前的明文最大长度117字节,解密密文最大长度为128字。那么为啥两者相差11字节呢?是因为RSA加密使用到了填充模式(padding),即内容不足117字节时会自动填满,用到填充模式自然会占用一定的字节,而且这部分字节也是参与加密的。

import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;

public class TestRSA {

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 获取密钥对
     *
     * @return 密钥对
     */
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
      
        //可自行调整,当然非对称加密随着密钥变长,安全性上升的同时性能也会有所下降。
        generator.initialize(1024);
        return generator.generateKeyPair();
    }

    /**
     * 获取私钥
     *
     * @param privateKey 私钥字符串
     * @return
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 获取公钥
     *
     * @param publicKey 公钥字符串
     * @return
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * RSA加密
     *
     * @param data 待加密数据
     * @param publicKey 公钥
     * @return
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
        // 加密后的字符串
        return new String(Base64.encodeBase64String(encryptedData));
    }

    /**
     * RSA解密
     *
     * @param data 待解密数据
     * @param privateKey 私钥
     * @return
     */
    public static String decrypt(String data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.decodeBase64(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        // 解密后的内容
        return new String(decryptedData, "UTF-8");
    }

    /**
     * 签名
     *
     * @param data 待签名数据
     * @param privateKey 私钥
     * @return 签名
     */
    public static String sign(String data, PrivateKey privateKey) throws Exception {
        byte[] keyBytes = privateKey.getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(key);
        signature.update(data.getBytes());
        return new String(Base64.encodeBase64(signature.sign()));
    }

    /**
     * 验签
     *
     * @param srcData 原始字符串
     * @param publicKey 公钥
     * @param sign 签名
     * @return 是否验签通过
     */
    public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
        byte[] keyBytes = publicKey.getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(key);
        signature.update(srcData.getBytes());
        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }

    public static void main(String[] args) {
        try {
            // 生成密钥对
            KeyPair keyPair = getKeyPair();
            String privateKey = new String(Base64.encodeBase64(keyPair.getPrivate().getEncoded()));
            String publicKey = new String(Base64.encodeBase64(keyPair.getPublic().getEncoded()));
            System.out.println("私钥:" + privateKey);
            System.out.println("公钥:" + publicKey);
            // RSA加密
            String data = "待加密的文字内容";
            String encryptData = encrypt(data, getPublicKey(publicKey));
            System.out.println("加密后内容:" + encryptData);
            // RSA解密
            String decryptData = decrypt(encryptData, getPrivateKey(privateKey));
            System.out.println("解密后内容:" + decryptData);

            // RSA签名
            String sign = sign(data, getPrivateKey(privateKey));
            // RSA验签
            boolean result = verify(data, getPublicKey(publicKey), sign);
            System.out.print("验签结果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.print("加解密异常");
        }
    }
}

上一篇 ft工具类

下一篇 尚zookeeper

Comments

Content