前言
SM4
分组明码算法,是由国家明码局公布的国产商用明码算法。该算法的分组长度为128 bit,密钥长度为128 bit。具体算法形容能够查阅GB/T 32907-2016 《信息安全技术 SM4分组明码算法》
。
本文SM4
的java实现办法,在BC库(bouncycastle
)的根底上做了简略的封装,JS办法在sm-crypto
的根底上做的封装。
JAVA
加解密办法
<!-- 轻量级加密API --><dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.68</version></dependency>
import java.security.SecureRandom;import java.security.Security;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import org.bouncycastle.jce.provider.BouncyCastleProvider;/** * 国密SM4分组明码算法工具类(对称加密) * <p>GB/T 32907-2016 信息安全技术 SM4分组明码算法</p> * * @author BBF * @see <a href="http://www.gb688.cn/bzgk/gb/newGbInfo?hcno=7803DE42D3BC5E80B0C3E5D8E873D56A">GB/T * 32907-2016</a> */public class Sm4Util { private static final String ALGORITHM_NAME = "SM4"; private static final String ALGORITHM_ECB_PKCS5PADDING = "SM4/ECB/PKCS5Padding"; /** * SM4算法目前只反对128位(即密钥16字节) */ private static final int DEFAULT_KEY_SIZE = 128; static { // 避免内存中呈现屡次BouncyCastleProvider的实例 if (null == Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)) { Security.addProvider(new BouncyCastleProvider()); } } private Sm4Util() { } /** * 生成密钥 * <p>倡议应用org.bouncycastle.util.encoders.Hex将二进制转成HEX字符串</p> * * @return 密钥16位 * @throws Exception 生成密钥异样 */ public static byte[] generateKey() throws Exception { KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); kg.init(DEFAULT_KEY_SIZE, new SecureRandom()); return kg.generateKey().getEncoded(); } /** * 加密,SM4-ECB-PKCS5Padding * * @param data 要加密的明文 * @param key 密钥16字节,应用Sm4Util.generateKey()生成 * @return 加密后的密文 * @throws Exception 加密异样 */ public static byte[] encryptEcbPkcs5Padding(byte[] data, byte[] key) throws Exception { return sm4(data, key, ALGORITHM_ECB_PKCS5PADDING, null, Cipher.ENCRYPT_MODE); } /** * 解密,SM4-ECB-PKCS5Padding * * @param data 要解密的密文 * @param key 密钥16字节,应用Sm4Util.generateKey()生成 * @return 解密后的明文 * @throws Exception 解密异样 */ public static byte[] decryptEcbPkcs5Padding(byte[] data, byte[] key) throws Exception { return sm4(data, key, ALGORITHM_ECB_PKCS5PADDING, null, Cipher.DECRYPT_MODE); } /** * SM4对称加解密 * * @param input 明文(加密模式)或密文(解密模式) * @param key 密钥 * @param sm4mode sm4加密模式 * @param iv 初始向量(ECB模式下传NULL) * @param mode Cipher.ENCRYPT_MODE - 加密;Cipher.DECRYPT_MODE - 解密 * @return 密文(加密模式)或明文(解密模式) * @throws Exception 加解密异样 */ private static byte[] sm4(byte[] input, byte[] key, String sm4mode, byte[] iv, int mode) throws Exception { IvParameterSpec ivParameterSpec = null; if (null != iv) { ivParameterSpec = new IvParameterSpec(iv); } SecretKeySpec sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); Cipher cipher = Cipher.getInstance(sm4mode, BouncyCastleProvider.PROVIDER_NAME); if (null == ivParameterSpec) { cipher.init(mode, sm4Key); } else { cipher.init(mode, sm4Key, ivParameterSpec); } return cipher.doFinal(input); }}
测试用例
import java.nio.charset.StandardCharsets;import org.bouncycastle.util.encoders.Hex;import org.junit.Assert;import org.junit.Test;public class Sm4UtilTest { @Test public void ecbPkcs5Padding() throws Exception { String txt = "sm4对称加密<pkcs5>演示←←"; byte[] output = Sm4Util.encryptEcbPkcs5Padding(txt.getBytes(StandardCharsets.UTF_8), KEY); String hex = Hex.toHexString(output); LOGGER.info("SM4-ECB-PKCS5Padding,加密输入HEX = {}", hex); // 解密 byte[] input = Hex.decode(hex); output = Sm4Util.decryptEcbPkcs5Padding(input, KEY); String s = new String(output, StandardCharsets.UTF_8); LOGGER.info("SM4-ECB-PKCS5Padding,解密输入 = {}", s); LOGGER.info("SM4-ECB-PKCS5Padding,key = {}", Hex.toHexString(KEY)); Assert.assertEquals(txt, s); }}
JS
加解密办法
/** * SM4-ECB-PKCS5Padding对称加密 * @param text {string} 要加密的明文 * @param secretKey {string} 密钥,43位随机大小写与数字 * @returns {string} 加密后的密文,Base64格局 */ function SM4_ECB_ENCRYPT(text, secretKey) { return sm4.encrypt(text, secretKey).toUpperCase(); } /** * SM4-ECB-PKCS5Padding对称解密 * @param textBase64 {string} 要解密的密文,Base64格局 * @param secretKey {string} 密钥,43位随机大小写与数字 * @returns {string} 解密后的明文 */ function SM4_ECB_DECRYPT(textBase64, secretKey) { return sm4.decrypt(textBase64, secretKey); }
测试用例
var message = "sm4对称加密<pkcs5>演示←←"; var key = "FA171555405706F73D7B973DB89F0B47"; var ecbEncrypt = SM4_ECB_ENCRYPT(message, key); console.log("ecb加密", ecbEncrypt); var ecbDecrypt = SM4_ECB_DECRYPT(ecbEncrypt, key); console.log("ecb后果比拟---", message === ecbDecrypt)
附录
- GB/T 32907-2016 http://www.gb688.cn/bzgk/gb/newGbInfo?hcno=7803DE42D3BC5E80B0C3E5D8E873D56A
- sm-crypto https://github.com/JuneAndGreen/sm-crypto
- sm4.js https://github.com/JuneAndGreen/sm-crypto/blob/master/dist/sm4.js