diff --git a/DynamicKey/ARDynamicKey/README.md b/DynamicKey/ARDynamicKey/README.md index 1c58883..4d84e37 100644 --- a/DynamicKey/ARDynamicKey/README.md +++ b/DynamicKey/ARDynamicKey/README.md @@ -24,6 +24,7 @@ Sample Code for generating AccessToken are available on the following platforms: + Node.js + Python + PHP + + C# ### **YOUR IMPLEMENTATIONS ARE VERY WELCOME.** @@ -205,3 +206,46 @@ if __name__ == "__main__": ``` + +### C# +```c# +using AnyRtcTools; +using System; + +namespace Sample +{ + class Program + { + static void Main(string[] args) + { + var appID = "970CA35de60c44645bbae8a215061b33"; + var appCertificate = "5CFd2fd1755d40ecb72977518be15d3b"; + var channelName = "7d72365eb983485397e3e3f9d460bdda"; + var userAccount = "2882341273"; + + var expireTimeInSeconds = 3600; + var currentTimestamp = DateTime.UtcNow.Subtract( + new DateTime(1970, 1, 1)).TotalSeconds; + var privilegeExpiredTs = (int)currentTimestamp + + expireTimeInSeconds; + + var token = new AccessToken(appID, + appCertificate, channelName, userAccount); + token.AddPrivilege(AccessToken.kJoinChannel, + privilegeExpiredTs); + //token.AddPrivilege(AccessToken.kPublishVideoStream, + // privilegeExpiredTs); + //token.AddPrivilege(AccessToken.kPublishAudioStream, + // privilegeExpiredTs); + //token.AddPrivilege(AccessToken.kPublishDataStream, + // privilegeExpiredTs); + + Console.WriteLine(token.Build()); + + Console.ReadLine(); + } + } +} + +``` + diff --git a/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.Tests/AccessTokenTest.cs b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.Tests/AccessTokenTest.cs new file mode 100644 index 0000000..ba98a02 --- /dev/null +++ b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.Tests/AccessTokenTest.cs @@ -0,0 +1,43 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AnyRtcTools.Tests +{ + [TestClass] + public class AccessTokenTest + { + private string appID = "970CA35de60c44645bbae8a215061b33"; + private string appCertificate = "5CFd2fd1755d40ecb72977518be15d3b"; + private string channelName = "7d72365eb983485397e3e3f9d460bdda"; + private string uid = "2882341273"; + private int expireTimestamp = 1446455471; + private int salt = 1; + private int ts = 1111111; + + [TestMethod] + public void TestBuild() + { + var expected = "006970CA35de60c44645bbae8a215061b33IACV0fZUBw+72cVoL9eyGGh3Q6Poi8bgjwVLnyKSJyOXR7dIfRBXoFHlEAABAAAAR/QQAAEAAQCvKDdW"; + + var key = new AccessToken(appID, appCertificate, channelName, uid); + key._salt = salt; + key._ts = ts; + key.AddPrivilege(AccessToken.kJoinChannel, expireTimestamp); + + var result = key.Build(); + + Assert.AreEqual(expected, result); + + // test uid = 0 + expected = "006970CA35de60c44645bbae8a215061b33IACw1o7htY6ISdNRtku3p9tjTPi0jCKf9t49UHJhzCmL6bdIfRAAAAAAEAABAAAAR/QQAAEAAQCvKDdW"; + + var uid_zero = ""; + key = new AccessToken(appID, appCertificate, channelName, uid_zero); + key._salt = salt; + key._ts = ts; + key.AddPrivilege(AccessToken.kJoinChannel, expireTimestamp); + + result = key.Build(); + Assert.AreEqual(expected, result); + } + } +} diff --git a/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.Tests/AnyRtcTools.Tests.csproj b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.Tests/AnyRtcTools.Tests.csproj new file mode 100644 index 0000000..29045a1 --- /dev/null +++ b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.Tests/AnyRtcTools.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + diff --git a/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.sln b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.sln new file mode 100644 index 0000000..10eaad4 --- /dev/null +++ b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools.sln @@ -0,0 +1,93 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1340 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnyRtcTools", "AnyRtcTools\AnyRtcTools.csproj", "{EB61A8BA-9739-44F0-A9AD-8A44E052922F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "Sample\Sample.csproj", "{D79D4486-176D-4B73-9305-8F350A75C844}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyRtcTools.Tests", "AnyRtcTools.Tests\AnyRtcTools.Tests.csproj", "{931463CF-3D94-46F1-AE27-AB28369B1376}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|ARM.Build.0 = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|ARM64.Build.0 = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|x64.ActiveCfg = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|x64.Build.0 = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|x86.ActiveCfg = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Debug|x86.Build.0 = Debug|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|Any CPU.Build.0 = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|ARM.ActiveCfg = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|ARM.Build.0 = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|ARM64.ActiveCfg = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|ARM64.Build.0 = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|x64.ActiveCfg = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|x64.Build.0 = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|x86.ActiveCfg = Release|Any CPU + {EB61A8BA-9739-44F0-A9AD-8A44E052922F}.Release|x86.Build.0 = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|ARM.Build.0 = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|ARM64.Build.0 = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|x64.ActiveCfg = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|x64.Build.0 = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|x86.ActiveCfg = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Debug|x86.Build.0 = Debug|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|Any CPU.Build.0 = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|ARM.ActiveCfg = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|ARM.Build.0 = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|ARM64.ActiveCfg = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|ARM64.Build.0 = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|x64.ActiveCfg = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|x64.Build.0 = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|x86.ActiveCfg = Release|Any CPU + {D79D4486-176D-4B73-9305-8F350A75C844}.Release|x86.Build.0 = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|Any CPU.Build.0 = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|ARM.ActiveCfg = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|ARM.Build.0 = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|ARM64.Build.0 = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|x64.ActiveCfg = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|x64.Build.0 = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|x86.ActiveCfg = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Debug|x86.Build.0 = Debug|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|Any CPU.ActiveCfg = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|Any CPU.Build.0 = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|ARM.ActiveCfg = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|ARM.Build.0 = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|ARM64.ActiveCfg = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|ARM64.Build.0 = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|x64.ActiveCfg = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|x64.Build.0 = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|x86.ActiveCfg = Release|Any CPU + {931463CF-3D94-46F1-AE27-AB28369B1376}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2C01FC33-D2A5-4D79-923B-874A5EE99420} + EndGlobalSection +EndGlobal diff --git a/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/AccessToken.cs b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/AccessToken.cs new file mode 100644 index 0000000..82d64cb --- /dev/null +++ b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/AccessToken.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace AnyRtcTools +{ + public class AccessToken + { + public const ushort kJoinChannel = 1; + public const ushort kPublishAudioStream = 2; + public const ushort kPublishVideoStream = 3; + public const ushort kPublishDataStream = 4; + public const ushort kPublishAudiocdn = 5; + public const ushort kPublishVideoCdn = 6; + public const ushort kRequestPublishAudioStream = 7; + public const ushort kRequestPublishVideoStream = 8; + public const ushort kRequestPublishDataStream = 9; + public const ushort kInvitePublishAudioStream = 10; + public const ushort kInvitePublishVideoStream = 11; + public const ushort kInvitePublishDataStream = 12; + public const ushort kAdministrateChannel = 101; + public const ushort kRtmLogin = 1000; + + public const int VERSION_LENGTH = 3; + public const int APP_ID_LENGTH = 32; + + private readonly string _appID; + private readonly string _appCertificate; + private readonly string _channelName; + private readonly string _uid; + public int _ts; + public int _salt; + private SortedDictionary _messages; + + public AccessToken(string appID, string appCertificate, string channelName, string uid) + { + _appID = appID; + _appCertificate = appCertificate; + _channelName = channelName; + _uid = uid; + + _ts = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds) + 24 * 3600; + var random = new Random(); + _salt = random.Next(1, 99999999); + } + + public void AddPrivilege(ushort privilege, int expireTimestamp) + { + if (_messages == null) + { + _messages = new SortedDictionary(); + } + + _messages.Add(privilege, expireTimestamp); + } + + public string Build() + { + if (_messages == null) + { + throw new Exception("Please specify some privileges first."); + } + + var m = BitConverter.GetBytes(_salt) + .Concat(BitConverter.GetBytes(_ts)) + .Concat(PackMapUint32(_messages)); + + var val = Encoding.UTF8.GetBytes(_appID) + .Concat(Encoding.UTF8.GetBytes(_channelName)) + .Concat(Encoding.UTF8.GetBytes(_uid)) + .Concat(m).ToArray(); + + HMACSHA256 hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(_appCertificate)); + var signature = hmacSha256.ComputeHash(val); + + var crc32 = new CRC32Cls(); + var crc_channel_name = crc32.GetCRC32Str(_channelName) & 0xffffffff; + var crc_uid = crc32.GetCRC32Str(_uid) & 0xffffffff; + + var content = PackString(signature) + .Concat(BitConverter.GetBytes(crc_channel_name)) + .Concat(BitConverter.GetBytes(crc_uid)) + .Concat(PackString(m.ToArray())).ToArray(); + + return $"006{_appID}{Convert.ToBase64String(content)}"; + } + + private static byte[] PackString(byte[] strBytes) + { + return BitConverter.GetBytes((ushort)(strBytes.Length)) + .Concat(strBytes).ToArray(); + } + + private static byte[] PackMapUint32(SortedDictionary ms) + { + var m = BitConverter.GetBytes((ushort)(ms.Count)); + IEnumerable ret = m.ToList(); + foreach (var kvp in ms) + { + ret = ret.Concat(BitConverter.GetBytes(kvp.Key)) + .Concat(BitConverter.GetBytes(kvp.Value)); + } + return ret.ToArray(); + } + } +} diff --git a/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/AnyRtcTools.csproj b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/AnyRtcTools.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/AnyRtcTools.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/CRC32Cls.cs b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/CRC32Cls.cs new file mode 100644 index 0000000..1a3a17a --- /dev/null +++ b/DynamicKey/ARDynamicKey/csharp/AnyRtcTools/CRC32Cls.cs @@ -0,0 +1,49 @@ +using System.Text; + +namespace AnyRtcTools +{ + class CRC32Cls + { + /* + * + * author: uusystem + * blog url: https://www.cnblogs.com/tianjifa/p/9216985.html + * + */ + protected uint[] Crc32Table; + //生成CRC32码表 + public void GetCRC32Table() + { + uint Crc; + Crc32Table = new uint[256]; + int i, j; + for (i = 0; i < 256; i++) + { + Crc = (uint)i; + for (j = 8; j > 0; j--) + { + if ((Crc & 1) == 1) + Crc = (Crc >> 1) ^ 0xEDB88320; + else + Crc >>= 1; + } + Crc32Table[i] = Crc; + } + } + + //获取字符串的CRC32校验值 + public uint GetCRC32Str(string sInputString) + { + //生成码表 + GetCRC32Table(); + byte[] buffer = Encoding.UTF8.GetBytes(sInputString); + uint value = 0xffffffff; + int len = buffer.Length; + for (int i = 0; i < len; i++) + { + value = (value >> 8) ^ Crc32Table[(value & 0xFF) ^ buffer[i]]; + } + return value ^ 0xffffffff; + } + } +} diff --git a/DynamicKey/ARDynamicKey/csharp/Sample/Program.cs b/DynamicKey/ARDynamicKey/csharp/Sample/Program.cs new file mode 100644 index 0000000..38f1463 --- /dev/null +++ b/DynamicKey/ARDynamicKey/csharp/Sample/Program.cs @@ -0,0 +1,30 @@ +using AnyRtcTools; +using System; + +namespace Sample +{ + class Program + { + static void Main(string[] args) + { + var appID = "970CA35de60c44645bbae8a215061b33"; + var appCertificate = "5CFd2fd1755d40ecb72977518be15d3b"; + var channelName = "7d72365eb983485397e3e3f9d460bdda"; + var userAccount = "2882341273"; + + var expireTimeInSeconds = 3600; + var currentTimestamp = DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + var privilegeExpiredTs = (int)currentTimestamp + expireTimeInSeconds; + + var token = new AccessToken(appID, appCertificate, channelName, userAccount); + token.AddPrivilege(AccessToken.kJoinChannel, privilegeExpiredTs); + //token.AddPrivilege(AccessToken.kPublishVideoStream, privilegeExpiredTs); + //token.AddPrivilege(AccessToken.kPublishAudioStream, privilegeExpiredTs); + //token.AddPrivilege(AccessToken.kPublishDataStream, privilegeExpiredTs); + + Console.WriteLine(token.Build()); + + Console.ReadLine(); + } + } +} diff --git a/DynamicKey/ARDynamicKey/csharp/Sample/Sample.csproj b/DynamicKey/ARDynamicKey/csharp/Sample/Sample.csproj new file mode 100644 index 0000000..0ff0d2b --- /dev/null +++ b/DynamicKey/ARDynamicKey/csharp/Sample/Sample.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp2.2 + + + + + + +