用户猫哥 發表於 2025-12-8 15:16:00

JAVA 使用国密 SM4 加解密

<h1>SM4算法</h1>
<p>百度百科</p>
<p>中华人民共和国政府采用的分组密码标准<br><span class="text_rxnHx J-lemma-content-lemma-text" data-text="true">SM4.0(原名SMS4.0)是</span><span class="text_rxnHx J-lemma-content-lemma-text" data-text="true">中华人民共和国国家密码管理局</span><span class="text_rxnHx J-lemma-content-lemma-text" data-text="true">于2012年3月21日发布的分组密码标准,对应行业标准为GM/T 0002-2012,采用128位分组长度和128位密钥结构,加密过程基于32轮非线性迭代,并配置固定8比特输入8比特输出的S盒,主要应用于商用数据加密领域</span></p>
<p><span class="text_rxnHx J-lemma-content-lemma-text" data-text="true">在密码学领域,国密算法 SM4 是我国自主研发的分组对称加密算法,凭借 128 位分组长度、128 位密钥长度的设计,在金融、政务、物联网等领域广泛应用。但分组密码本身仅能处理固定长度(SM4 为 128 位)的明文数据,而现实中需要加密的文件、数据流、存储块等往往是任意长度的。为解决这一问题,“工作模式” 应运而生 —— 它相当于分组密码的 “应用框架”,定义了如何将固定长度的分组加密逻辑扩展到任意长度数据,同时兼顾安全性、性能与实际场景需求。SM4 的 8 种主流工作模式:ECB、CBC、CFB、OFB、CTR、GCM、CCM 与 XTS<br></span></p>
<p>本文简单介绍SM4以下常用的加解密模式</p>
<ul>
<li>ECB模式(电子密码本):工作原理:将明文分成固定长度的块(如SM4的16字节),每个块独立加密,互不影响。相同明文块会生成相同的密文块
<ul>
<li>优点:ECB模块可以并行处理数据。</li>
<li>缺点:同样原文生成同样的密文,并不能很好地保护数据。</li>
<li>安全性:存在致命缺陷 —— 相同明文分组会生成相同密文分组。例如用 ECB 加密图片,若图片中有重复像素块(如背景),密文会呈现明显的 “块重复”,攻击者可通过统计分析还原明文结构,甚至篡改单个密文块(对应明文块也会被篡改,不影响其他块);</li>
<li>适用场景:仅用于加密 “短且唯一” 的数据(如密钥封装、固定标识),绝对禁止用于文件、数据流等大数据加密。</li>
</ul>
</li>
<li>CBC模式(密码分组链接模式):工作原理:通过 “链式关联” 解决 ECB 的关联性问题:
<ol>
<li>明文分组按 128 位拆分,最后一块补全;</li>
<li>第一个明文分组与 “初始化向量(IV)” 进行异或运算,再用 SM4 加密得到第一个密文分组;</li>
<li>后续每个明文分组先与前一个密文分组异或,再加密,形成 “前密文影响后明文” 的链式结构。</li>
</ol>
</li>
<li>CBC模式特点和应用<br>
<ul>
<li>安全性:相同明文分组因 “前密文异或” 的干扰,会生成不同密文,抵御统计分析攻击;但密文块被篡改后,会影响后续所有明文块的解密(链式传播);</li>
<li>性能:加密无法并行(需等待前一个密文块),解密可并行(已知密文块即可独立解密,再与前一个密文块异或);</li>
<li>IV要求:需随机生成且唯一(无需保密),若 IV 重复,相同明文会生成相同密文;</li>
<li>适用场景:文件加密、早期 SSL/TLS 协议(如 TLS 1.0)、IPsec VPN,适合对并行性要求不高的场景。</li>
</ul>
</li>
<li>CFB模式:将分组密码转化为<code>流密码</code></li>
</ul>
<p>其余工作模式可参考博客:https://blog.csdn.net/openHiTLS/article/details/151288800</p>
<p>以下使用Bouncy Castle 开源加密库进行加解密</p>
<pre class="language-xml highlighter-hljs" data-dark-theme="true"><code>&lt;!-- Maven 方式 --&gt;
&lt;!--Bouncy Castle 开源加密库      --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.bouncycastle&lt;/groupId&gt;
    &lt;artifactId&gt;bcprov-jdk15on&lt;/artifactId&gt;
    &lt;version&gt;1.70&lt;/version&gt;
&lt;/dependency&gt;

&lt;!-- Gradle 方式 --&gt;
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'</code></pre>
<h2>ECB模式</h2>
<details>
<summary>SM4Utils</summary>
<pre class="language-java highlighter-hljs" data-dark-theme="true"><code>&nbsp;package com.isoftstone.cascade02.utils;


import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

/**
* @Author:wk
* @Slogan:无论风雨,和自己一决胜负吧
* @Create:2025/12/5/18:16
* @Description:SM4算法 ECB 模式
* @Version:1.0
*/
public class SM4Utils {

    public final static String KEY = "4c73aae48f4d5edba3a365837904dbc8";

    private static final String ALGORITHM = "SM4";
    private static final String TRANSFORM= "SM4/ECB/PKCS5Padding";


    static {
      Security.addProvider(new BouncyCastleProvider());
    }

    /** 生成 128-bit(16 字节)SM4 密钥,返回 Base64 字符串 */
    /*public static String generateKey() throws Exception {
      KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, "BC");
      kg.init(128, new SecureRandom());
      byte[] keyBytes = kg.generateKey().getEncoded();
      return Base64.getEncoder().encodeToString(keyBytes);
    }*/

    /** 生成UUID随机数,作为16进制密钥 */
    public static String generateKey() throws Exception {
      return CommonUtils.getUUID();
//      byte[] keyBytes = kg.generateKey().getEncoded();
//      return Base64.getEncoder().encodeToString(keyBytes);
    }

    /** 加密:明文 + Base64 密钥 → Base64 密文 */
    public static String encrypt(String plainText, String base64Key) {
      String encrypt = "";
      try {
            byte[] key = Hex.decodeHex(base64Key);
//      byte[] key = Base64.getDecoder().decode(base64Key);
            SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
            Cipher cipher = Cipher.getInstance(TRANSFORM, "BC");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
            encrypt = Base64.getEncoder().encodeToString(encrypted);
      } catch (DecoderException e) {
            e.printStackTrace();
      } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
      } catch (NoSuchProviderException e) {
            e.printStackTrace();
      } catch (NoSuchPaddingException e) {
            e.printStackTrace();
      } catch (InvalidKeyException e) {
            e.printStackTrace();
      } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
      } catch (BadPaddingException e) {
            e.printStackTrace();
      }
      return encrypt;
    }

    /** 解密:Base64 密文 + Base64 密钥 → 明文 */
    public static String decrypt(String base64Cipher, String base64Key) {
      String decrypt = "";
      try {
            byte[] key = Hex.decodeHex(base64Key);
//      byte[] key = Base64.getDecoder().decode(base64Key);
            SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
            Cipher cipher = Cipher.getInstance(TRANSFORM, "BC");
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(base64Cipher));
            decrypt = new String(decrypted, StandardCharsets.UTF_8);
      } catch (DecoderException e) {
            e.printStackTrace();
      } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
      } catch (NoSuchProviderException e) {
            e.printStackTrace();
      } catch (NoSuchPaddingException e) {
            e.printStackTrace();
      } catch (InvalidKeyException e) {
            e.printStackTrace();
      } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
      } catch (BadPaddingException e) {
            e.printStackTrace();
      }
      return decrypt;
    }

    /* ------ 简单测试 ------ */
    public static void main(String[] args) throws Exception {
      String plain = "你好,国密 SM4!";
      String key= generateKey();
      String cipher = encrypt(plain, key);
      String result = decrypt(cipher, key);

      System.out.println("密钥 :" + key);
      System.out.println("密钥 :" + KEY);
      System.out.println("明文 :" + plain);
      System.out.println("密文 :" + cipher);
      System.out.println("解密 :" + result);
    }

}</code></pre>
</details>
<h2>CBC模式</h2>
<details>
<summary>SM4IVUtils</summary>
<pre class="language-java highlighter-hljs" data-dark-theme="true"><code>package com.isoftstone.cascade02.utils;


import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

/**
* @Author:wk
* @Slogan:无论风雨,和自己一决胜负吧
* @Create:2025/12/5/18:16
* @Description:SM4 IV 算法
* @Version:1.0
*/
public class SM4IVUtils {

    public final static String KEY = "4c73aae48f4d5edba3a365837904dbc8";

    private static final String ALGORITHM = "SM4";
    private static final String TRANSFORM= "SM4/CBC/PKCS5Padding";

    static {
      Security.addProvider(new BouncyCastleProvider());
    }

    /** 生成 128-bit(16 字节)SM4 密钥,返回 Base64 字符串 */
    /*public static String generateKey() throws Exception {
      KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, "BC");
      kg.init(128, new SecureRandom());
      byte[] keyBytes = kg.generateKey().getEncoded();
      return Base64.getEncoder().encodeToString(keyBytes);
    }*/

    /** 生成UUID随机数,作为16进制密钥 */
    public static String generateKey() throws Exception {
      return CommonUtils.getUUID();
    }


    /** 生成 16 字节 IV */
    public static byte[] generateIV() {
      byte[] iv = new byte;
      new SecureRandom().nextBytes(iv);
      return iv;
    }

    /** 加密:返回 “Base64(IV) : Base64(密文)” 的拼接字符串 */
    public static String encrypt(String plainText, String base64Key){
      String encrypt = "";
      try {
            byte[] key = Hex.decodeHex(base64Key);
//      byte[] key = Base64.getDecoder().decode(base64Key);
            SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);

            byte[] iv= generateIV();
            IvParameterSpec ivSpec = new IvParameterSpec(iv);

            Cipher cipher = Cipher.getInstance(TRANSFORM, "BC");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            byte[] cipherBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

            // 把 IV 和密文一起发出去
            String ivBase64   = Base64.getEncoder().encodeToString(iv);
            String cipherBase64 = Base64.getEncoder().encodeToString(cipherBytes);
            encrypt = ivBase64 + ":" + cipherBase64;
      } catch (DecoderException e) {
            e.printStackTrace();
      } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
      } catch (NoSuchProviderException e) {
            e.printStackTrace();
      } catch (NoSuchPaddingException e) {
            e.printStackTrace();
      } catch (InvalidKeyException e) {
            e.printStackTrace();
      } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
      } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
      } catch (BadPaddingException e) {
            e.printStackTrace();
      }
      return encrypt;
    }

    /** 解密:传入 “Base64(IV) : Base64(密文)” 的拼接字符串 */
    public static String decrypt(String pack, String base64Key) {
      String decrypt = "";
      try {
            String[] parts = pack.split(":");
            if (parts.length != 2) throw new IllegalArgumentException("数据格式错误");
            byte[] key = Hex.decodeHex(base64Key);
//      byte[] key = Base64.getDecoder().decode(base64Key);
            SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);

            byte[] iv= Base64.getDecoder().decode(parts);
            byte[] cipherBytes = Base64.getDecoder().decode(parts);

            Cipher cipher = Cipher.getInstance(TRANSFORM, "BC");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
            byte[] plainBytes = cipher.doFinal(cipherBytes);
            decrypt = new String(plainBytes, StandardCharsets.UTF_8);
      } catch (DecoderException e) {
            e.printStackTrace();
      } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
      } catch (NoSuchProviderException e) {
            e.printStackTrace();
      } catch (NoSuchPaddingException e) {
            e.printStackTrace();
      } catch (InvalidKeyException e) {
            e.printStackTrace();
      } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
      } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
      } catch (BadPaddingException e) {
            e.printStackTrace();
      }
      return decrypt;
    }

    /* ------ 测试 ------ */
    public static void main(String[] args) throws Exception {
      String plain = "你好,国密 SM4-CBC!";
      String key   = generateKey();   // 复用上一篇的生成方法
      String pack= encrypt(plain, KEY);
      String ret   = decrypt(pack, KEY);

      System.out.println("密钥 :" + KEY);
      System.out.println("明文 :" + plain);
      System.out.println("包   :" + pack);
      System.out.println("解密 :" + ret);
    }
}</code></pre>
</details>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    无论风雨,和自己一决胜负吧<br><br>
来源:https://www.cnblogs.com/aerfazhe/p/19320090
頁: [1]
查看完整版本: JAVA 使用国密 SM4 加解密