记一次三方接口开发的数据加密方案
前段时间工作中,开发某银行系统接口,接口报文数据安全性要求比较高,安全方案中用到了数字证书加密,解密,签名,验签,国标SM4加密,解密。整个的开发下来,还是踩了不少坑,值得一提的是对方用Java解析报文,我方用C#,对于算法的沟通处理又增加了一些困难,尤其度娘查到的资料实在有限,基本还是通过谷哥补全了自己需要的技术知识和一些源码。不是专业做加密学的或做算法的,没必要陷入技术细节,但是基本原理一定要明白,当然,重点还包括一份可靠的源代码 ↓↓↓↓↓↓
一、SSL数字证书
数字证书在很多网站可以在线申请,也有免费的,但是建议自己安装openssl,通过命令行的方式生成证书,这样可以自己控制所有参数。
在这里数字证书只是用来加密通讯数据,并不是用做网站证书,所以也没必要花钱申请CA证书。
开始我在某网站申请crt格式的免费自签名证书,明明选择的SHA256哈希签名算法,结果做签名的时候用SHA256CryptoServiceProvider,就出现异常提示“指定的算法无效”,只能用SHA1CryptoServiceProvider。但是对方的Java程序验签制定要用SHA256withRSA算法。
具体可以参考这篇提问https://stackoverflow.com/questions/7433074/why-am-i-getting-invalid-algorithm-specified-exception
首先安装openssl,然后用命令行生成pem私钥和证书,及pfx格式私钥。
1 -- 创建私钥和证书: 2 openssl req -x509 -nodes -sha512 -newkey rsa:2048 -keyout 512key.pem -out 512cert.pem -days 3650 3 4 -- 使用Microsoft增强RSA和AES加密提供程序创建PFX文件: 5 openssl pkcs12 -export -in 512cert.pem -inkey 512key.pem -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -out 512pfx.pfx
创建过程中按照提示输入各类信息即可,如证书的国家城市算法等,又如pfx私钥的密码等。
最终我们得到了512key.pem,512cert.pem,512pfx.pfx三个文件。
二、证书加密与解密,签名与验签
先上代码,证书加密:
1 /// <summary> 2 /// 公钥加密 3 /// </summary> 4 /// <param name="plainText"></param> 5 /// <returns></returns> 6 public static String EncryptPublic(String plainText) 7 { 8 X509Certificate2 _X509Certificate2 = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "\\xxx.cer"); 9 using (RSACryptoServiceProvider RSACryptography = _X509Certificate2.PublicKey.Key as RSACryptoServiceProvider) 10 { 11 Byte[] plainTextData = Encoding.UTF8.GetBytes(plainText); 12 int maxBlockSize = RSACryptography.KeySize / 8 - 11;//加密块最大长度限制 13 if (plainTextData.Length <= maxBlockSize) 14 { 15 //银行的接口要用下面的格式 16 //var ret = Convert.ToBase64String(RSACryptography.Encrypt(plainTextData, false)); 17 var ret = Hex.ToHexString(RSACryptography.Encrypt(plainTextData, false)); 18 return ret; 19 } 20 using (MemoryStream plainTextStream = new MemoryStream(plainTextData)) 21 using (MemoryStream cryptTextStream = new MemoryStream()) 22 { 23 Byte[] buffer = new Byte[maxBlockSize]; 24 int BlockSize = plainTextStream.Read(buffer, 0, maxBlockSize); 25 while (BlockSize > 0) 26 { 27 Byte[] toEncrypt = new Byte[BlockSize]; 28 Array.Copy(buffer, 0, toEncrypt, 0, BlockSize); 29 Byte[] Cryptograph = RSACryptography.Encrypt(toEncrypt, false); 30 cryptTextStream.Write(Cryptograph, 0, Cryptograph.Length); 31 BlockSize = plainTextStream.Read(buffer, 0, maxBlockSize); 32 } 33 //银行的接口要用下面的格式 34 //var ret = Convert.ToBase64String(cryptTextStream.ToArray(), Base64FormattingOptions.None); 35 var ret = Hex.ToHexString(cryptTextStream.ToArray()); 36 return ret; 37 } 38 } 39 }
12行,加密块最大长度限制为key length in bits / 8 - 11,如果更长的明文需要加密,那么就只能截断,一段一段加密。比如这里是2048/8-11=245,也就是说明文转化为字节数组后长度不得超过245,才可以直接加密,否则需要分段加密。
16行或34行,将加密后的byte数组序列转回字符串,有两种方式,由于对方采用了Hex,我这边也需要改为Hex,对方才能顺利解密数据,所以现实应用还需根据实际情况处理。
不知道Hex是什么?请自行Nuget搜索BouncyCastle。
证书解密:
1 /// <summary> 2 /// 私钥解密 3 /// </summary> 4 /// <param name="cipherText"></param> 5 /// <returns></returns> 6 public static String DecryptPrivate(String cipherText) 7 { 8 X509Certificate2 _X509Certificate2 = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "\\xxx.pfx", "123456"); 9 using (RSACryptoServiceProvider RSACryptography = _X509Certificate2.PrivateKey as RSACryptoServiceProvider) 10 { 11 Byte[] cipherTextData = Hex.Decode(cipherText); 12 int maxBlockSize = RSACryptography.KeySize / 8; //解密块最大长度限制 13 if (cipherTextData.Length <= maxBlockSize) 14 return Encoding.UTF8.GetString(RSACryptography.Decrypt(cipherTextData, false)); 15 using (MemoryStream cryptTextStream = new MemoryStream(cipherTextData)) 16 using (MemoryStream plainTextStream = new MemoryStream()) 17 { 18 Byte[] buffer = new Byte[maxBlockSize]; 19 int blockSize = cryptTextStream.Read(buffer, 0, maxBlockSize); 20 while (blockSize > 0) 21 { 22 Byte[] toDecrypt = new Byte[blockSize]; 23 Array.Copy(buffer, 0, toDecrypt, 0, blockSize); 24 Byte[] plaintext = RSACryptography.Decrypt(toDecrypt, false); 25 plainTextStream.Write(plaintext, 0, plaintext.Length); 26 blockSize = cryptTextStream.Read(buffer, 0, maxBlockSize); 27 } 28 return Encoding.UTF8.GetString(plainTextStream.ToArray()); 29 } 30 } 31 }
签名与验签,注意字符串编码格式和算法选择即可:
1 /// <summary> 2 /// 私钥签名 3 /// </summary> 4 /// <param name="textToSign"></param> 5 /// <returns></returns> 6 public static string HashAndSign(string textToSign) 7 { 8 X509Certificate2 _X509Certificate2 = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "\\512.pfx", "123456"); 9 using (RSACryptoServiceProvider RSACryptography = _X509Certificate2.PrivateKey as RSACryptoServiceProvider) 10 { 11 ASCIIEncoding byteConverter = new ASCIIEncoding(); 12 byte[] DataToSign = byteConverter.GetBytes(textToSign); 13 byte[] signedData = RSACryptography.SignData(DataToSign, new SHA256CryptoServiceProvider()); 14 //string textSign = Convert.ToBase64String(signedData); 15 string textSign = Hex.ToHexString(signedData); 16 return textSign; 17 } 18 } 19 /// <summary> 20 /// 公钥验签 21 /// </summary> 22 /// <param name="textToVerify">待验证文本</param> 23 /// <param name="signatureText">base64签名</param> 24 /// <returns></returns> 25 public static bool VerifySignedHash(string textToVerify, string signatureText) 26 { 27 X509Certificate2 _X509Certificate2 = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "\\512.pem"); 28 using (RSACryptoServiceProvider RSACryptography = _X509Certificate2.PublicKey.Key as RSACryptoServiceProvider) 29 { 30 //byte[] signature = Convert.FromBase64String(signatureText); 31 byte[] signature = Hex.Decode(signatureText); 32 ASCIIEncoding ByteConverter = new ASCIIEncoding(); 33 byte[] dataToVerify = ByteConverter.GetBytes(textToVerify); 34 return RSACryptography.VerifyData(dataToVerify, new SHA256CryptoServiceProvider(), signature); 35 } 36 }
三、SM4加密与解密
SM4加解密算法的C#版本可用源码真的难找:
1 public class Sm4 2 { 3 public const int SM4_ENCRYPT = 1; 4 public const int SM4_DECRYPT = 0; 5 6 private long GET_ULONG_BE(byte[] b, int i) 7 { 8 long n = (long)(b[i] & 0xff) << 24 | (long)((b[i + 1] & 0xff) << 16) | (long)((b[i + 2] & 0xff) << 8) | (long)(b[i + 3] & 0xff) & 0xffffffffL; 9 return n; 10 } 11 private void PUT_ULONG_BE(long n, byte[] b, int i) 12 { 13 b[i] = (byte)(int)(0xFF & n >> 24); 14 b[i + 1] = (byte)(int)(0xFF & n >> 16); 15 b[i + 2] = (byte)(int)(0xFF & n >> 8); 16 b[i + 3] = (byte)(int)(0xFF & n); 17 } 18 private long SHL(long x, int n) 19 { 20 return (x & 0xFFFFFFFF) << n; 21 } 22 private long ROTL(long x, int n) 23 { 24 return SHL(x, n) | x >> (32 - n); 25 } 26 private void SWAP(long[] sk, int i) 27 { 28 long t = sk[i]; 29 sk[i] = sk[(31 - i)]; 30 sk[(31 - i)] = t; 31 } 32 public byte[] SboxTable = new byte[] { (byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe, 33 (byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6, 34 0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67, 35 (byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3, 36 (byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06, 37 (byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91, 38 (byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 39 (byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4, 40 (byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8, 41 (byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa, 42 0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7, 43 (byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83, 44 0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8, 45 0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda, 46 (byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56, 47 (byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1, 48 (byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87, 49 (byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27, 50 0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4, 51 (byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a, 52 (byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3, 53 (byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15, 54 (byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4, 55 (byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32, 56 0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d, 57 (byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca, 58 0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f, 59 (byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd, 60 (byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 61 0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb, 62 (byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41, 63 0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31, 64 (byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d, 65 0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4, 66 (byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c, 67 (byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09, 68 (byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0, 69 0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79, 70 (byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48 }; 71 public uint[] FK = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc }; 72 public uint[] CK = { 0x00070e15,0x1c232a31,0x383f464d,0x545b6269, 73 0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9, 74 0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249, 75 0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9, 76 0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229, 77 0x30373e45,0x4c535a61,0x686f767d,0x848b9299, 78 0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209, 79 0x10171e25,0x2c333a41,0x484f565d,0x646b7279 }; 80 private byte sm4Sbox(byte inch) 81 { 82 int i = inch & 0xFF; 83 byte retVal = SboxTable[i]; 84 return retVal; 85 } 86 private long sm4Lt(long ka) 87 { 88 long bb = 0L; 89 long c = 0L; 90 byte[] a = new byte[4]; 91 byte[] b = new byte[4]; 92 PUT_ULONG_BE(ka, a, 0); 93 b[0] = sm4Sbox(a[0]); 94 b[1] = sm4Sbox(a[1]); 95 b[2] = sm4Sbox(a[2]); 96 b[3] = sm4Sbox(a[3]); 97 bb = GET_ULONG_BE(b, 0); 98 c = bb ^ ROTL(bb, 2) ^ ROTL(bb, 10) ^ ROTL(bb, 18) ^ ROTL(bb, 24); 99 return c; 100 } 101 private long sm4F(long x0, long x1, long x2, long x3, long rk) 102 { 103 return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk); 104 } 105 106 private long sm4CalciRK(long ka) 107 { 108 long bb = 0L; 109 long rk = 0L; 110 byte[] a = new byte[4]; 111 byte[] b = new byte[4]; 112 PUT_ULONG_BE(ka, a, 0); 113 b[0] = sm4Sbox(a[0]); 114 b[1] = sm4Sbox(a[1]); 115 b[2] = sm4Sbox(a[2]); 116 b[3] = sm4Sbox(a[3]); 117 bb = GET_ULONG_BE(b, 0); 118 rk = bb ^ ROTL(bb, 13) ^ ROTL(bb, 23); 119 return rk; 120 } 121 122 private void sm4_setkey(long[] SK, byte[] key) 123 { 124 long[] MK = new long[4]; 125 long[] k = new long[36]; 126 int i = 0; 127 MK[0] = GET_ULONG_BE(key, 0); 128 MK[1] = GET_ULONG_BE(key, 4); 129 MK[2] = GET_ULONG_BE(key, 8); 130 MK[3] = GET_ULONG_BE(key, 12); 131 k[0] = MK[0] ^ (long)FK[0]; 132 k[1] = MK[1] ^ (long)FK[1]; 133 k[2] = MK[2] ^ (long)FK[2]; 134 k[3] = MK[3] ^ (long)FK[3]; 135 for (; i < 32; i++) 136 { 137 k[(i + 4)] = (k[i] ^ sm4CalciRK(k[(i + 1)] ^ k[(i + 2)] ^ k[(i + 3)] ^ (long)CK[i])); 138 SK[i] = k[(i + 4)]; 139 } 140 } 141 142 private void sm4_one_round(long[] sk, byte[] input, byte[] output) 143 { 144 int i = 0; 145 long[] ulbuf = new long[36]; 146 ulbuf[0] = GET_ULONG_BE(input, 0); 147 ulbuf[1] = GET_ULONG_BE(input, 4); 148 ulbuf[2] = GET_ULONG_BE(input, 8); 149 ulbuf[3] = GET_ULONG_BE(input, 12); 150 while (i < 32) 151 { 152 ulbuf[(i + 4)] = sm4F(ulbuf[i], ulbuf[(i + 1)], ulbuf[(i + 2)], ulbuf[(i + 3)], sk[i]); 153 i++; 154 } 155 PUT_ULONG_BE(ulbuf[35], output, 0); 156 PUT_ULONG_BE(ulbuf[34], output, 4); 157 PUT_ULONG_BE(ulbuf[33], output, 8); 158 PUT_ULONG_BE(ulbuf[32], output, 12); 159 } 160 161 private byte[] padding(byte[] input, int mode) 162 { 163 if (input == null) 164 { 165 return null; 166 } 167 168 byte[] ret = (byte[])null; 169 if (mode == SM4_ENCRYPT) 170 { 171 int p = 16 - input.Length % 16; 172 ret = new byte[input.Length + p]; 173 Array.Copy(input, 0, ret, 0, input.Length); 174 for (int i = 0; i < p; i++) 175 { 176 ret[input.Length + i] = (byte)p; 177 } 178 } 179 else 180 { 181 int p = input[input.Length - 1]; 182 ret = new byte[input.Length - p]; 183 Array.Copy(input, 0, ret, 0, input.Length - p); 184 } 185 return ret; 186 } 187 188 public void sm4_setkey_enc(SM4Context ctx, byte[] key) 189 { 190 ctx.mode = SM4_ENCRYPT; 191 sm4_setkey(ctx.sk, key); 192 } 193 194 public void sm4_setkey_dec(SM4Context ctx, byte[] key) 195 { 196 int i = 0; 197 ctx.mode = SM4_DECRYPT; 198 sm4_setkey(ctx.sk, key); 199 for (i = 0; i < 16; i++) 200 { 201 SWAP(ctx.sk, i); 202 } 203 } 204 205 public byte[] sm4_crypt_ecb(SM4Context ctx, byte[] input) 206 { 207 if ((ctx.isPadding) && (ctx.mode == SM4_ENCRYPT)) 208 { 209 input = padding(input, SM4_ENCRYPT); 210 } 211 212 int length = input.Length; 213 byte[] bins = new byte[length]; 214 Array.Copy(input, 0, bins, 0, length); 215 byte[] bous = new byte[length]; 216 for (int i = 0; length > 0; length -= 16, i++) 217 { 218 byte[] inBytes = new byte[16]; 219 byte[] outBytes = new byte[16]; 220 Array.Copy(bins, i * 16, inBytes, 0, length > 16 ? 16 : length); 221 sm4_one_round(ctx.sk, inBytes, outBytes); 222 Array.Copy(outBytes, 0, bous, i * 16, length > 16 ? 16 : length); 223 } 224 225 if (ctx.isPadding && ctx.mode == SM4_DECRYPT) 226 { 227 bous = padding(bous, SM4_DECRYPT); 228 } 229 return bous; 230 } 231 232 public byte[] sm4_crypt_cbc(SM4Context ctx, byte[] iv, byte[] input) 233 { 234 if (ctx.isPadding && ctx.mode == SM4_ENCRYPT) 235 { 236 input = padding(input, SM4_ENCRYPT); 237 } 238 239 int i = 0; 240 int length = input.Length; 241 byte[] bins = new byte[length]; 242 Array.Copy(input, 0, bins, 0, length); 243 byte[] bous = null; 244 List<byte> bousList = new List<byte>(); 245 if (ctx.mode == SM4_ENCRYPT) 246 { 247 for (int j = 0; length > 0; length -= 16, j++) 248 { 249 byte[] inBytes = new byte[16]; 250 byte[] outBytes = new byte[16]; 251 byte[] out1 = new byte[16]; 252 Array.Copy(bins, i * 16, inBytes, 0, length > 16 ? 16 : length); 253 for (i = 0; i < 16; i++) 254 { 255 outBytes[i] = ((byte)(inBytes[i] ^ iv[i])); 256 } 257 sm4_one_round(ctx.sk, outBytes, out1); 258 Array.Copy(out1, 0, iv, 0, 16); 259 for (int k = 0; k < 16; k++) 260 { 261 bousList.Add(out1[k]); 262 } 263 } 264 } 265 else 266 { 267 byte[] temp = new byte[16]; 268 for (int j = 0; length > 0; length -= 16, j++) 269 { 270 byte[] inBytes = new byte[16]; 271 byte[] outBytes = new byte[16]; 272 byte[] out1 = new byte[16]; 273 274 275 Array.Copy(bins, i * 16, inBytes, 0, length > 16 ? 16 : length); 276 Array.Copy(inBytes, 0, temp, 0, 16); 277 sm4_one_round(ctx.sk, inBytes, outBytes); 278 for (i = 0; i < 16; i++) 279 { 280 out1[i] = ((byte)(outBytes[i] ^ iv[i])); 281 } 282 Array.Copy(temp, 0, iv, 0, 16); 283 for (int k = 0; k < 16; k++) 284 { 285 bousList.Add(out1[k]); 286 } 287 } 288 } 289 if (ctx.isPadding && ctx.mode == SM4_DECRYPT) 290 { 291 bous = padding(bousList.ToArray(), SM4_DECRYPT); 292 return bous; 293 } 294 else 295 { 296 return bousList.ToArray(); 297 } 298 } 299 }
1 public class SM4Context 2 { 3 public int mode; 4 public long[] sk; 5 public bool isPadding; 6 public SM4Context() 7 { 8 this.mode = 1; 9 this.isPadding = true; 10 this.sk = new long[32]; 11 } 12 }
1 class SM4Util 2 { 3 /// <summary> 4 /// 加密ECB模式 5 /// </summary> 6 /// <param name="secretKey">密钥</param> 7 /// <param name="hexString">明文是否是十六进制</param> 8 /// <param name="plainText">明文</param> 9 /// <returns>返回密文</returns> 10 public static String Encrypt_ECB(String secretKey, bool hexString, String plainText) 11 { 12 SM4Context ctx = new SM4Context(); 13 ctx.isPadding = true; 14 ctx.mode = Sm4.SM4_ENCRYPT; 15 byte[] keyBytes; 16 if (hexString) 17 { 18 keyBytes = Hex.Decode(secretKey); 19 } 20 else 21 { 22 keyBytes = Encoding.UTF8.GetBytes(secretKey); 23 } 24 Sm4 sm4 = new Sm4(); 25 sm4.sm4_setkey_enc(ctx, keyBytes); 26 byte[] encrypted = sm4.sm4_crypt_ecb(ctx, Encoding.UTF8.GetBytes(plainText)); 27 String cipherText = Encoding.UTF8.GetString(Hex.Encode(encrypted)); 28 return cipherText; 29 } 30 /// <summary> 31 /// 解密ECB模式 32 /// </summary> 33 /// <param name="secretKey">密钥</param> 34 /// <param name="hexString">明文是否是十六进制</param> 35 /// <param name="cipherText">密文</param> 36 /// <returns>返回明文</returns> 37 public static String Decrypt_ECB(String secretKey, bool hexString, String cipherText) 38 { 39 SM4Context ctx = new SM4Context(); 40 ctx.isPadding = true; 41 ctx.mode = Sm4.SM4_DECRYPT; 42 43 byte[] keyBytes; 44 if (hexString) 45 { 46 keyBytes = Hex.Decode(secretKey); 47 } 48 else 49 { 50 keyBytes = Encoding.UTF8.GetBytes(secretKey); 51 } 52 53 Sm4 sm4 = new Sm4(); 54 sm4.sm4_setkey_dec(ctx, keyBytes); 55 byte[] decrypted = sm4.sm4_crypt_ecb(ctx, Hex.Decode(cipherText)); 56 return Encoding.UTF8.GetString(decrypted); 57 } 58 /// <summary> 59 /// 加密CBC模式 60 /// </summary> 61 /// <param name="secretKey">密钥</param> 62 /// <param name="hexString">明文是否是十六进制</param> 63 /// <param name="iv"></param> 64 /// <param name="plainText">明文</param> 65 /// <returns>返回密文</returns> 66 public static String Encrypt_CBC(String secretKey, bool hexString, string iv, String plainText) 67 { 68 SM4Context ctx = new SM4Context(); 69 ctx.isPadding = true; 70 ctx.mode = Sm4.SM4_ENCRYPT; 71 byte[] keyBytes; 72 byte[] ivBytes; 73 if (hexString) 74 { 75 keyBytes = Hex.Decode(secretKey); 76 ivBytes = Hex.Decode(iv); 77 } 78 else 79 { 80 keyBytes = Encoding.UTF8.GetBytes(secretKey); 81 ivBytes = Encoding.UTF8.GetBytes(iv); 82 } 83 Sm4 sm4 = new Sm4(); 84 sm4.sm4_setkey_enc(ctx, keyBytes); 85 byte[] encrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, Encoding.UTF8.GetBytes(plainText)); 86 String cipherText = Encoding.UTF8.GetString(Hex.Encode(encrypted)); 87 return cipherText; 88 } 89 /// <summary> 90 /// 解密CBC模式 91 /// </summary> 92 /// <param name="secretKey">密钥</param> 93 /// <param name="hexString">明文是否是十六进制</param> 94 /// <param name="iv"></param> 95 /// <param name="cipherText">密文</param> 96 /// <returns>返回明文</returns> 97 public static String Decrypt_CBC(String secretKey, bool hexString, string iv, String cipherText) 98 { 99 SM4Context ctx = new SM4Context(); 100 ctx.isPadding = true; 101 ctx.mode = Sm4.SM4_DECRYPT; 102 byte[] keyBytes; 103 byte[] ivBytes; 104 if (hexString) 105 { 106 keyBytes = Hex.Decode(secretKey); 107 ivBytes = Hex.Decode(iv); 108 } 109 else 110 { 111 keyBytes = Encoding.UTF8.GetBytes(secretKey); 112 ivBytes = Encoding.UTF8.GetBytes(iv); 113 } 114 Sm4 sm4 = new Sm4(); 115 sm4.sm4_setkey_dec(ctx, keyBytes); 116 byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, Hex.Decode(cipherText)); 117 return Encoding.UTF8.GetString(decrypted); 118 } 119 }
四、接口整体加密方案
有了以上加密工具方法,数据安全传输的整体方案如何来实现的?
具体方案实现见下图:
最终得到的绿色框中字段和值就是要发送到接收方的数据了。
接收方处理并返回数据加密安全原理类似,不再赘述 ^_^