SM4Util.java 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package com.ruoyi.common.utils;
  2. import com.ruoyi.common.config.Sm4Config;
  3. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Component;
  6. import javax.crypto.Cipher;
  7. import javax.crypto.spec.SecretKeySpec;
  8. import java.nio.charset.StandardCharsets;
  9. import java.security.Security;
  10. import java.util.Base64;
  11. /**
  12. * SM4加密算法工具类
  13. * 使用固定密钥,直接从配置文件读取
  14. * 兼容Java 8版本
  15. */
  16. @Component
  17. public class SM4Util {
  18. private final byte[] key;
  19. static {
  20. // 添加BouncyCastleProvider
  21. Security.addProvider(new BouncyCastleProvider());
  22. }
  23. // 算法名称
  24. private static final String ALGORITHM_NAME = "SM4";
  25. private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
  26. @Autowired
  27. public SM4Util(Sm4Config sm4Config) {
  28. // 从配置文件中读取密钥并转换为字节数组
  29. this.key = hexStringToBytes(sm4Config.getSecretKey());
  30. if (this.key.length != 16) {
  31. throw new IllegalArgumentException("SM4密钥必须是16字节(32位十六进制字符串)");
  32. }
  33. }
  34. /**
  35. * 将十六进制字符串转换为字节数组(兼容Java 8)
  36. */
  37. private byte[] hexStringToBytes(String hexString) {
  38. if (hexString == null || hexString.length() % 2 != 0) {
  39. throw new IllegalArgumentException("无效的十六进制字符串");
  40. }
  41. int len = hexString.length();
  42. byte[] data = new byte[len / 2];
  43. for (int i = 0; i < len; i += 2) {
  44. data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
  45. + Character.digit(hexString.charAt(i + 1), 16));
  46. }
  47. return data;
  48. }
  49. /**
  50. * 将字节数组转换为十六进制字符串(兼容Java 8)
  51. */
  52. private String bytesToHexString(byte[] bytes) {
  53. StringBuilder hexString = new StringBuilder();
  54. for (byte b : bytes) {
  55. String hex = Integer.toHexString(0xff & b);
  56. if (hex.length() == 1) {
  57. hexString.append('0');
  58. }
  59. hexString.append(hex);
  60. }
  61. return hexString.toString().toUpperCase();
  62. }
  63. /**
  64. * 加密字符串
  65. *
  66. * @param data 待加密的字符串
  67. * @return Base64编码的加密结果
  68. */
  69. public String encrypt(String data) {
  70. if (StringUtils.isEmpty(data)) return null;
  71. try {
  72. Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
  73. SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM_NAME);
  74. cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
  75. byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
  76. return Base64.getEncoder().encodeToString(encrypted);
  77. } catch (Exception e) {
  78. throw new RuntimeException("SM4加密失败", e);
  79. }
  80. }
  81. /**
  82. * 解密字符串
  83. *
  84. * @param encryptedData Base64编码的加密数据
  85. * @return 解密后的原始字符串
  86. */
  87. public String decrypt(String encryptedData) {
  88. if (StringUtils.isEmpty(encryptedData)) return null;
  89. try {
  90. Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
  91. SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM_NAME);
  92. cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
  93. byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
  94. byte[] decrypted = cipher.doFinal(encryptedBytes);
  95. return new String(decrypted, StandardCharsets.UTF_8);
  96. } catch (Exception e) {
  97. throw new RuntimeException("SM4解密失败", e);
  98. }
  99. }
  100. /**
  101. * 加密字节数组
  102. *
  103. * @param data 待加密的字节数组
  104. * @return 加密后的字节数组
  105. */
  106. public byte[] encrypt(byte[] data) {
  107. try {
  108. Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
  109. SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM_NAME);
  110. cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
  111. return cipher.doFinal(data);
  112. } catch (Exception e) {
  113. throw new RuntimeException("SM4加密失败", e);
  114. }
  115. }
  116. /**
  117. * 解密字节数组
  118. *
  119. * @param encryptedData 加密的字节数组
  120. * @return 解密后的原始字节数组
  121. */
  122. public byte[] decrypt(byte[] encryptedData) {
  123. try {
  124. Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
  125. SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM_NAME);
  126. cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
  127. return cipher.doFinal(encryptedData);
  128. } catch (Exception e) {
  129. throw new RuntimeException("SM4解密失败", e);
  130. }
  131. }
  132. /**
  133. * 获取当前使用的密钥(十六进制格式)
  134. */
  135. public String getKeyHex() {
  136. return bytesToHexString(key);
  137. }
  138. /**
  139. * 验证加密解密功能是否正常
  140. */
  141. public boolean selfCheck() {
  142. try {
  143. String testData = "SM4测试数据123";
  144. String encrypted = encrypt(testData);
  145. String decrypted = decrypt(encrypted);
  146. return testData.equals(decrypted);
  147. } catch (Exception e) {
  148. return false;
  149. }
  150. }
  151. }