using System; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.Encoders; namespace SecretUtils.Crypto { /** * sm4加解密工具类 *

因为数据加解密都是对字节数据加解密,因此需要注意加密前和解密后使用的字符集保持一致 *

若无特殊说明,接口接收的都是原始的二进制数据,被hex或者base64编码的数据,务必解码之后再传入接口 * @author liangruxing * */ internal class SM4Util { private const int SM4_ENCRYPT = 1; private const int SM4_DECRYPT = 0; public const int SM4_PKCS8PADDING = 1; public const int SM4_NOPADDING = 0; public const int SM4_KEY_128 = 128; private const String iv = "DB4433CBE745731BBFA534109636F5FD"; ///

/// 使用国密SM4对文本加密字符串 /// /// /// /// public static string SM4EncryptData(string key, byte[] dataBytes) { //byte[] dataBytes = Encoding.UTF8.GetBytes(data); byte[] cipher = SM4Util.EncryptCBC(dataBytes, Hex.Decode(key), Hex.Decode(key)); return Convert.ToBase64String(cipher); } public static string SM4DecryptData(string key,string data) { byte[] cipher = Convert.FromBase64String(data); byte[] plain = SM4Util.DecryptCBC(cipher, Hex.Decode(key), Hex.Decode(key)); return Hex.ToHexString(cipher, 0, cipher.Length); } /** * 生成sm4密钥,长度使用 * @param keySize 密钥位数(通过SM4Util的常量获取长度值) * @return sm4密钥 */ public static byte[] GenerateKey(int keySize) { byte[] key = new byte[keySize / 8]; SecureRandom sr = new SecureRandom(); sr.NextBytes(key); return key; } /** * sm4 ecb模式加密数据,数据长度非16倍数,则使用默认PKCS8PADDING方式填充 * @param data 待加密的数据 * @param key sm4密钥 * @return 密文数据 */ public static byte[] EncryptECB(byte[] data, byte[] key) { return EncryptECB(data, key, SM4_PKCS8PADDING); } /** * sm4 ecb模式解密数据,使用默认PKCS8PADDING方式去除填充 * @param cipher 密文数据 * @param key sm4密钥 * @return 明文字节数据 */ public static byte[] DecryptECB(byte[] cipher, byte[] key) { return DecryptECB(cipher, key, SM4_PKCS8PADDING); } /** * sm4 CBC模式加密数据,数据长度非16倍数,则使用默认PKCS8PADDING方式填充 * @param data 待加密数据 * @param key sm4密钥 * @param iv 向量 * @return 密文数据 */ public static byte[] EncryptCBC(byte[] data, byte[] key, byte[] iv) { return EncryptCBC(data, key, iv, SM4_PKCS8PADDING); } /** * sm4 cbc模式解密数据,使用默认PKCS8PADDING方式去除填充 * @param cipher sm4密文数据 * @param key sm4密钥 * @param iv 向量 * @return 明文字节数据 */ public static byte[] DecryptCBC(byte[] cipher, byte[] key, byte[] iv) { return DecryptCBC(cipher, key, iv, SM4_PKCS8PADDING); } /** * sm4 ecb模式加密数据 * @param data 待加密数据 * @param key sm4密钥 * @param paddingMode 填充模式,具体支持请看类的常量字段,若使用不支持的模式则会默认无填充 * @return 返回密文数据 */ public static byte[] EncryptECB(byte[] data, byte[] key, int paddingMode) { IBlockCipher engine = new SM4Engine(); engine.Init(true, new KeyParameter(key)); if (paddingMode == SM4_PKCS8PADDING) { data = padding(data, SM4_ENCRYPT); } else { data = (byte [])data.Clone(); } int length = data.Length; for (int i = 0; length > 0; length -= 16, i += 16) { engine.ProcessBlock(data, i, data, i); } return data; } /** * sm4 ecb模式解密数据 * @param cipher 密文数据 * @param key sm4密钥 * @param paddingMode 填充模式,具体支持请看类的常量字段,若使用不支持的模式则会默认无填充 * @return 返回明文字节数据 */ public static byte[] DecryptECB(byte[] cipher, byte[] key, int paddingMode) { IBlockCipher engine = new SM4Engine(); engine.Init(false, new KeyParameter(key)); int length = cipher.Length; byte[] tmp = new byte[cipher.Length]; for (int i = 0; length > 0; length -= 16, i += 16) { engine.ProcessBlock(cipher, i, tmp, i); } byte[] plain = null; if (paddingMode == SM4_PKCS8PADDING) { plain = padding(tmp, SM4_DECRYPT); } else { plain = tmp; } return plain; } /** * CBC模式加密数据 * @param data 待加密数据 * @param key 密钥 * @param iv 向量 * @param paddingMode 填充模式,具体支持请看类的常量字段,若使用不支持的模式则会默认无填充 * @return 返回密文值 */ public static byte[] EncryptCBC(byte[] data, byte[] key, byte[] iv, int paddingMode) { IBlockCipher engine = new SM4Engine(); engine.Init(true, new KeyParameter(key)); if (paddingMode == SM4_PKCS8PADDING) { data = padding(data, SM4_ENCRYPT); } else { data = (byte [])data.Clone(); } int length = data.Length; iv = (byte [])iv.Clone(); for (int i = 0; length > 0; length -= 16, i += 16) { for (int j = 0; j < 16; j++) { data[i + j] = ((byte)(data[i + j] ^ iv[j])); } engine.ProcessBlock(data, i, data, i); Buffer.BlockCopy(data, i, iv, 0, 16); } return data; } /** * CBC模式解密数据 * @param cipher 密文数据 * @param key 密钥 * @param iv 向量 * @param isPadding 填充模式,具体支持请看类的常量字段,若使用不支持的模式则会默认无填充 * @return 返回明文字节数据 */ public static byte[] DecryptCBC(byte[] cipher, byte[] key, byte[] iv, int paddingMode) { IBlockCipher engine = new SM4Engine(); engine.Init(false, new KeyParameter(key)); int length = cipher.Length; byte[] plain = new byte[cipher.Length]; iv = (byte [])iv.Clone(); for (int i = 0; length > 0; length -= 16, i += 16) { engine.ProcessBlock(cipher, i, plain, i); for (int j = 0; j < 16; j++) { plain[j + i] = ((byte)(plain[i + j] ^ iv[j])); } Buffer.BlockCopy(cipher, i, iv, 0, 16); } byte[] res = null; if (paddingMode == SM4_PKCS8PADDING) { res = padding(plain, SM4_DECRYPT); } else { res = plain; } return res; } /** * PKCS8PADDING标准填充 * @param input 输入数据 * @param mode 填充或去除填充 * @return */ private static byte[] padding(byte[] input, int mode) { if (input == null) { return null; } byte[] ret = (byte[])null; if (mode == SM4_ENCRYPT) { int p = 16 - input.Length % 16; ret = new byte[input.Length + p]; Buffer.BlockCopy(input, 0, ret, 0, input.Length); for (int i = 0; i < p; i++) { ret[input.Length + i] = (byte)p; } } else { int p = input[input.Length - 1]; ret = new byte[input.Length - p]; Buffer.BlockCopy(input, 0, ret, 0, input.Length - p); } return ret; } } }