加密和解密

0 阅读1分钟

代码

demo源码

密码相关

明文、密文、加密、解密(公钥)、密钥(私钥)、加密和非对称加密、密码算法

加密方式

对称加密、非对称加密、哈希算法、混合加密

Image textImage text

对称加密

  • 特点: 加密和解密使用同一个密钥(就像用同一把钥匙锁门和开门)。
  • 优点: 速度快,效率高,适合加密大量数据。
  • 缺点: 密钥如何安全地传输给对方是一个难题(密钥分发问题)
DES加密

目前确认已是不安全的加密方式(64位密钥进行加密,8个字节,实际密钥长度56位,其中8位用于奇偶校验)

早期的标准,因为密钥长度较短(56位),现在已被认为不够安全,容易被暴力破解

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class DesDemo {

    public static void main(String[] args) {
        try {
            // 原文
            String plainText = "Hello, DES! 这是一段测试文本。";

            // 1. 生成 DES 密钥
            KeyGenerator keyGen = KeyGenerator.getInstance("DES");
            keyGen.init(56); // 指定密钥长度(实际是56位,加上8位奇偶校验共64位)
            SecretKey secretKey = keyGen.generateKey();

            // 也可以从字节数组恢复密钥(例如存储的密钥)
            // byte[] keyBytes = secretKey.getEncoded();
            // SecretKey keyFromBytes = new SecretKeySpec(keyBytes, "DES");

            System.out.println("原始文本: " + plainText);

            // 2. 加密
            byte[] cipherData = encrypt(plainText.getBytes("UTF-8"), secretKey);
            System.out.println("加密后 (Base64): " + Base64.getEncoder().encodeToString(cipherData));
            System.out.println("加密后 (Hex): " + bytesToHex(cipherData));

            // 3. 解密
            byte[] decryptedData = decrypt(cipherData, secretKey);
            String decryptedText = new String(decryptedData, "UTF-8");
            System.out.println("解密后文本: " + decryptedText);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用 DES 加密数据
     * @param data 待加密的字节数组
     * @param key  DES 密钥
     * @return 加密后的字节数组
     */
    public static byte[] encrypt(byte[] data, SecretKey key) throws Exception {
        Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * 使用 DES 解密数据
     * @param encryptedData 密文字节数组
     * @param key           DES 密钥
     * @return 解密后的字节数组
     */
    public static byte[] decrypt(byte[] encryptedData, SecretKey key) throws Exception {
        Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(encryptedData);
    }

    /**
     * 将字节数组转换为十六进制字符串(用于显示)
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
}
3DES加密

为了替代DES,对数据应用三次DES加密,安全性较高但速度较慢,逐渐被AES取代

package com.jysemel.top;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.util.Base64;

public class TripleDesDemo {

    public static void main(String[] args) {
        try {
            // 原文
            String plainText = "Hello, 3DES! 这是一段需要加密的文本。";

            // 1. 生成 3DES 密钥 (DESede)
            KeyGenerator keyGen = KeyGenerator.getInstance("DESede");
            // 3DES 密钥长度可选 112 或 168 位,通常使用 168 位(24 字节)
            keyGen.init(168);
            SecretKey secretKey = keyGen.generateKey();

            // 也可以从预先定义的字节数组恢复密钥(例如存储的密钥)
            // byte[] keyBytes = secretKey.getEncoded();
            // SecretKey keyFromBytes = new SecretKeySpec(keyBytes, "DESede");

            System.out.println("原始文本: " + plainText);

            // 2. 加密
            byte[] cipherData = encrypt(plainText.getBytes("UTF-8"), secretKey);
            System.out.println("加密后 (Base64): " + Base64.getEncoder().encodeToString(cipherData));
            System.out.println("加密后 (Hex): " + bytesToHex(cipherData));

            // 3. 解密
            byte[] decryptedData = decrypt(cipherData, secretKey);
            String decryptedText = new String(decryptedData, "UTF-8");
            System.out.println("解密后文本: " + decryptedText);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用 3DES 加密数据
     * @param data 待加密的字节数组
     * @param key  3DES 密钥
     * @return 加密后的字节数组
     */
    public static byte[] encrypt(byte[] data, SecretKey key) throws Exception {
        // 算法/模式/填充:ECB 模式 + PKCS5Padding(可根据需要改为 CBC 等)
        Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * 使用 3DES 解密数据
     * @param encryptedData 密文字节数组
     * @param key           3DES 密钥
     * @return 解密后的字节数组
     */
    public static byte[] decrypt(byte[] encryptedData, SecretKey key) throws Exception {
        Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(encryptedData);
    }

    /**
     * 将字节数组转换为十六进制字符串(用于显示)
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
}
AES加密

一种分组加密算法,将要加密的数据分成固定大小的数据块处理,每个数据库128位(16字节)

目前最常用的对称加密算法,用于文件加密、Wi-Fi加密(WPA2)、硬盘加密等

package com.jysemel.top;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.security.SecureRandom;
import java.util.Base64;

public class AesDemo {

    public static void main(String[] args) {
        try {
            // 原文
            String plainText = "Hello, AES! 这是一段需要加密的敏感信息。";

            // 1. 生成 AES 密钥(128位)
            KeyGenerator keyGen = KeyGenerator.getInstance("AES");
            keyGen.init(128); // 也可以使用 192 或 256 位(需要 JCE 无限强度策略文件支持 256)
            SecretKey secretKey = keyGen.generateKey();

            // 2. 生成随机 IV(初始化向量),长度为 16 字节
            byte[] ivBytes = new byte[16];
            SecureRandom secureRandom = new SecureRandom();
            secureRandom.nextBytes(ivBytes);
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

            System.out.println("原始文本: " + plainText);
            System.out.println("使用的 IV (Base64): " + Base64.getEncoder().encodeToString(ivBytes));

            // 3. 加密
            byte[] cipherData = encrypt(plainText.getBytes("UTF-8"), secretKey, ivSpec);
            System.out.println("加密后 (Base64): " + Base64.getEncoder().encodeToString(cipherData));
            System.out.println("加密后 (Hex): " + bytesToHex(cipherData));

            // 4. 解密(使用相同的密钥和 IV)
            byte[] decryptedData = decrypt(cipherData, secretKey, ivSpec);
            String decryptedText = new String(decryptedData, "UTF-8");
            System.out.println("解密后文本: " + decryptedText);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用 AES/CBC/PKCS5Padding 加密
     * @param data 明文数据
     * @param key  AES 密钥
     * @param iv   初始化向量
     * @return 密文
     */
    public static byte[] encrypt(byte[] data, SecretKey key, IvParameterSpec iv) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);
        return cipher.doFinal(data);
    }

    /**
     * 使用 AES/CBC/PKCS5Padding 解密
     * @param encryptedData 密文
     * @param key           AES 密钥
     * @param iv            初始化向量
     * @return 明文
     */
    public static byte[] decrypt(byte[] encryptedData, SecretKey key, IvParameterSpec iv) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        return cipher.doFinal(encryptedData);
    }

    /**
     * 将字节数组转换为十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
}

非对称加密

  • 特点: 加密和解密使用不同的密钥(一对密钥:公钥和私钥)。公钥可以公开给别人,私钥自己秘密保存。
  • 优点: 解决了密钥分发问题,安全性极高。
  • 缺点: 计算复杂,速度非常慢,通常只用来加密少量数据(如对称加密的密钥)
RSA

RSA算法,RSA算法是公开密钥加密算法,RSA算法的密钥长度可以自己定义

package com.jysemel.top;

import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;

public class RsaDemo {

    public static void main(String[] args) {
        try {
            // 原文(RSA 加密的明文长度有限,这里使用较短文本)
            String plainText = "Hello, RSA! 这是一段测试。";

            // 1. 生成 RSA 密钥对(2048位)
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
            keyPairGen.initialize(2048); // 推荐使用 2048 位或更高
            KeyPair keyPair = keyPairGen.generateKeyPair();
            PublicKey publicKey = keyPair.getPublic();
            PrivateKey privateKey = keyPair.getPrivate();

            System.out.println("原始文本: " + plainText);
            System.out.println("公钥 (Base64): " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
            System.out.println("私钥 (Base64): " + Base64.getEncoder().encodeToString(privateKey.getEncoded()));

            // 2. 使用公钥加密
            byte[] cipherData = encrypt(plainText.getBytes("UTF-8"), publicKey);
            System.out.println("加密后 (Base64): " + Base64.getEncoder().encodeToString(cipherData));

            // 3. 使用私钥解密
            byte[] decryptedData = decrypt(cipherData, privateKey);
            String decryptedText = new String(decryptedData, "UTF-8");
            System.out.println("解密后文本: " + decryptedText);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用公钥加密
     * @param data      明文数据
     * @param publicKey 公钥
     * @return 密文
     */
    public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // 常用填充方式
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 使用私钥解密
     * @param encryptedData 密文
     * @param privateKey    私钥
     * @return 明文
     */
    public static byte[] decrypt(byte[] encryptedData, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(encryptedData);
    }
    
    // 签名
    public static byte[] sign(byte[] data, PrivateKey privateKey) throws Exception {
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initSign(privateKey);
        sig.update(data);
        return sig.sign();
    }

    // 验签
    public static boolean verify(byte[] data, byte[] signature, PublicKey publicKey) throws Exception {
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(publicKey);
        sig.update(data);
        return sig.verify(signature);
    }
}

DSA

DSA算法,DSA算法是数字签名算法,DSA算法的密钥长度可以自己定义

  • DSA 与 RSA 不同,只能用于数字签名,不能用于加密或密钥交换

    package com.jysemel.top;

    import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.util.Base64;

    public class DsaDemo {

    public static void main(String[] args) {
        try {
            // 待签名的原始消息
            String message = "Hello, DSA! 这是一条需要签名的消息。";
    
            // 1. 生成 DSA 密钥对(1024位)
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DSA");
            keyPairGen.initialize(1024); // 也可使用 2048 位(需 JDK 支持)
            KeyPair keyPair = keyPairGen.generateKeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
    
            System.out.println("原始消息: " + message);
            System.out.println("私钥 (Base64): " + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
            System.out.println("公钥 (Base64): " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
    
            // 2. 使用私钥对消息进行签名
            Signature signer = Signature.getInstance("SHA1withDSA"); // 也可用 SHA256withDSA
            signer.initSign(privateKey);
            signer.update(message.getBytes("UTF-8"));
            byte[] signature = signer.sign();
            System.out.println("签名结果 (Base64): " + Base64.getEncoder().encodeToString(signature));
            System.out.println("签名结果 (Hex): " + bytesToHex(signature));
    
            // 3. 使用公钥验证签名
            Signature verifier = Signature.getInstance("SHA1withDSA");
            verifier.initVerify(publicKey);
            verifier.update(message.getBytes("UTF-8"));
            boolean isVerified = verifier.verify(signature);
            System.out.println("签名验证结果: " + (isVerified ? "成功 ✓" : "失败 ✗"));
    
            // 4. 模拟篡改消息后的验证
            String tamperedMessage = message + "篡改";
            verifier.initVerify(publicKey);
            verifier.update(tamperedMessage.getBytes("UTF-8"));
            boolean isTamperedVerified = verifier.verify(signature);
            System.out.println("篡改消息验证结果: " + (isTamperedVerified ? "成功 ✓" : "失败 ✗(符合预期)"));
    
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 将字节数组转换为十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
    

    }

ECC

密钥长度较短、但安全性同样高,适合对性能要求高的场景

哈希算法

不可逆加密算法,单向的加密算法、散列算法。相同的明文数据加密之后得到的密文相同,只能暴力破解、撞库攻击

  • 特点: 将任意长度的数据,计算成固定长度的唯一字符串(哈希值)。这是一个单向过程,无法从哈希值还原原始数据。
  • 用途: 验证数据完整性、存储密码、数字签名
MD5 算法
package com.jysemel.top;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Md5Demo {

    public static void main(String[] args) {
        String input = "Hello, MD5! 这是一段测试文本。";

        try {
            // 计算 MD5 哈希
            String md5Hash = calculateMD5(input);
            System.out.println("原始字符串: " + input);
            System.out.println("MD5 哈希值: " + md5Hash);

            // 演示空字符串的 MD5
            String emptyHash = calculateMD5("");
            System.out.println("空字符串 MD5: " + emptyHash);

        } catch (NoSuchAlgorithmException e) {
            System.err.println("MD5 算法不可用: " + e.getMessage());
        }
    }

    /**
     * 计算字符串的 MD5 哈希值(十六进制字符串)
     * @param input 输入字符串
     * @return 32 位小写十六进制哈希值
     * @throws NoSuchAlgorithmException 如果 JDK 不支持 MD5(几乎不会发生)
     */
    public static String calculateMD5(String input) throws NoSuchAlgorithmException {
        // 获取 MD5 MessageDigest 实例
        MessageDigest md = MessageDigest.getInstance("MD5");

        // 更新摘要数据(UTF-8 编码)
        byte[] messageDigest = md.digest(input.getBytes());

        // 将字节数组转换为十六进制字符串
        StringBuilder hexString = new StringBuilder();
        for (byte b : messageDigest) {
            // 将每个字节转换为两位十六进制
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}
SHA-1 算法

SHA-1 已被证实存在碰撞攻击,不应用于数字签名、证书或任何安全关键场景。本示例仅供学习或兼容遗留系统使用。实际开发请使用 SHA-256 或更高强度的哈希算法

package com.jysemel.top;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;

public class Sha1Demo {

    public static void main(String[] args) {
        String input = "Hello, SHA-1! 这是一段测试文本。";

        try {
            // 计算 SHA-1 哈希
            String sha1Hash = calculateSHA1(input);
            System.out.println("原始字符串: " + input);
            System.out.println("SHA-1 哈希值: " + sha1Hash);

            // 演示空字符串的 SHA-1
            String emptyHash = calculateSHA1("");
            System.out.println("空字符串 SHA-1: " + emptyHash);

        } catch (NoSuchAlgorithmException e) {
            System.err.println("SHA-1 算法不可用: " + e.getMessage());
        }
    }

    /**
     * 计算字符串的 SHA-1 哈希值(十六进制字符串)
     * @param input 输入字符串
     * @return 40 位小写十六进制哈希值
     * @throws NoSuchAlgorithmException 如果 JDK 不支持 SHA-1(几乎不会发生)
     */
    public static String calculateSHA1(String input) throws NoSuchAlgorithmException {
        // 获取 SHA-1 MessageDigest 实例
        MessageDigest md = MessageDigest.getInstance("SHA-1");

        // 更新摘要数据(指定 UTF-8 编码避免跨平台问题)
        byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));

        // 将字节数组转换为十六进制字符串
        StringBuilder hexString = new StringBuilder();
        for (byte b : messageDigest) {
            // 将每个字节转换为两位十六进制
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}

SHA-256 算法
package com.jysemel.top;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;

public class Sha256Demo {

    public static void main(String[] args) {
        String input = "Hello, SHA-256! 这是一段测试文本。";

        try {
            // 计算 SHA-256 哈希
            String sha256Hash = calculateSHA256(input);
            System.out.println("原始字符串: " + input);
            System.out.println("SHA-256 哈希值: " + sha256Hash);

            // 演示空字符串的 SHA-256
            String emptyHash = calculateSHA256("");
            System.out.println("空字符串 SHA-256: " + emptyHash);

        } catch (NoSuchAlgorithmException e) {
            System.err.println("SHA-256 算法不可用: " + e.getMessage());
        }
    }

    /**
     * 计算字符串的 SHA-256 哈希值(十六进制字符串)
     * @param input 输入字符串
     * @return 64 位小写十六进制哈希值
     * @throws NoSuchAlgorithmException 如果 JDK 不支持 SHA-256(通常都支持)
     */
    public static String calculateSHA256(String input) throws NoSuchAlgorithmException {
        // 获取 SHA-256 MessageDigest 实例
        MessageDigest md = MessageDigest.getInstance("SHA-256");

        // 更新摘要数据(指定 UTF-8 编码避免跨平台问题)
        byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));

        // 将字节数组转换为十六进制字符串
        StringBuilder hexString = new StringBuilder();
        for (byte b : messageDigest) {
            // 将每个字节转换为两位十六进制
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}

混合加密

在实际应用中(比如你正在使用的HTTPS协议),通常不会只用一种加密方式,而是混合使用:

  • 使用非对称加密:安全地传输一个临时生成的对称密钥(解决密钥传递问题)。
  • 使用对称加密:用这个协商好的密钥,加密实际传输的大量数据(解决加解密速度问题)。
  • 使用哈希函数:验证数据在传输过程中是否被篡改

国密

国密算法是国家密码管理局制定的国产密码标准,主要包括SM2(非对称)、SM3(哈希)和SM4(对称)三种核心算法

  • maven 依赖

    org.bouncycastle bcprov-jdk15on 1.70
  • 示例

    package com.jysemel.top;

    import org.bouncycastle.jce.provider.BouncyCastleProvider;

    import javax.crypto.Cipher; import java.security.*; import java.security.spec.ECGenParameterSpec;

    public class SM2Demo {

    static {
        // 添加 Bouncy Castle 作为安全提供者
        Security.addProvider(new BouncyCastleProvider());
    }
    
    public static void main(String[] args) throws Exception {
        String plainText = "Hello, SM2! 这是一段需要加密的文本。";
    
        // 1. 生成 SM2 密钥对 (使用 sm2p256v1 曲线)
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
        ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
        keyPairGenerator.initialize(sm2Spec, new SecureRandom());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
    
        System.out.println("原始文本: " + plainText);
    
        // 2. 使用公钥加密
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherData = cipher.doFinal(plainText.getBytes("UTF-8"));
        System.out.println("加密后 (Base64): " + java.util.Base64.getEncoder().encodeToString(cipherData));
    
        // 3. 使用私钥解密
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedData = cipher.doFinal(cipherData);
        String decryptedText = new String(decryptedData, "UTF-8");
        System.out.println("解密后文本: " + decryptedText);
    }
    

    }

    package com.jysemel.top;

    import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.Security;

    public class SM3Demo {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    
    public static void main(String[] args) {
        String plainText = "Hello, SM3! 这是一段需要哈希的文本。";
        System.out.println("原始文本: " + plainText);
    
        // 计算 SM3 哈希值
        byte[] hash = sm3Hash(plainText.getBytes());
        System.out.println("SM3 哈希值 (Hex): " + bytesToHex(hash));
        System.out.println("SM3 哈希值 (Base64): " + java.util.Base64.getEncoder().encodeToString(hash));
    }
    
    public static byte[] sm3Hash(byte[] input) {
        SM3Digest digest = new SM3Digest();
        digest.update(input, 0, input.length);
        byte[] hash = new byte[digest.getDigestSize()];
        digest.doFinal(hash, 0);
        return hash;
    }
    
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
    

    }

    package com.jysemel.top;

    import org.bouncycastle.crypto.engines.SM4Engine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.SecureRandom; import java.security.Security;

    public class SM4Demo {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    
    public static void main(String[] args) throws Exception {
        String plainText = "Hello, SM4! 这是一段需要加密的文本。";
        // 生成一个 128 位的随机密钥和 IV
        byte[] key = new byte[16];
        byte[] iv = new byte[16];
        new SecureRandom().nextBytes(key);
        new SecureRandom().nextBytes(iv);
    
        System.out.println("原始文本: " + plainText);
    
        // 加密
        byte[] cipherData = sm4CbcEncrypt(plainText.getBytes("UTF-8"), key, iv);
        System.out.println("加密后 (Base64): " + java.util.Base64.getEncoder().encodeToString(cipherData));
    
        // 解密
        byte[] decryptedData = sm4CbcDecrypt(cipherData, key, iv);
        String decryptedText = new String(decryptedData, "UTF-8");
        System.out.println("解密后文本: " + decryptedText);
    }
    
    public static byte[] sm4CbcEncrypt(byte[] input, byte[] key, byte[] iv) throws Exception {
        // 创建 SM4 引擎并包装为 CBC 模式 + PKCS7 填充
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
                new CBCBlockCipher(new SM4Engine()));
        cipher.init(true, new ParametersWithIV(new KeyParameter(key), iv));
        return process(cipher, input);
    }
    
    public static byte[] sm4CbcDecrypt(byte[] input, byte[] key, byte[] iv) throws Exception {
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
                new CBCBlockCipher(new SM4Engine()));
        cipher.init(false, new ParametersWithIV(new KeyParameter(key), iv));
        return process(cipher, input);
    }
    
    private static byte[] process(PaddedBufferedBlockCipher cipher, byte[] input) throws Exception {
        byte[] output = new byte[cipher.getOutputSize(input.length)];
        int bytesProcessed = cipher.processBytes(input, 0, input.length, output, 0);
        int bytesFinalized = cipher.doFinal(output, bytesProcessed);
        byte[] finalOutput = new byte[bytesProcessed + bytesFinalized];
        System.arraycopy(output, 0, finalOutput, 0, finalOutput.length);
        return finalOutput;
    }
    

    }

编码

常见编码方式Base64编码、URL编码等

Base64编码

将任意二进制数据编码为ASCII字符串、将二进制数据存储为文本文件,或者将二进制数据进行网络传输

  • 由64个字符组成编码集,26个大写字母、26个小写字母、10个数字、2个特殊字符(+,/),当原始数据不够三个字节时,用=进行填充
  • 编码

将二进制数据转为字符

  • 解码

将字符转为二进制数据

URL编码

主要将特殊字符串和不可打印的字符转换为百分号(%)加上两位十六进制的形式,以确保字符在URL中可以正确传递和解析

package com.jysemel.top;

import java.util.Base64;

public class Base64Demo {
    public static void main(String[] args) {
        String original = "Hello, 编码世界!";

        // 编码
        String encoded = Base64.getEncoder().encodeToString(original.getBytes());
        System.out.println("Base64 编码: " + encoded);

        // 解码
        byte[] decodedBytes = Base64.getDecoder().decode(encoded);
        String decoded = new String(decodedBytes);
        System.out.println("解码后: " + decoded);

        // URL 安全的 Base64(替换 + 和 / 为 - 和 _)
        String urlSafe = Base64.getUrlEncoder().encodeToString(original.getBytes());
        System.out.println("URL 安全 Base64: " + urlSafe);
    }
}


package com.jysemel.top;

/**
 * Hex 将每个字节表示为两个十六进制字符(0-9A-F),常用于查看二进制数据或生成哈希值的字符串表示
 */
public class HexDemo {
    public static void main(String[] args) {
        String original = "Hello Hex";

        // 编码:字节 -> 十六进制字符串
        byte[] bytes = original.getBytes();
        StringBuilder hex = new StringBuilder();
        for (byte b : bytes) {
            hex.append(String.format("%02x", b));
        }
        System.out.println("Hex 编码: " + hex);

        // 解码:十六进制字符串 -> 字节
        String hexStr = hex.toString();
        byte[] decodedBytes = new byte[hexStr.length() / 2];
        for (int i = 0; i < decodedBytes.length; i++) {
            int index = i * 2;
            int val = Integer.parseInt(hexStr.substring(index, index + 2), 16);
            decodedBytes[i] = (byte) val;
        }
        String decoded = new String(decodedBytes);
        System.out.println("解码后: " + decoded);
    }
}


package com.jysemel.top;
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

public class URLEncodingDemo {
    public static void main(String[] args) throws Exception {
        String original = "name=张三&age=20";

        // 编码
        String encoded = URLEncoder.encode(original, String.valueOf(StandardCharsets.UTF_8));
        System.out.println("URL 编码: " + encoded); // name%3D%E5%BC%A0%E4%B8%89%26age%3D20

        // 解码
        String decoded = URLDecoder.decode(encoded, String.valueOf(StandardCharsets.UTF_8));
        System.out.println("解码后: " + decoded);
    }
}