在上一篇[Unity接入中宣部防沉迷实名认证之AES-128/GCM + BASE64加密(一)](https://www.jianshu.com/p/2f9d1e36ff49)中,使用的是Chilkat插件,但是由于购买太昂贵,所以决定弃用,有了这一篇文章。
因为最终要在Unity中使用,所以我这里新建一个可用于Android和iOS的`.NET Standard 2.0`库,然后安装BouncyCastle这个插件。
库中只有一个脚本`AesGcm.cs`
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.Encoders; using System; using System.Text; namespace AesGcmCrypt { public class AesGcm { private const string ALGORITHM_NAME = "AES"; private const int NONCE_LEN = 12; private const int ALGORITHM_KEY_SIZE = 16; private const int TAG_LEN = 16; private const int PBKDF2_ITERATIONS = 32767; public static string Encrypt(string plainText, string secretKey) { var cipherText = string.Empty; byte[] K = Hex.Decode(secretKey); byte[] P = Encoding.UTF8.GetBytes(plainText); byte[] N = new byte[NONCE_LEN]; Random random = new Random(); random.NextBytes(N); KeyParameter key = ParameterUtilities.CreateKeyParameter(ALGORITHM_NAME, K); IBufferedCipher inCipher = CipherUtilities.GetCipher("AES/GCM/NoPadding"); //加密 inCipher.Init(true, new ParametersWithIV(key, N)); byte[] enc = inCipher.DoFinal(P); byte[] data = new byte[N.Length + enc.Length]; Array.ConstrainedCopy(N, 0, data, 0, N.Length); Array.ConstrainedCopy(enc, 0, data, N.Length, enc.Length); cipherText = Convert.ToBase64String(data); return cipherText; } public static string Decrypt(string cipherText, string secretKey) { byte[] data = Convert.FromBase64String(cipherText); byte[] iv = new byte[NONCE_LEN]; //byte[] tag = new byte[TAG_LEN]; byte[] cipherData = new byte[data.Length /*- tag.Length*/ - iv.Length]; //Array.Copy(data, data.Length-tag.Length, tag, 0, tag.Length); Array.Copy(data, 0, iv, 0, iv.Length); Array.Copy(data, iv.Length, cipherData, 0, cipherData.Length); byte[] keyData = Hex.Decode(secretKey); KeyParameter key = ParameterUtilities.CreateKeyParameter(ALGORITHM_NAME, keyData); IBufferedCipher outCipher = CipherUtilities.GetCipher("AES/GCM/NoPadding"); outCipher.Init(false, new ParametersWithIV(key, iv)); byte[] dec = outCipher.DoFinal(cipherData); var plainText = Encoding.UTF8.GetString(dec); return plainText; } } }
using AesGcmCrypt; using System; namespace Test { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); string secretKey = "2836e95fcd10e04b0069bb1ee659955b"; string jsondata = "{"ai":"test-accountId","name":"用户姓名","idNum":"371321199012310912"}"; var data = "CqT/33f3jyoiYqT8MtxEFk3x2rlfhmgzhxpHqWosSj4d3hq2EbrtVyx2aLj565ZQNTcPrcDipnvpq/D/vQDaLKW70O83Q42zvR0//OfnYLcIjTPMnqa+SOhsjQrSdu66ySSORCAo"; var data2 = AesGcm.Decrypt(data, secretKey); Console.WriteLine("测试用例解密:" + data2); data = AesGcm.Encrypt(jsondata, secretKey); Console.WriteLine("自行加密后:" + data); //data = "CqT/33f3jyoiYqT8MtxEFk3x2rlfhmgzhxpHqWosSj4d3hq2EbrtVyx2aLj565ZQNTcPrcDipnvpq/D/vQDaLKW70O83Q42zvR0//OfnYLcIjTPMnqa+SOhsjQrSdu66ySSORCAo"; data2 = AesGcm.Decrypt(data, secretKey); Console.WriteLine("自行解密后:" + data2); Console.ReadLine(); } } }
输出结果:
- BouncyCastle免费开源库;Chilkat需要付费解锁API,而且价格很贵。
- Chilkat在加解密的时候,都能获取到TAG,格式是`IV + 真正密文 + TAG`;而BouncyCastle的格式是:`IV + 真正密文`