Bootstrap

[密码学实战]Java实现SM4加解密(ecb,cbc)及工具验证

前言

在现代信息安全领域,数据加密技术是保障数据安全的核心手段之一。SM4作为中国国家密码管理局发布的对称加密算法,因其高效性和安全性,广泛应用于金融、政务、通信等领域。本文将详细介绍如何使用Java实现SM4的加解密操作,并深入探讨SM4的几种常见加密模式及其原理。

一、结果验证

1.代码运行结果

1.1 ECB模式加密结果

在这里插入图片描述

1.2 CBC模式加密结果

在这里插入图片描述

2.工具运行结果

2.1 ECB模式解密结果
在这里插入图片描述

2.2 CBC模式解密结果

在这里插入图片描述

二、SM4算法简介

SM4是一种分组密码算法,分组长度和密钥长度均为128位(16字节)。它采用与AES类似的轮函数结构,但具体的S盒和线性变换与AES不同,因此具有独特的加密性能。SM4支持多种加密模式,包括ECB、CBC、CTR、CFB和OFB等,以适应不同的应用场景。

三、SM4加密模式详解

1. ECB模式(电子密码本模式)

原理:
ECB模式是最简单的加密模式,它将明文分成若干个固定大小的块(如128位),然后对每个块独立进行加密。由于每个块的加密是独立的,相同的明文块会被加密成相同的密文块。

优点:

  • 实现简单,易于并行处理。

缺点:

  • 安全性较低,相同的明文块会产生相同的密文块,容易被分析。
  • 不适合加密包含重复模式的数据,如图像或文本。

适用场景:

  • 适用于加密随机数据或不需要高安全性的场景。
2. CBC模式(加密分组链接模式)

原理:
CBC模式通过引入初始向量(IV)和分组链接机制,解决了ECB模式的安全性问题。每个明文分组在加密前会与前一个密文分组进行异或操作,然后再进行加密。第一个分组使用初始向量(IV)进行异或操作。

优点:

  • 相同的明文块在不同的位置会被加密成不同的密文块,安全性较高。
  • 适合加密包含重复模式的数据。

缺点:

  • 加密过程是串行的,不能并行处理。
  • 需要管理和传输初始向量(IV)。

适用场景:

  • 适用于需要较高安全性的场景,如文件加密、网络通信加密等。
3. CTR模式(计数器模式)

原理:
CTR模式将计数器的值与密钥结合生成伪随机序列,然后与明文进行异或操作生成密文。计数器可以是任意值,但必须保证唯一性。通常,计数器会从某个初始值开始递增。

优点:

  • 加密和解密过程可以并行处理,效率较高。
  • 不需要填充数据,适合加密任意长度的数据。

缺点:

  • 计数器的管理较为复杂,必须保证唯一性。
  • 如果计数器重复使用,会导致安全性问题。

适用场景:

  • 适用于需要高效加密的场景,如实时通信、流媒体加密等。
4. CFB模式(密码反馈模式)

原理:
CFB模式将密文分组的一部分反馈到加密过程中,通过与明文异或生成密文。CFB模式可以将分组密码转换为自同步的流密码。每个分组的加密依赖于前一个分组的密文。

优点:

  • 适合处理小数据块,具有良好的错误传播特性。
  • 可以实现自同步,适合传输过程中可能出现数据丢失的场景。

缺点:

  • 加密过程是串行的,不能并行处理。
  • 需要管理和传输初始向量(IV)。

适用场景:

  • 适用于需要自同步特性的场景,如实时通信、流媒体加密等。
5. OFB模式(输出反馈模式)

原理:
OFB模式将加密器的输出反馈到输入中,生成伪随机序列,然后与明文异或生成密文。OFB模式与CTR模式类似,但它更适合处理小数据块。

优点:

  • 加密和解密过程可以并行处理,效率较高。
  • 不需要填充数据,适合加密任意长度的数据。

缺点:

  • 如果初始向量(IV)重复使用,会导致安全性问题。
  • 加密过程是串行的,不能并行处理。

适用场景:

  • 适用于需要高效加密的场景,如实时通信、流媒体加密等。

四、Java实现SM4加解密

1. 引入依赖

pom.xml中添加以下依赖:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.65</version>
</dependency>
2. 实现代码
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Base64;

public class SM4EncDec {

    static {
        // 添加Bouncy Castle提供者
        Security.addProvider(new BouncyCastleProvider());
    }

    // 算法名称
    public static final String ALGORITHM_NAME = "SM4";
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
    public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS7Padding";

    /**
     * 生成128位密钥
     */
    public static String generateKey() {
        byte[] key = new byte[16]; // 128位密钥
        new java.security.SecureRandom().nextBytes(key);
        return Hex.toHexString(key);
    }

    /**
     * ECB模式加密
     *
     * @param keyHex  16字节密钥(Hex格式)
     * @param data    明文数据
     * @return 加密后的Hex字符串
     */
    public static String encryptECB(String keyHex, String data) throws Exception {
        byte[] keyData = Hex.decode(keyHex);
        byte[] dataBytes = data.getBytes("UTF-8");

        // 初始化Cipher
        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, "BC");
        SecretKeySpec keySpec = new SecretKeySpec(keyData, ALGORITHM_NAME);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);

        // 加密
        byte[] encrypted = cipher.doFinal(dataBytes);
        return Hex.toHexString(encrypted);
    }

    /**
     * ECB模式解密
     *
     * @param keyHex        16字节密钥(Hex格式)
     * @param cipherDataHex 密文数据(Hex格式)
     * @return 解密后的字符串
     */
    public static String decryptECB(String keyHex, String cipherDataHex) throws Exception {
        byte[] keyData = Hex.decode(keyHex);
        byte[] cipherData = Hex.decode(cipherDataHex);

        // 初始化Cipher
        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, "BC");
        SecretKeySpec keySpec = new SecretKeySpec(keyData, ALGORITHM_NAME);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);

        // 解密
        byte[] decrypted = cipher.doFinal(cipherData);
        return new String(decrypted, "UTF-8");
    }

    /**
     * CBC模式加密
     *
     * @param keyHex  16字节密钥(Hex格式)
     * @param data    明文数据
     * @param ivHex   16字节初始向量(Hex格式)
     * @return 加密后的Hex字符串
     */
    public static String encryptCBC(String keyHex, String data, String ivHex) throws Exception {
        byte[] keyData = Hex.decode(keyHex);
        byte[] dataBytes = data.getBytes("UTF-8");
        byte[] ivBytes = Hex.decode(ivHex);

        // 初始化Cipher
        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_CBC_PADDING, "BC");
        SecretKeySpec keySpec = new SecretKeySpec(keyData, ALGORITHM_NAME);
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

        // 加密
        byte[] encrypted = cipher.doFinal(dataBytes);
        return Hex.toHexString(encrypted);
    }

    /**
     * CBC模式解密
     *
     * @param keyHex        16字节密钥(Hex格式)
     * @param cipherDataHex 密文数据(Hex格式)
     * @param ivHex         16字节初始向量(Hex格式)
     * @return 解密后的字符串
     */
    public static String decryptCBC(String keyHex, String cipherDataHex, String ivHex) throws Exception {
        byte[] keyData = Hex.decode(keyHex);
        byte[] cipherData = Hex.decode(cipherDataHex);
        byte[] ivBytes = Hex.decode(ivHex);

        // 初始化Cipher
        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_CBC_PADDING, "BC");
        SecretKeySpec keySpec = new SecretKeySpec(keyData, ALGORITHM_NAME);
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

        // 解密
        byte[] decrypted = cipher.doFinal(cipherData);
        return new String(decrypted, "UTF-8");
    }

    public static void main(String[] args) throws Exception {
        // 生成密钥
        String key = generateKey();
        System.out.println("生成的密钥: " + key);

        String plainText = "Hello, SM4!";
        System.out.println("明文: " + plainText);

        // ECB模式加密解密
        String cipherTextECB = encryptECB(key, plainText);
        System.out.println("ECB模式加密结果: " + cipherTextECB);
        String decryptedTextECB = decryptECB(key, cipherTextECB);
        System.out.println("ECB模式解密结果: " + decryptedTextECB);

        // CBC模式加密解密
        String iv = "1234567812345678"; // 初始向量
        String cipherTextCBC = encryptCBC(key, plainText, iv);
        System.out.println("CBC模式加密结果: " + cipherTextCBC);
        String decryptedTextCBC = decryptCBC(key, cipherTextCBC, iv);
        System.out.println("CBC模式解密结果: " + decryptedTextCBC);
    }
}

五、总结

在实际项目中常用CBC模式,ECB模式安全性较低。密评时ECB模式会要求整改为CBC模式。

如果觉得不错,记得点赞收藏哦!

;