Server/csharp/AppToken/Model/AppToken.cs (94 lines of code) (raw):

using System.Text; using AppToken.Util; namespace AppToken.Model { public class AppToken { private readonly string appId; private readonly string appKey; private int issueTimestamp; private int salt; private int timestamp; private Service service; private AppTokenOptions options; private byte[] signature; private const string VERSION_0 = "000"; private const int VERSION_LENGTH = 3; public AppToken(string appId, string appKey, int timestamp) { this.appId = appId ?? throw new ArgumentNullException("missing appId"); this.appKey = appKey ?? throw new ArgumentNullException("missing appKey"); this.timestamp = timestamp; this.issueTimestamp = (int)(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000); this.salt = new Random().Next(); } private AppToken(string appId, int issueTimestamp, int salt, int timestamp, Service service, AppTokenOptions options, byte[] signature) { this.appId = appId; this.timestamp = timestamp; this.issueTimestamp = issueTimestamp; this.salt = salt; this.service = service; this.options = options; this.signature = signature; } public void SetService(Service service) { this.service = service ?? throw new ArgumentNullException("missing service"); } public void SetOptions(AppTokenOptions options) { this.options = options; } public string Build() { using var buf = new MemoryStream(); using (var writer = new BinaryWriter(buf, Encoding.UTF8, true)) { var appIdBytes = Encoding.UTF8.GetBytes(this.appId); writer.Write(BigEndianUtils.GetBytesBigEndian(appIdBytes.Length)); writer.Write(appIdBytes); writer.Write(BigEndianUtils.GetBytesBigEndian(this.issueTimestamp)); writer.Write(BigEndianUtils.GetBytesBigEndian(this.salt)); writer.Write(BigEndianUtils.GetBytesBigEndian(this.timestamp)); this.service.Pack(buf); this.options = this.options ?? new AppTokenOptions(); this.options.Pack(buf); } using var tokenBuf = new MemoryStream(); using (var writer = new BinaryWriter(tokenBuf, Encoding.UTF8, true)) { byte[] signKey = SignatureUtils.GenerateSign(this.appKey, this.issueTimestamp, this.salt); byte[] tokenBody = BytesUtils.GetFixedLengthByteArray(buf); byte[] signature = SignatureUtils.Sign(signKey, tokenBody); writer.Write(BigEndianUtils.GetBytesBigEndian(signature.Length)); writer.Write(signature); writer.Write(tokenBody); } return VERSION_0 + EncodeUtils.Base64Encode(CompressUtils.Compress(BytesUtils.GetFixedLengthByteArray(tokenBuf))); } public static AppToken Parse(string token) { token = token ?? throw new ArgumentNullException("empty appToken"); if (!token.StartsWith(VERSION_0)) { throw new ArgumentNullException("unsupported version"); } byte[] data = CompressUtils.Decompress(EncodeUtils.Base64Decode(token.Substring(VERSION_LENGTH))); using var buf = new MemoryStream(data); using (var reader = new BinaryReader(buf, Encoding.UTF8, true)) { int signLength = BigEndianUtils.FromBytesBigEndianToInt(reader.ReadBytes(4)); byte[] byteSignature = reader.ReadBytes(signLength); int appIdLength = BigEndianUtils.FromBytesBigEndianToInt(reader.ReadBytes(4)); string appId = Encoding.UTF8.GetString(reader.ReadBytes(appIdLength)); int issueTimestamp = BigEndianUtils.FromBytesBigEndianToInt(reader.ReadBytes(4)); int salt = BigEndianUtils.FromBytesBigEndianToInt(reader.ReadBytes(4)); int timestamp = BigEndianUtils.FromBytesBigEndianToInt(reader.ReadBytes(4)); Service service = Service.Unpack(buf); AppTokenOptions options = AppTokenOptions.Unpack(buf); return new AppToken(appId, issueTimestamp, salt, timestamp, service, options, byteSignature); } } } }