找回密码
 立即注册
首页 业界区 业界 JAVA 使用国密 SM4 加解密

JAVA 使用国密 SM4 加解密

骆熙华 1 小时前
SM4算法

百度百科
中华人民共和国政府采用的分组密码标准
SM4.0(原名SMS4.0)是中华人民共和国国家密码管理局于2012年3月21日发布的分组密码标准,对应行业标准为GM/T 0002-2012,采用128位分组长度和128位密钥结构,加密过程基于32轮非线性迭代,并配置固定8比特输入8比特输出的S盒,主要应用于商用数据加密领域
在密码学领域,国密算法 SM4 是我国自主研发的分组对称加密算法,凭借 128 位分组长度、128 位密钥长度的设计,在金融、政务、物联网等领域广泛应用。但分组密码本身仅能处理固定长度(SM4 为 128 位)的明文数据,而现实中需要加密的文件、数据流、存储块等往往是任意长度的。为解决这一问题,“工作模式” 应运而生 —— 它相当于分组密码的 “应用框架”,定义了如何将固定长度的分组加密逻辑扩展到任意长度数据,同时兼顾安全性、性能与实际场景需求。SM4 的 8 种主流工作模式:ECB、CBC、CFB、OFB、CTR、GCM、CCM 与 XTS

本文简单介绍SM4以下常用的加解密模式

  • ECB模式(电子密码本):工作原理:将明文分成固定长度的块(如SM4的16字节),每个块独立加密,互不影响。相同明文块会生成相同的密文块

    • 优点:ECB模块可以并行处理数据。
    • 缺点:同样原文生成同样的密文,并不能很好地保护数据。
    • 安全性:存在致命缺陷 —— 相同明文分组会生成相同密文分组。例如用 ECB 加密图片,若图片中有重复像素块(如背景),密文会呈现明显的 “块重复”,攻击者可通过统计分析还原明文结构,甚至篡改单个密文块(对应明文块也会被篡改,不影响其他块);
    • 适用场景:仅用于加密 “短且唯一” 的数据(如密钥封装、固定标识),绝对禁止用于文件、数据流等大数据加密。

  • CBC模式(密码分组链接模式):工作原理:通过 “链式关联” 解决 ECB 的关联性问题:

    • 明文分组按 128 位拆分,最后一块补全;
    • 第一个明文分组与 “初始化向量(IV)” 进行异或运算,再用 SM4 加密得到第一个密文分组;
    • 后续每个明文分组先与前一个密文分组异或,再加密,形成 “前密文影响后明文” 的链式结构。

  • CBC模式特点和应用

    • 安全性:相同明文分组因 “前密文异或” 的干扰,会生成不同密文,抵御统计分析攻击;但密文块被篡改后,会影响后续所有明文块的解密(链式传播);
    • 性能:加密无法并行(需等待前一个密文块),解密可并行(已知密文块即可独立解密,再与前一个密文块异或);
    • IV要求:需随机生成且唯一(无需保密),若 IV 重复,相同明文会生成相同密文;
    • 适用场景:文件加密、早期 SSL/TLS 协议(如 TLS 1.0)、IPsec VPN,适合对并行性要求不高的场景。

  • CFB模式:将分组密码转化为流密码
其余工作模式可参考博客:https://blog.csdn.net/openHiTLS/article/details/151288800
以下使用Bouncy Castle 开源加密库进行加解密
  1. <dependency>
  2.     <groupId>org.bouncycastle</groupId>
  3.     bcprov-jdk15on</artifactId>
  4.     <version>1.70</version>
  5. </dependency>
  6. implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
复制代码
ECB模式

SM4Utils
  1.  package com.isoftstone.cascade02.utils;
  2. import org.apache.commons.codec.DecoderException;
  3. import org.apache.commons.codec.binary.Hex;
  4. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  5. import javax.crypto.*;
  6. import javax.crypto.spec.IvParameterSpec;
  7. import javax.crypto.spec.SecretKeySpec;
  8. import java.nio.charset.StandardCharsets;
  9. import java.security.*;
  10. import java.util.Base64;
  11. /**
  12. * @Author:wk
  13. * @Slogan:无论风雨,和自己一决胜负吧
  14. * @Create:2025/12/5/18:16
  15. * @Description:SM4算法 ECB 模式
  16. * @Version:1.0
  17. */
  18. public class SM4Utils {
  19.     public final static String KEY = "4c73aae48f4d5edba3a365837904dbc8";
  20.     private static final String ALGORITHM = "SM4";
  21.     private static final String TRANSFORM  = "SM4/ECB/PKCS5Padding";
  22.     static {
  23.         Security.addProvider(new BouncyCastleProvider());
  24.     }
  25.     /** 生成 128-bit(16 字节)SM4 密钥,返回 Base64 字符串 */
  26.     /*public static String generateKey() throws Exception {
  27.         KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, "BC");
  28.         kg.init(128, new SecureRandom());
  29.         byte[] keyBytes = kg.generateKey().getEncoded();
  30.         return Base64.getEncoder().encodeToString(keyBytes);
  31.     }*/
  32.     /** 生成UUID随机数,作为16进制密钥 */
  33.     public static String generateKey() throws Exception {
  34.         return CommonUtils.getUUID();
  35. //        byte[] keyBytes = kg.generateKey().getEncoded();
  36. //        return Base64.getEncoder().encodeToString(keyBytes);
  37.     }
  38.     /** 加密:明文 + Base64 密钥 → Base64 密文 */
  39.     public static String encrypt(String plainText, String base64Key) {
  40.         String encrypt = "";
  41.         try {
  42.             byte[] key = Hex.decodeHex(base64Key);
  43. //        byte[] key = Base64.getDecoder().decode(base64Key);
  44.             SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
  45.             Cipher cipher = Cipher.getInstance(TRANSFORM, "BC");
  46.             cipher.init(Cipher.ENCRYPT_MODE, keySpec);
  47.             byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
  48.             encrypt = Base64.getEncoder().encodeToString(encrypted);
  49.         } catch (DecoderException e) {
  50.             e.printStackTrace();
  51.         } catch (NoSuchAlgorithmException e) {
  52.             e.printStackTrace();
  53.         } catch (NoSuchProviderException e) {
  54.             e.printStackTrace();
  55.         } catch (NoSuchPaddingException e) {
  56.             e.printStackTrace();
  57.         } catch (InvalidKeyException e) {
  58.             e.printStackTrace();
  59.         } catch (IllegalBlockSizeException e) {
  60.             e.printStackTrace();
  61.         } catch (BadPaddingException e) {
  62.             e.printStackTrace();
  63.         }
  64.         return encrypt;
  65.     }
  66.     /** 解密:Base64 密文 + Base64 密钥 → 明文 */
  67.     public static String decrypt(String base64Cipher, String base64Key) {
  68.         String decrypt = "";
  69.         try {
  70.             byte[] key = Hex.decodeHex(base64Key);
  71. //        byte[] key = Base64.getDecoder().decode(base64Key);
  72.             SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
  73.             Cipher cipher = Cipher.getInstance(TRANSFORM, "BC");
  74.             cipher.init(Cipher.DECRYPT_MODE, keySpec);
  75.             byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(base64Cipher));
  76.             decrypt = new String(decrypted, StandardCharsets.UTF_8);
  77.         } catch (DecoderException e) {
  78.             e.printStackTrace();
  79.         } catch (NoSuchAlgorithmException e) {
  80.             e.printStackTrace();
  81.         } catch (NoSuchProviderException e) {
  82.             e.printStackTrace();
  83.         } catch (NoSuchPaddingException e) {
  84.             e.printStackTrace();
  85.         } catch (InvalidKeyException e) {
  86.             e.printStackTrace();
  87.         } catch (IllegalBlockSizeException e) {
  88.             e.printStackTrace();
  89.         } catch (BadPaddingException e) {
  90.             e.printStackTrace();
  91.         }
  92.         return decrypt;
  93.     }
  94.     /* ------ 简单测试 ------ */
  95.     public static void main(String[] args) throws Exception {
  96.         String plain = "你好,国密 SM4!";
  97.         String key  = generateKey();
  98.         String cipher = encrypt(plain, key);
  99.         String result = decrypt(cipher, key);
  100.         System.out.println("密钥 :" + key);
  101.         System.out.println("密钥 :" + KEY);
  102.         System.out.println("明文 :" + plain);
  103.         System.out.println("密文 :" + cipher);
  104.         System.out.println("解密 :" + result);
  105.     }
  106. }
复制代码
CBC模式

SM4IVUtils
  1. package com.isoftstone.cascade02.utils;
  2. import org.apache.commons.codec.DecoderException;
  3. import org.apache.commons.codec.binary.Hex;
  4. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  5. import javax.crypto.*;
  6. import javax.crypto.spec.IvParameterSpec;
  7. import javax.crypto.spec.SecretKeySpec;
  8. import java.nio.charset.StandardCharsets;
  9. import java.security.*;
  10. import java.util.Base64;
  11. /**
  12. * @Author:wk
  13. * @Slogan:无论风雨,和自己一决胜负吧
  14. * @Create:2025/12/5/18:16
  15. * @Description:SM4 IV 算法
  16. * @Version:1.0
  17. */
  18. public class SM4IVUtils {
  19.     public final static String KEY = "4c73aae48f4d5edba3a365837904dbc8";
  20.     private static final String ALGORITHM = "SM4";
  21.     private static final String TRANSFORM  = "SM4/CBC/PKCS5Padding";
  22.     static {
  23.         Security.addProvider(new BouncyCastleProvider());
  24.     }
  25.     /** 生成 128-bit(16 字节)SM4 密钥,返回 Base64 字符串 */
  26.     /*public static String generateKey() throws Exception {
  27.         KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, "BC");
  28.         kg.init(128, new SecureRandom());
  29.         byte[] keyBytes = kg.generateKey().getEncoded();
  30.         return Base64.getEncoder().encodeToString(keyBytes);
  31.     }*/
  32.     /** 生成UUID随机数,作为16进制密钥 */
  33.     public static String generateKey() throws Exception {
  34.         return CommonUtils.getUUID();
  35.     }
  36.     /** 生成 16 字节 IV */
  37.     public static byte[] generateIV() {
  38.         byte[] iv = new byte[16];
  39.         new SecureRandom().nextBytes(iv);
  40.         return iv;
  41.     }
  42.     /** 加密:返回 “Base64(IV) : Base64(密文)” 的拼接字符串 */
  43.     public static String encrypt(String plainText, String base64Key){
  44.         String encrypt = "";
  45.         try {
  46.             byte[] key = Hex.decodeHex(base64Key);
  47. //        byte[] key = Base64.getDecoder().decode(base64Key);
  48.             SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
  49.             byte[] iv  = generateIV();
  50.             IvParameterSpec ivSpec = new IvParameterSpec(iv);
  51.             Cipher cipher = Cipher.getInstance(TRANSFORM, "BC");
  52.             cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
  53.             byte[] cipherBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
  54.             // 把 IV 和密文一起发出去
  55.             String ivBase64     = Base64.getEncoder().encodeToString(iv);
  56.             String cipherBase64 = Base64.getEncoder().encodeToString(cipherBytes);
  57.             encrypt = ivBase64 + ":" + cipherBase64;
  58.         } catch (DecoderException e) {
  59.             e.printStackTrace();
  60.         } catch (NoSuchAlgorithmException e) {
  61.             e.printStackTrace();
  62.         } catch (NoSuchProviderException e) {
  63.             e.printStackTrace();
  64.         } catch (NoSuchPaddingException e) {
  65.             e.printStackTrace();
  66.         } catch (InvalidKeyException e) {
  67.             e.printStackTrace();
  68.         } catch (InvalidAlgorithmParameterException e) {
  69.             e.printStackTrace();
  70.         } catch (IllegalBlockSizeException e) {
  71.             e.printStackTrace();
  72.         } catch (BadPaddingException e) {
  73.             e.printStackTrace();
  74.         }
  75.         return encrypt;
  76.     }
  77.     /** 解密:传入 “Base64(IV) : Base64(密文)” 的拼接字符串 */
  78.     public static String decrypt(String pack, String base64Key) {
  79.         String decrypt = "";
  80.         try {
  81.             String[] parts = pack.split(":");
  82.             if (parts.length != 2) throw new IllegalArgumentException("数据格式错误");
  83.             byte[] key = Hex.decodeHex(base64Key);
  84. //        byte[] key = Base64.getDecoder().decode(base64Key);
  85.             SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
  86.             byte[] iv  = Base64.getDecoder().decode(parts[0]);
  87.             byte[] cipherBytes = Base64.getDecoder().decode(parts[1]);
  88.             Cipher cipher = Cipher.getInstance(TRANSFORM, "BC");
  89.             cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
  90.             byte[] plainBytes = cipher.doFinal(cipherBytes);
  91.             decrypt = new String(plainBytes, StandardCharsets.UTF_8);
  92.         } catch (DecoderException e) {
  93.             e.printStackTrace();
  94.         } catch (NoSuchAlgorithmException e) {
  95.             e.printStackTrace();
  96.         } catch (NoSuchProviderException e) {
  97.             e.printStackTrace();
  98.         } catch (NoSuchPaddingException e) {
  99.             e.printStackTrace();
  100.         } catch (InvalidKeyException e) {
  101.             e.printStackTrace();
  102.         } catch (InvalidAlgorithmParameterException e) {
  103.             e.printStackTrace();
  104.         } catch (IllegalBlockSizeException e) {
  105.             e.printStackTrace();
  106.         } catch (BadPaddingException e) {
  107.             e.printStackTrace();
  108.         }
  109.         return decrypt;
  110.     }
  111.     /* ------ 测试 ------ */
  112.     public static void main(String[] args) throws Exception {
  113.         String plain = "你好,国密 SM4-CBC!";
  114.         String key   = generateKey();   // 复用上一篇的生成方法
  115.         String pack  = encrypt(plain, KEY);
  116.         String ret   = decrypt(pack, KEY);
  117.         System.out.println("密钥 :" + KEY);
  118.         System.out.println("明文 :" + plain);
  119.         System.out.println("包   :" + pack);
  120.         System.out.println("解密 :" + ret);
  121.     }
  122. }
复制代码
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册