Java程序员从笨鸟到菜鸟(六十九)常用加密方法

前言

在这个信息共享的时代,信息安全无论是对于开发还是用户来说都是重点关注的问题,例如在表单提交时,采用密文的方式来代替明文,可以相对有效避免重要信息外泄,文中阐述了几种比较常用的加密方法

加密方式

1、BASE64

严格来说是编码格式,而非加密算法;特点是加密解密是双向的,可以求反解;主要是 BASE64Encoder、BASE64Decoder 两个类;常见用于邮件、http 加密

测试代码实现:

package encoder;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * create by [email protected] on 2018/12/13 9:22
 * BASE64 严格来说是编码格式,而非加密算法
 * 加密解密是双向的,可以求反解
 * 主要是 BASE64Encoder、BASE64Decoder 两个类
 * 常见于邮件、http 加密
 **/
 public class BASE64 {
    /**
     * BASE64 解密
     * @param key 秘钥
     * @throws Exception 异常
     * */
    private static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

    /**
     * BASE64 加密
     * @param key 秘钥
     * @throws Exception 异常
     * */
    private static String encrypyBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    public static void main(String[] args) {
        String str = "12345678";
        try {
            System.out.println("str====加密前====" + str);
            String encodeData = BASE64.encrypyBASE64(str.getBytes());
            System.out.println("encodeData====加密数据=====" + encodeData);
            byte[] decodeData = BASE64.decryptBASE64(encodeData);
            System.out.println("decodeData====解密数据=====" + new String(decodeData));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:

str====加密前====12345678
encodeData====加密数据=====MTIzNDU2Nzg=

decodeData====解密数据=====12345678

Process finished with exit code 0

2、MD5

MD5(Message Digest Algorithm 5) 信息摘要算法,用于确保信息传输完整一致,是计算机广泛使用的杂凑算法之一(又称摘要算法、哈希算法),将数据(如汉字)运算为另一固定长度值,常用语文件校验;通常是不直接使用上述 MD5 加密,通常将 MD5 产生的字节数组交给 BASE64 再加密一把,得到相应的字符串
具有以下特点:

  • 压缩性:任意长度的数据,算出的 MD5 值长度是固定的
  • 容易计算:从原数据计算出 MD5 值很容易
  • 抗修改性:对于原数据进行任何更改,所得到的 MD5 值都有很大的区别
  • 弱抗碰撞:已知原数据和其 MD5 值,想找到一个具有相同 MD5 值的数据非常困难
  • 强抗碰撞:想找到两个不同的数据,使他们具有相同的 MD5 值,非常困难
    测试代码实现:
package encoder;

import java.math.BigInteger;
import java.security.MessageDigest;

/**
 * create by [email protected] on 2018/12/13 10:02
 * MD5(Message Digest Algorithm 5) 信息摘要算法 用于确保信息传输完整一致
 * 是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法)
 * 将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,常用语文件校验
 * 通常不直接使用上述 MD5 加密,通常将 MD5 产生的字节数组交给 BASE64 再加密一把,得到相应的字符串
 * 具有以下特点:
 * 1、压缩性:任意长度的数据,算出的 MD5 值长度是固定的
 * 2、容易计算:从原数据计算出 MD5 值很容易
 * 3、抗修改性:对原数据进行任何更改,所得到的 MD5 值都有很大区别
 * 4、弱抗碰撞:已知原数据和其 MD5 值,想找到一个具有相同 MD5 值得数据时非常困难的
 * 5、强抗碰撞:想找到两个不同的数据,使他们具有相同的 MD5 值,非常困难
 **/
public class MD5 {
    private static final String KEY_MD5 = "MD5";

    private static String getResult(String inputStr) {
        System.out.println("=====加密前的数据:" + inputStr);
        BigInteger bigInteger = null;

        try {
            MessageDigest md = MessageDigest.getInstance(KEY_MD5);
            byte[] inputData = inputStr.getBytes();
            md.update(inputData);
            bigInteger = new BigInteger(md.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bigInteger.toString(16);
    }
    public static void main(String[] args) {
        try {
            String inputStr = "简单加密8888888888";
            System.out.println("MD5 加密后:" + getResult(inputStr));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:

=====加密前的数据:简单加密8888888888
MD5 加密后:-60398b41d73fd45b9f90ce6a612ada8c

Process finished with exit code 0

3、SHA

SHA(Secure Hash Algorithm 安全散列算法)主要适用于数字签名标准(Digest Signature Standard DSS)里面定义的数字签名算法
算法思想:接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文
较之 MD5 更为安全
SHA-1 与 MD5 比较;因为二者均由 MD4 导出,SHA-1 和 MD5 彼此很相似,但还有以下几点不同:

  • 对强行攻击的安全性:最显著和最重要的区别是 SHA-1 摘要要比 MD5 摘要长 32 位
  • 对密码分析的安全性:由于 MD5 的设计,易受密码分析攻击,SHA-1 不易受攻击
  • 速度:在相同的硬件基础上,SHA-1 的运行速度比 MD5 慢
    测试代码实现:
package encoder;

import java.math.BigInteger;
import java.security.MessageDigest;

/**
 * create by [email protected] on 2018/12/13 10:36
 * SHA (Secure Hash Algorithm 安全散列算法)主要适用于数字签名标准(Digest Signature Standard DSS)里面定义的数字签名算法
 * (Digest Signature Algorithm DSA) 对于长度小于 2^64 位的消息,SHA1 会产生一个 160 位的消息摘要
 * 算法思想:接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文
 * 较之 MD5 更为安全
 * SHA-1 与 MD5 的比较
 * 因为二者均由 MD4 导出,SHA-1 和 MD5 彼此很相似,但还有以下几点不同
 * 1、对强行攻击的安全性:最显著和最重要的区别是 SHA-1 摘要要比 MD5 摘要长 32 位
 * 2、对密码分析的安全性:由于 MD5 的设计,易受密码分析攻击,SHA-1 不易受攻击
 * 3、速度:在相同的硬件基础上,SHA-1 的运行速度比 MD5 慢
 **/
public class SHA {
    private static final String KEY_SHA = "SHA";
    private static String getResult(String inputStr) {
        BigInteger bigInteger = null;
        System.out.println("=======加密前的数据:" + inputStr);
        byte[] inputData = inputStr.getBytes();
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(KEY_SHA);
            messageDigest.update(inputData);
            bigInteger = new BigInteger(messageDigest.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bigInteger.toString(32);
    }
    public static void main(String[] args) {
        try {
            String inputStr = "简单加密";
            System.out.println("=======加密后的数据:" + getResult(inputStr));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:

=======加密前的数据:简单加密
=======加密后的数据:91k9vo7p400cjkgfhjh0ia9qthsjagfn

Process finished with exit code 0

4、AES

AES(Advanced Encryption Standard 高级加密标准)是现在对称加密算法中最流行的算法之一
AES 可以使用 128、192 和 256 位密钥,并且用 128 位分组加密和解密数据,相对来说安全
测试代码实现:

public class AES {
    private static String src = "TestAES";

    /**
     * AES 加密字符串
     * @param content 需要被加密的字符串
     * @param password 加密需要的密码
     * @return 密文
     * */
    public static byte[] encrypt(String content, String password) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 创建 AES 的 key 生产者
            keyGenerator.init(128, new SecureRandom(password.getBytes())); // 利用用户密码作为随机初始化出
            // 128 位的 key 生产者,SecureRandom 是生成安全随机序列,password.getBytes() 是种子,只要种子相同,序列就一样
            // 所以解密只要有 password 就行
            SecretKey secretKey = keyGenerator.generateKey(); // 根据用户密码,生成一个密钥
            byte[] enCodeFormate = secretKey.getEncoded(); // 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 null
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormate, "AES"); // 转换为 AES 专用密钥
            Cipher cipher = Cipher.getInstance("AES"); // 创建密码器
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); // 初始化为加密模式的密码器
            byte[] result = cipher.doFinal(byteContent); // 加密
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES 解密 解密AES 加密的字符串
     * @param content AES 加密过的内容
     * @param password 加密时的密码
     * @return 明文
     * */
    public static byte[] decrypt(byte[] content, String password) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 创建 AES 的 key 生产者
            keyGenerator.init(128, new SecureRandom(password.getBytes()));
            SecretKey secretKey = keyGenerator.generateKey(); // 根据用户密码,生成一个密钥
            byte[] enCodeFormat = secretKey.getEncoded(); // 返回基本编码格式的密钥
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); // 转为 AES 专用密钥
            Cipher cipher = Cipher.getInstance("AES"); // 创建密码器
            cipher.init(Cipher.DECRYPT_MODE, key); // 初始化为解密模式的密码器
            byte[] result = cipher.doFinal(content);
            return result; // 明文
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        String content = "美女,约吗?";
        String password = "123";
        System.out.println("====加密前的内容:" + content);

        // 加密
        byte[] encrypt = encrypt(content, password);
        System.out.println("====加密后的内容,转换前:" + new String(encrypt));
        System.out.println("====加密后的内容,转换后:" + ParseSystemUtil.parseByte2HexStr(encrypt));

        // 解密
        byte[] decrypt = decrypt(encrypt, password);
        System.out.println("====解密后的内容:" + new String(decrypt));
    }
}

运行结果:

====加密前的内容:美女,约吗?
====加密后的内容,转换前:P�d�g�K�3�g�����,Ꝏ?U納�_
====加密后的内容,转换后:50FE6401E867A34BD533FE67BB85EDABFED62CEA9D8E3F5516E7B48D01F21A5F
====解密后的内容:美女,约吗?

Process finished with exit code 0

编码转换工具:
ParseSystemUtil 类实现:

扫描二维码关注公众号,回复: 4682247 查看本文章
package encoder;

/**
 * create by [email protected] on 2018/12/13 13:53
 * 字节数组和十六进制之间的相互转换
 **/
public class ParseSystemUtil {

    /**
     * 将字节数组转换成十六进制
     * @param buf 字节数组
     * @return 十六进制字符串
     * */
    public static 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();
    }

    /**
     * 将十六进制转换成字节数组
     * @param hex 十六进制的字符串
     * @return 字节数组
     * */
    public static byte[] parseHexStr2Byte(String hex) {
        if (hex.length() < 1) {
            return null;
        }
        byte[] result = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length() / 2; i++) {
            int high = Integer.parseInt(hex.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hex.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte)(high * 16 + low);
        }
        return result;
    }

    // 字符串转字符数组
    public static char[] change(String s) {
        char[] result = new char[s.length()/2]; // 定义数组
        for (int i = 0; i < result.length; i++)
            result[i] = (char)(Integer.parseInt(s.substring(2*i, 2*i + 2),16 & 0xFF));
        return result;
    }
}

猜你喜欢

转载自blog.csdn.net/u013090299/article/details/84998706