【加密算法】5 种常见的摘要、加密算法

大家平时的工作中,可能也在很多地方用到了加密、解密,比如:

  1. 用户的密码不能明文存储,要存储加密后的密文
  2. 用户的银行卡号、身份证号之类的敏感数据,需要加密传输
  3. 还有一些重要接口,比如支付,客户端要对请求生成一个签名,服务端要对签名进行验证
  4. ……

那么上面提到的这些能力,我们都可以利用哪些加密算法来实现呢?咱们接着往下看。

加密算法整体上可以分为:不可逆加密、可逆加密。可逆加密又可以分为:对称加密、非对称加密。
在这里插入图片描述

1.不可逆算法

不可逆加密的算法的加密是不可逆的,密文无法被还原成原文。

散列算法,就是一种不可逆算法。散列算法中,明文通过散列算法生成散列值,散列值是长度固定的数据,和明文长度无关;常用于数字签名、消息认证、密码存储等场景;不需要密钥的,当然也有一些不可逆算法,需要密钥,例如 HMAC 算法。

散列算法的具体实现有很多种,常见的包括 MD5SHA1SHA-224SHA-256 等等。

1.1 MD5

MD5,全称为 “Message-Digest Algorithm 5”,翻译过来叫“信息摘要算法”。它可以将任意长度的数据通过散列算法,生成一个固定长度的散列值。MD5算法的输出长度为 128 位,通常用 32 个 16 进制数表示

public class Md5Util {
    
    

    private static final String[] hexDigits = new String[]{
    
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

    /**
     * 获取MD5加密结果
     *
     * @param plainText   要加密的原字符串
     * @param uppercase   是否转为大写字符串
     * @param bit         加密结果位数(16,32),默认是32
     * @return 返回加密结果
     */
    public static String encrypt(String plainText, boolean uppercase, Integer bit) {
    
    
        byte[] btInput = plainText.getBytes(StandardCharsets.UTF_8);
        MessageDigest messageDigest;
        try {
    
    
            //获得MD5摘要对象
            messageDigest = MessageDigest.getInstance("MD5");
            //使用指定的字节数组更新摘要信息
            messageDigest.update(btInput);
        } catch (NoSuchAlgorithmException e) {
    
    
            throw new RuntimeException("MD5签名过程中出现错误,算法异常");
        }
        byte[] digest = messageDigest.digest();
        //字节数组转成16进制字符串
        String result = byteArrayToHexString(digest);
        //如果获取的是16位加密结果的,则截取原加密结果(32位)中间的16位,也就是8-24位
        if (bit != null && bit == 16) {
    
    
            //截取下标从0开始
            result = result.substring(8, 24);
        }
        //结果的大小写处理
        return uppercase ? result.toUpperCase() : result;
    }

    /**
     * 字节数组转成16进制字符串
     *
     * @author zzc
     * @date 2023/6/28 17:29
     * @param bytes
     * @return java.lang.String
     */
    private static String byteArrayToHexString(byte[] bytes) {
    
    
        StringBuilder builder = new StringBuilder();
        for (byte b : bytes) {
    
    
            builder.append(byteToHexString(b));
        }
        return builder.toString();
    }

    /**
     * 字节转成16进制字符
     *
     * @author zzc
     * @date 2023/6/28 17:29
     * @param b
     * @return java.lang.String
     */
    private static String byteToHexString(byte b) {
    
    
        int n = b;
        if (n < 0) {
    
    
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    public static void main(String[] args) throws Exception {
    
    
        String s = "zzc";
        System.out.println(encrypt(s, false, 32));
    }

}
  • 优点:算速度快、输出长度固定、应用广泛
  • 缺点:不安全。

MD5算法已经被攻破,而且 MD5 算法的输出长度有限,攻击者可以通过暴力破解或彩虹表攻击等方式,找到与原始数据相同的散列值,从而破解数据。虽然可以通过加盐,也就是对在原文里再加上一些不固定的字符串来缓解,但是完全可以用更安全的 SHA 系列算法替代

MD5工具类(含16位、32位、加盐、无盐、大小写)
MD5加密工具类

1.2 SHA-256

SHA(Secure Hash Algorithm)系列算法是一组密码散列函数,用于将任意长度的数据映射为固定长度的散列值。SHA系列算法由美国国家安全局(NSA)于1993年设计,目前共有 SHA-1SHA-2SHA-3 三种版本。其中 SHA-1 系列存在缺陷,已经不再被推荐使用。

SHA-2 算法包括 SHA-224、SHA-256、SHA-384 和 SHA-512 四种散列函数,分别将任意长度的数据映射为 224 位、256 位、384 位和 512 位的散列值。

public class SHA256Util {
    
    

    public static String getSHA256String(String str) {
    
    
        MessageDigest messageDigest;
        String sha256Str = "";
        try {
    
    
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes("UTF-8"));
            sha256Str = byte2Hex(messageDigest.digest());
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return sha256Str;
    }

    /**
     * 将byte转为16进制
     *
     * @param bytes
     * @return
     */
    private static String byte2Hex(byte[] bytes) {
    
    
        StringBuffer stringBuffer = new StringBuffer();
        String temp = null;
        for (int i = 0; i < bytes.length; i++) {
    
    
            temp = Integer.toHexString(bytes[i] & 0xFF);
            if (temp.length() == 1) {
    
    
                stringBuffer.append("0");
            }
            stringBuffer.append(temp);
        }
        return stringBuffer.toString();
    }

    public static void main(String[] args) throws Exception {
    
    
        String data = "Hello World";
        String encryptedData = getSHA256String(data);
        System.out.println("加密后的数据:" + encryptedData);
    }

}

SHA-2 算法之所以比 MD5 强,主要有两个原因:

  1. 散列值长度更长:例如 SHA-256 算法的散列值长度为 256 位,而 MD5 算法的散列值长度为 128 位,这就提高了攻击者暴力破解或者彩虹表攻击的难度。
  2. 更强的碰撞抗性:SHA 算法采用了更复杂的运算过程和更多的轮次,使得攻击者更难以通过预计算或巧合找到碰撞。
    当然,SHA-2 也不是绝对安全的,散列算法都有被暴力破解或者彩虹表攻击的风险,所以,在实际的应用中,加盐还是必不可少的

2.可逆算法

2.1 对称加密

对称加密算法使用同一个密钥进行加密和解密。加密和解密过程使用的是相同的密钥,因此密钥的安全性至关重要。如果密钥泄露,攻击者可以轻易地破解加密数据。

常见的对称加密算法包括 DES、3DES、AES 等。其中,AES 算法是目前使用最广泛的对称加密算法之一,具有比较高的安全性和加密效率

2.1.1 DES

DES(Data Encryption Standard)算法是一种对称加密算法,由IBM公司于1975年研发,是最早的一种广泛应用的对称加密算法之一。

DES 算法使用 56 位密钥对数据进行加密,加密过程中使用了置换、替换、异或等运算,具有较高的安全性

public class DES {
    
    

    private static final String DES_ALGORITHM = "DES";

    /**
     * DES加密
     *
     * @param data 待加密的数据
     * @param key  密钥,长度必须为8位
     * @return 加密后的数据,使用Base64编码
     */
    public static String encrypt(String data, String key) throws Exception {
    
    
        // 根据密钥生成密钥规范
        KeySpec keySpec = new DESKeySpec(key.getBytes());
        // 根据密钥规范生成密钥工厂
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);
        // 根据密钥工厂和密钥规范生成密钥
        SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);

        // 根据加密算法获取加密器
        Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
        // 初始化加密器,设置加密模式和密钥
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        // 加密数据
        byte[] encryptedData = cipher.doFinal(data.getBytes());
        // 对加密后的数据进行Base64编码
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * DES解密
     *
     * @param encryptedData 加密后的数据,使用Base64编码
     * @param key           密钥,长度必须为8位
     * @return 解密后的数据
     */
    public static String decrypt(String encryptedData, String key) throws Exception {
    
    
        // 根据密钥生成密钥规范
        KeySpec keySpec = new DESKeySpec(key.getBytes());
        // 根据密钥规范生成密钥工厂
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);
        // 根据密钥工厂和密钥规范生成密钥
        SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);

        // 对加密后的数据进行Base64解码
        byte[] decodedData = Base64.getDecoder().decode(encryptedData);
        // 根据加密算法获取解密器
        Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
        // 初始化解密器,设置解密模式和密钥
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        // 解密数据
        byte[] decryptedData = cipher.doFinal(decodedData);
        // 将解密后的数据转换为字符串
        return new String(decryptedData);
    }

    public static void main(String[] args) throws Exception {
    
    
        String data = "Hello World";
        String key = "12345678";

        String encryptedData = encrypt(data, key);
        System.out.println("加密后的数据:" + encryptedData);

        String decryptedData = decrypt(encryptedData, key);
        System.out.println("解密后的数据:" + decryptedData);
    }
}

DES 的算法速度较快,但是在安全性上面并不是最优选择,因为DES算法的密钥长度比较短,被暴力破解和差分攻击的风险比较高,一般推荐用一些更安全的对称加密算法,比如 3DES、AES

2.1.2 AES

AES(Advanced Encryption Standard)即高级加密标准,是一种对称加密算法,被广泛应用于数据加密和保护领域。AES 算法使用的密钥长度为 128 位、192 位或 256 位,比 DES 算法的密钥长度更长,安全性更高

public class AES {
    
    

    private static final String AES_ALGORITHM = "AES";
    // AES加密模式为CBC,填充方式为PKCS5Padding
    private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    // AES密钥为16位
    private static final String AES_KEY = "1234567890123456";
    // AES初始化向量为16位
    private static final String AES_IV = "abcdefghijklmnop";

    /**
     * AES加密
     *
     * @param data 待加密的数据
     * @return 加密后的数据,使用Base64编码
     */
    public static String encrypt(String data) throws Exception {
    
    
        // 将AES密钥转换为SecretKeySpec对象
        SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY.getBytes(), AES_ALGORITHM);
        // 将AES初始化向量转换为IvParameterSpec对象
        IvParameterSpec ivParameterSpec = new IvParameterSpec(AES_IV.getBytes());
        // 根据加密算法获取加密器
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        // 初始化加密器,设置加密模式、密钥和初始化向量
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        // 加密数据
        byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        // 对加密后的数据使用Base64编码
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * AES解密
     *
     * @param encryptedData 加密后的数据,使用Base64编码
     * @return 解密后的数据
     */
    public static String decrypt(String encryptedData) throws Exception {
    
    
        // 将AES密钥转换为SecretKeySpec对象
        SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY.getBytes(), AES_ALGORITHM);
        // 将AES初始化向量转换为IvParameterSpec对象
        IvParameterSpec ivParameterSpec = new IvParameterSpec(AES_IV.getBytes());
        // 根据加密算法获取解密器
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        // 初始化解密器,设置解密模式、密钥和初始化向量
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        // 对加密后的数据使用Base64解码
        byte[] decodedData = Base64.getDecoder().decode(encryptedData);
        // 解密数据
        byte[] decryptedData = cipher.doFinal(decodedData);
        // 返回解密后的数据
        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws Exception {
    
    
        String data = "Hello World";

        String encryptedData = encrypt(data);
        System.out.println("加密后的数据:" + encryptedData);

        String decryptedData = decrypt(encryptedData);
        System.out.println("解密后的数据:" + decryptedData);
    }
}

AES 算法采用的密钥长度更长,密钥空间更大,安全性更高,能够有效地抵抗暴力破解攻击。当然,因为密钥长度较长,需要的存储也更多。

对于对称加密算法而言,最大的痛点就在于密钥管理困难,相比而言,非对称加密就没有这个担忧

2.2 非对称加密

非对称加密算法需要两个密钥,这两个密钥互不相同,但是相互匹配,一个称为公钥,另一个称为私钥。使用其中的一个加密,则使用另一个进行解密。例如使用公钥加密,则需要使用私钥解密。

RSA 算法是是目前应用最广泛的非对称加密算法

public class RSA {
    
    

    private static final String RSA_ALGORITHM = "RSA";

    /**
     * 生成RSA密钥对
     *
     * @return RSA密钥对
     */
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
    
    
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        keyPairGenerator.initialize(2048); // 密钥大小为2048位
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 使用公钥加密数据
     *
     * @param data      待加密的数据
     * @param publicKey 公钥
     * @return 加密后的数据
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
    
    
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * 使用私钥解密数据
     *
     * @param encryptedData 加密后的数据
     * @param privateKey    私钥
     * @return 解密后的数据
     */
    public static String decrypt(String encryptedData, PrivateKey privateKey) throws Exception {
    
    
        byte[] decodedData = Base64.getDecoder().decode(encryptedData);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedData = cipher.doFinal(decodedData);
        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws Exception {
    
    
        KeyPair keyPair = generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        String data = "Hello World";

        String encryptedData = encrypt(data, publicKey);
        System.out.println("加密后的数据:" + encryptedData);

        String decryptedData = decrypt(encryptedData, privateKey);
        System.out.println("解密后的数据:" + decryptedData);
    }

}
  • 优点:安全性高,公钥可以公开,私钥必须保密,保证了数据的安全性;可用于数字签名、密钥协商等多种应用场景。

  • 缺点:加密、解密速度较慢,密钥长度越长,加密、解密时间越长;密钥长度过短容易被暴力破解,密钥长度过长则会增加计算量和存储空间的开销

猜你喜欢

转载自blog.csdn.net/sco5282/article/details/131455497