public override void HandleMcpeServerToClientHandshake(McpeServerToClientHandshake message) { string token = message.token; IDictionary <string, dynamic> headers = JWT.Headers(token); string x5u = headers["x5u"]; ECPublicKeyParameters remotePublicKey = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(x5u.DecodeBase64()); var signParam = new ECParameters { Curve = ECCurve.NamedCurves.nistP384, Q = { X = remotePublicKey.Q.AffineXCoord.GetEncoded(), Y = remotePublicKey.Q.AffineYCoord.GetEncoded() }, }; signParam.Validate(); var signKey = ECDsa.Create(signParam); try { var data = JWT.Decode <HandshakeData>(token, signKey); BaseClient.InitiateEncryption(Base64Url.Decode(x5u), Base64Url.Decode(data.salt)); } catch (Exception e) { Log.Error(token, e); throw; } }
public static string Decrypt(string jweString) { var headers = JWT.Headers(jweString); var keyId = GetKeyID(headers); var sharedSecret = GetSharedSecret(keyId); return(JWT.Decode(jweString, GetKeyBytes(sharedSecret))); }
public static byte[] EncodeJwt(string username, CngKey newKey) { byte[] t = ImportECDsaCngKeyFromCngKey(newKey.Export(CngKeyBlobFormat.EccPrivateBlob)); CngKey tk = CngKey.Import(t, CngKeyBlobFormat.EccPrivateBlob); ECDiffieHellmanCng ecKey = new ECDiffieHellmanCng(newKey); ecKey.HashAlgorithm = CngAlgorithm.Sha256; ecKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; string b64Key = Convert.ToBase64String(ecKey.PublicKey.GetDerEncoded()); long exp = DateTimeOffset.UtcNow.AddDays(1).ToUnixTimeMilliseconds(); CertificateData certificateData = new CertificateData { Exp = exp, Iat = exp, ExtraData = new ExtraData { DisplayName = username, //Identity = "af6f7c5e-fcea-3e43-bf3a-e005e400e579", Identity = Guid.NewGuid().ToString(), }, Iss = "self", IdentityPublicKey = b64Key, CertificateAuthority = true, Nbf = exp, RandomNonce = new Random().Next(), }; // string txt = $@"{{ // ""exp"": 1467508449, // ""extraData"": {{ // ""displayName"": ""gurunxx"", // ""identity"": ""4e0199c6-7cfd-3550-b676-74398e0a5f1a"" // }}, // ""identityPublicKey"": ""{b64Key}"", // ""nbf"": 1467508448 //}}"; string val = JWT.Encode(certificateData, tk, JwsAlgorithm.ES384, new Dictionary <string, object> { { "x5u", b64Key } }); Log.Warn(JWT.Payload(val)); Log.Warn(string.Join(";", JWT.Headers(val))); //val = "eyJhbGciOiJFUzM4NCIsIng1dSI6Ik1IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFREVLck5xdk93Y25iV3I5aUtVQ0MyeklFRmZ6Q0VnUEhQdG5Kd3VEdnZ3VjVtd1E3QzNkWmhqd0g0amxWc2RDVTlNdVl2QllQRktCTEJkWU52K09ZeW1MTFJGTU9odVFuSDhuZFRRQVV6VjJXRTF4dHdlVG1wSVFzdXdmVzRIdzAifQo.eyJleHAiOjE0Njc1MDg0NDksImV4dHJhRGF0YSI6eyJkaXNwbGF5TmFtZSI6Imd1cnVueHgiLCJpZGVudGl0eSI6IjRlMDE5OWM2LTdjZmQtMzU1MC1iNjc2LTc0Mzk4ZTBhNWYxYSJ9LCJpZGVudGl0eVB1YmxpY0tleSI6Ik1IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFREVLck5xdk93Y25iV3I5aUtVQ0MyeklFRmZ6Q0VnUEhQdG5Kd3VEdnZ3VjVtd1E3QzNkWmhqd0g0amxWc2RDVTlNdVl2QllQRktCTEJkWU52K09ZeW1MTFJGTU9odVFuSDhuZFRRQVV6VjJXRTF4dHdlVG1wSVFzdXdmVzRIdzAiLCJuYmYiOjE0Njc1MDg0NDh9Cg.jpCqzTo8nNVEW8ArK1NFBaqLx6kyJV6wPF8cAU6UGav6cfMc60o3m5DjwspN-JcyC14AlcNiPdWX8TEm1QFhtScb-bXo4WOJ0dNYXV8iI_eCTCcXMFjX4vgIHpb9xfjv"; val = $@"{{ ""chain"": [""{val}""] }}"; return(Encoding.UTF8.GetBytes(val)); }
public void ReadLoginData(byte[] payload) { string json = Encoding.UTF8.GetString(payload); JObject data = JObject.Parse(json); JToken chainToken = data["chain"]; foreach (var token in chainToken) { IDictionary <string, object> headers = JWT.Headers(token.ToString()); string x5u = headers["x5u"].ToString(); ECPublicKeyParameters x5KeyParam = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(x5u)); ECParameters signParam = new ECParameters { Curve = ECCurve.NamedCurves.nistP384, Q = { X = x5KeyParam.Q.AffineXCoord.GetEncoded(), Y = x5KeyParam.Q.AffineYCoord.GetEncoded() }, }; signParam.Validate(); try { JObject jwt = JObject.Parse(JWT.Decode(token.ToString(), ECDsa.Create(signParam))); if (jwt.ContainsKey("extraData")) { JObject extData = JObject.Parse(jwt["extraData"].ToString()); LoginData.ClientUUID = new Guid(extData["identity"].Value <string>()); LoginData.DisplayName = extData["displayName"].Value <string>(); LoginData.IdentityPublicKey = jwt["identityPublicKey"].ToString(); LoginData.Xuid = extData["XUID"].Value <string>(); } } catch (IntegrityException) { TokenValidated = false; JObject jwt = JObject.Parse(JWT.Payload(token.ToString())); if (jwt.ContainsKey("extraData")) { JObject extData = JObject.Parse(jwt["extraData"].ToString()); LoginData.ClientUUID = new Guid(extData["identity"].Value <string>()); LoginData.DisplayName = extData["displayName"].Value <string>(); LoginData.IdentityPublicKey = jwt["identityPublicKey"].ToString(); LoginData.Xuid = extData["XUID"].Value <string>(); } } } }
public static byte[] EncodeJwt(string username, AsymmetricCipherKeyPair newKey, bool isEmulator) { long iat = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); long exp = DateTimeOffset.UtcNow.AddDays(1).ToUnixTimeSeconds(); ECDsa signKey = ConvertToSingKeyFormat(newKey); string b64Key = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(newKey.Public).GetEncoded().EncodeBase64(); var certificateData = new CertificateData { Exp = exp, Iat = iat, ExtraData = new ExtraData { Xuid = "", DisplayName = username, Identity = isEmulator ? Guid.NewGuid().ToString() : "85e4febd-3d33-4008-b044-1ad9fb85b26c", TitleId = "89692877" }, Iss = "self", IdentityPublicKey = b64Key, CertificateAuthority = true, Nbf = iat, RandomNonce = new Random().Next(), }; // string txt = $@"{{ // ""exp"": 1467508449, // ""extraData"": {{ // ""displayName"": ""gurunxx"", // ""identity"": ""4e0199c6-7cfd-3550-b676-74398e0a5f1a"" // }}, // ""identityPublicKey"": ""{b64Key}"", // ""nbf"": 1467508448 //}}"; string val = JWT.Encode(certificateData, signKey, JwsAlgorithm.ES384, new Dictionary <string, object> { { "x5u", b64Key } }); Log.Debug(JWT.Payload(val)); Log.Debug(string.Join(";", JWT.Headers(val))); //val = "eyJhbGciOiJFUzM4NCIsIng1dSI6Ik1IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFREVLck5xdk93Y25iV3I5aUtVQ0MyeklFRmZ6Q0VnUEhQdG5Kd3VEdnZ3VjVtd1E3QzNkWmhqd0g0amxWc2RDVTlNdVl2QllQRktCTEJkWU52K09ZeW1MTFJGTU9odVFuSDhuZFRRQVV6VjJXRTF4dHdlVG1wSVFzdXdmVzRIdzAifQo.eyJleHAiOjE0Njc1MDg0NDksImV4dHJhRGF0YSI6eyJkaXNwbGF5TmFtZSI6Imd1cnVueHgiLCJpZGVudGl0eSI6IjRlMDE5OWM2LTdjZmQtMzU1MC1iNjc2LTc0Mzk4ZTBhNWYxYSJ9LCJpZGVudGl0eVB1YmxpY0tleSI6Ik1IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFREVLck5xdk93Y25iV3I5aUtVQ0MyeklFRmZ6Q0VnUEhQdG5Kd3VEdnZ3VjVtd1E3QzNkWmhqd0g0amxWc2RDVTlNdVl2QllQRktCTEJkWU52K09ZeW1MTFJGTU9odVFuSDhuZFRRQVV6VjJXRTF4dHdlVG1wSVFzdXdmVzRIdzAiLCJuYmYiOjE0Njc1MDg0NDh9Cg.jpCqzTo8nNVEW8ArK1NFBaqLx6kyJV6wPF8cAU6UGav6cfMc60o3m5DjwspN-JcyC14AlcNiPdWX8TEm1QFhtScb-bXo4WOJ0dNYXV8iI_eCTCcXMFjX4vgIHpb9xfjv"; val = $@"{{ ""chain"": [""{val}""] }}"; return(Encoding.UTF8.GetBytes(val)); }
/// <summary> /// Decrypts a blob of data encrypted using <see cref="EncryptAndSign"/> after verifying the digital signature on it. /// </summary> /// <param name="container"></param> /// <param name="joseObject">The compact form of the encrypted and signed JOSE object.</param> /// <param name="signedBy">The certificate of the signer will be output into this variable.</param> /// <param name="decryptionCertificates">The set of certificates whose key pairs may be used for decryption. The correct certificate and key pair will be selected automatically (if it is among the set).</param> /// <exception cref="CryptographicException"> /// Thrown if a cryptographic operation fails (e.g. because you do not have the correct decryption key). /// </exception> public static byte[] VerifyAndDecrypt(this HelpersContainerClasses.Jose container, string joseObject, out X509Certificate2 signedBy, params X509Certificate2[] decryptionCertificates) { Helpers.Argument.ValidateIsNotNull(joseObject, nameof(joseObject)); Helpers.Argument.ValidateIsNotNull(decryptionCertificates, nameof(decryptionCertificates)); foreach (var decryptionCertificate in decryptionCertificates) { if (decryptionCertificate == null) { throw new ArgumentException("Decryption certificate list cannot contain null values.", nameof(decryptionCertificates)); } VerifyCertificateAndPrivateKeyIsSaneAndUsable(decryptionCertificate); } // Part 1: verify signature. var signedHeader = JWT.Headers(joseObject); if (!signedHeader.ContainsKey("typ") || signedHeader["typ"] as string != JoseSignedType) { throw new ArgumentException("The JOSE object was not produced by " + nameof(EncryptAndSign), nameof(joseObject)); } signedBy = GetCertificateFromJoseHeader(signedHeader); VerifyCertificateIsSaneAndUsable(signedBy); var encrypted = JWT.Decode(joseObject, signedBy.GetRSAPublicKey(), JwsAlgorithm.RS512); // Part 2: decrypt. var encryptedHeader = JWT.Headers(encrypted); if (!encryptedHeader.ContainsKey("typ") || encryptedHeader["typ"] as string != JoseEncryptedType) { throw new ArgumentException("The JOSE object was not produced by " + nameof(EncryptAndSign), nameof(joseObject)); } var encryptedFor = GetCertificateFromJoseHeader(encryptedHeader); var decryptWith = decryptionCertificates.FirstOrDefault(c => c.Thumbprint == encryptedFor.Thumbprint); if (decryptWith == null) { throw new CryptographicException("None of the available decryption keys matched the one used to encrypt the JOSE object."); } return(JWT.DecodeBytes(encrypted, decryptWith.GetRSAPrivateKey(), JweAlgorithm.RSA_OAEP, JweEncryption.A256CBC_HS512)); }
public string CheckSignatureAndDecryptJWT(string jsonToken, List <OIDCKey> OPKeys = null, string ClientSecret = null, List <OIDCKey> RPKeys = null) { Dictionary <string, object> headers = null; try { headers = (Dictionary <string, object>)JWT.Headers(jsonToken); } catch (Exception) { // No JWT in the message. return(jsonToken); } if (headers.ContainsKey("enc")) { string kid = (headers.ContainsKey("kid")) ? headers["kid"] as string : null; RSACryptoServiceProvider encKey = null; if (RPKeys.Count == 1) { encKey = RPKeys[0].GetRSA(); } else { encKey = RPKeys.Find( delegate(OIDCKey k) { return(k.Kid == kid); } ).GetRSA(); } jsonToken = JWT.Decode(jsonToken, encKey); try { headers = (Dictionary <string, object>)JWT.Headers(jsonToken); } catch (Exception) { // No JWT in the message. return(jsonToken); } } object sigKey = GetSignKey(headers, OPKeys, ClientSecret); return(JWT.Decode(jsonToken, sigKey)); }
private byte[] EncodeJwt(CertificateData certificateData, string b64Key, ECDsa signKey) { string val = JWT.Encode(certificateData, signKey, JwsAlgorithm.ES384, new Dictionary <string, object> { { "x5u", b64Key } }, new JwtSettings() { JsonMapper = new JWTMapper() }); Log.Warn(JWT.Payload(val)); Log.Warn(string.Join(";", JWT.Headers(val))); val = $@"{{ ""chain"": [""{val}""] }}"; return(Encoding.UTF8.GetBytes(val)); }
public void CheckClaim(JToken token) { JArray signatures = (JArray)token; string payloadString = Base64Url.Encode(Encoding.UTF8.GetBytes(payload.ToString(Formatting.None))); foreach (JToken signature in signatures) { var protectedPart = signature["protected"]; var signaturePart = signature["signature"]; var compactJWS = protectedPart + "." + payloadString + "." + signaturePart; var headers = JWT.Headers(compactJWS); // Get jwk and it's kid var jwk = JObject.FromObject(headers["jwk"]); string kid = jwk["kid"].ToString(); // compute the actual key's thumbprint string computedThumbprint = Base64Url.Encode(Util.Sha256Hash(Encoding.ASCII.GetBytes( $"{{\"crv\":\"{jwk["crv"]}\",\"kty\":\"{jwk["kty"]}\",\"x\":\"{jwk["x"]}\",\"y\":\"{jwk["y"]}\"}}"))); // Based on the kid - check that recently computed thumbprint is matching with one of the sent in the claims var claimToCheck = kid.Substring(0, kid.Length - "_jwk".Length); if (payload[claimToCheck] == null) { throw new ValidationException("Missing app signed claim"); } if (payload[claimToCheck].ToString().Equals(computedThumbprint) == false) { throw new ValidationException($"Bad signiture {claimToCheck}"); } // - check the signature try { Util.VerifyTokenSignature(compactJWS, jwk); } catch (Exception) { throw new ValidationException("Invalid signatures"); } } }
public EncodedParameters Verify(string token) { if (token == null) { throw new ArgumentNullException("token"); } try { var headers = JWT.Headers(token); if (headers == null || !headers.ContainsKey(HttpSigningConstants.Jwk.AlgorithmProperty)) { Logger.Error("Token does not contain " + HttpSigningConstants.Jwk.AlgorithmProperty + " property in header"); return(null); } var alg = headers[HttpSigningConstants.Jwk.AlgorithmProperty]; if (!Alg.Equals(alg)) { Logger.Error("Signature alg does not match token alg"); return(null); } var json = JWT.Decode(token, _key); if (json == null) { Logger.Error("Failed to decode token"); return(null); } return(EncodedParameters.FromJson(json)); } catch (Exception ex) { Logger.ErrorException("Failed to decode token", ex); } return(null); }
private AuthenticateResult validateToken(string token) { var headers = JWT.Headers(token); var jwk = headers.FirstOrDefault(t => t.Key == "jwk").Value; var jwkDictionary = JsonConvert.DeserializeObject <Dictionary <string, string> >(JsonConvert.SerializeObject(jwk)); var e = jwkDictionary.First(x => x.Key == "e").Value; var n = jwkDictionary.First(x => x.Key == "n").Value; RSACryptoServiceProvider key = new RSACryptoServiceProvider(); key.ImportParameters(new RSAParameters { Modulus = Base64Url.Decode(n), Exponent = Base64Url.Decode(e) }); var paylod = JWT.Decode(token, key); var json = JObject.Parse(paylod); var rolJson = json.Value <JArray>("authorities").ToString(); var rol = JsonConvert.DeserializeObject <string[]>(rolJson); var claims = new List <Claim> { new Claim(ClaimTypes.Name, json.Value <string>("sub").ToString()), new Claim(ClaimTypes.Expiration, json.Value <string>("exp").ToString()), new Claim(ClaimTypes.Expired, json.Value <string>("iat").ToString()), new Claim(ClaimTypes.NameIdentifier, json.Value <string>("userId").ToString()), new Claim(ClaimTypes.Role, rolJson), }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new System.Security.Principal.GenericPrincipal(identity, rol); var ticket = new AuthenticationTicket(principal, Scheme.Name); return(AuthenticateResult.Success(ticket)); }
public static bool JwtTokenValidate(HttpContext context, TryConvert2ClaimsIdentity tryConvert2ClaimsIdentity) { string authorization = context.Request.Headers["Authorization"]; if (!string.IsNullOrEmpty(authorization) && authorization.Contains(".") && authorization.StartsWith("User ", StringComparison.OrdinalIgnoreCase)) { var tokenValue = authorization.Substring("User ".Length).Trim(); var jwtHeader = JWT.Headers(tokenValue); if ((jwtHeader["alg"] == null || !jwtHeader["alg"].Equals("RSA1_5")) || (jwtHeader["enc"] == null || !jwtHeader["enc"].Equals("A256GCM"))) { return(false); } tryConvert2ClaimsIdentity(CustomAuthorizationScheme.User, tokenValue, out var identity); if (identity != null) { context.User = new ClaimsPrincipal(identity); return(true); } } return(false); }
public async Task <IJwtToken> ValidateTokenAsync(IOauthToken token) { IJwtToken jwtToken; using (HttpResponseMessage response = await client.GetAsync(config.JwtKeySetEndpoint)) { Stream stream = await response.Content.ReadAsStreamAsync(); JwtKeySet keySet = await JsonSerializer.DeserializeAsync <JwtKeySet>(stream); var headers = JWT.Headers(token.AccessToken); var jwk = keySet.Keys[1]; RSACryptoServiceProvider key = new RSACryptoServiceProvider(); key.ImportParameters(new RSAParameters { Modulus = Base64Url.Decode(jwk.n), Exponent = Base64Url.Decode(jwk.e) }); jwtToken = JWT.Decode <JwtToken>(token.AccessToken, key); if (jwtToken.Issuer != "login.eveonline.com") { throw new Exception("Invalid JWT Token!"); } uint unixTimestamp = (uint)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; if (jwtToken.Expiery < unixTimestamp) { throw new Exception("Invalid JWT Token"); } } return(jwtToken); }
public void TestJWTHandling() { var newKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP384, null, new CngKeyCreationParameters() { ExportPolicy = CngExportPolicies.AllowPlaintextExport, KeyUsage = CngKeyUsages.AllUsages }); var t = CryptoUtils.ImportECDsaCngKeyFromCngKey(newKey.Export(CngKeyBlobFormat.EccPrivateBlob)); var tk = CngKey.Import(t, CngKeyBlobFormat.EccPrivateBlob); Assert.AreEqual(CngAlgorithmGroup.ECDsa, tk.AlgorithmGroup); ECDiffieHellmanCng ecKey = new ECDiffieHellmanCng(newKey); ecKey.HashAlgorithm = CngAlgorithm.Sha256; ecKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; var b64key = Convert.ToBase64String(ecKey.PublicKey.GetDerEncoded()); string test = $@" {{ ""exp"": 1464983845, ""extraData"": {{ ""displayName"": ""gurunx"", ""identity"": ""af6f7c5e -fcea-3e43-bf3a-e005e400e578"" }}, ""identityPublicKey"": ""{b64key}"" ""nbf"": 1464983844 }}"; string val = JWT.Encode(test, tk, JwsAlgorithm.ES384, new Dictionary <string, object> { { "x5u", b64key } }); Assert.AreEqual(b64key, JWT.Headers(val)["x5u"]); //Assert.AreEqual("", string.Join(";", JWT.Headers(val))); Assert.AreEqual(test, JWT.Payload(val)); }
/// <summary> /// Validate that the token received has not been tampered with and get additional character information. /// </summary> /// <param name="credential"></param> /// <returns></returns> static async Task <JwtToken> ValidateCredentials(EveCredentials credential) { JwtToken token; using (HttpResponseMessage response = await Client.GetAsync("https://login.eveonline.com/oauth/jwks")) { string json = await response.Content.ReadAsStringAsync(); Dictionary <string, JToken> keys = JsonConvert.DeserializeObject <Dictionary <string, JToken> >(json); var headers = JWT.Headers(credential.AccessToken); var jwk = keys["keys"]; RSACryptoServiceProvider key = new RSACryptoServiceProvider(); key.ImportParameters(new RSAParameters { Modulus = Base64Url.Decode(jwk[1]["n"].ToString()), Exponent = Base64Url.Decode(jwk[1]["e"].ToString()) }); token = JWT.Decode <JwtToken>(credential.AccessToken, key); if (token.Issuer != "login.eveonline.com") { throw new Exception("Invalid JWT Token"); } int unixTimestamp = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; if (token.Expiery < unixTimestamp) { throw new Exception("Invalid JWT Token"); } } return(token); }
protected void DecodeCert(McpeLogin message) { _playerInfo = new PlayerInfo(); // Get bytes byte[] buffer = message.payload; //Log.Debug($"Unknown byte in login packet is: {message.unknown}"); if (message.payload.Length != buffer.Length) { Log.Debug($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); throw new Exception($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); } // Decompress bytes Log.Debug("Lenght: " + message.payload.Length + ", Message: " + Convert.ToBase64String(buffer)); //MemoryStream stream = new MemoryStream(buffer); //if (stream.ReadByte() != 0x78) //{ // throw new InvalidDataException("Incorrect ZLib header. Expected 0x78 0x9C"); //} //stream.ReadByte(); string certificateChain; string skinData; //using (var defStream2 = new DeflateStream(stream, CompressionMode.Decompress, false)) { // Get actual package out of bytes //using (MemoryStream destination = MiNetServer.MemoryStreamManager.GetStream()) { //defStream2.CopyTo(destination); var destination = new MemoryStream(buffer); destination.Position = 0; NbtBinaryReader reader = new NbtBinaryReader(destination, false); try { var countCertData = reader.ReadInt32(); Log.Debug("Count cert: " + countCertData); certificateChain = Encoding.UTF8.GetString(reader.ReadBytes(countCertData)); Log.Debug("Decompressed certificateChain " + certificateChain); var countSkinData = reader.ReadInt32(); Log.Debug("Count skin: " + countSkinData); skinData = Encoding.UTF8.GetString(reader.ReadBytes(countSkinData)); Log.Debug("Decompressed skinData" + skinData); } catch (Exception e) { Log.Error("Parsing login", e); return; } } } try { { if (Log.IsDebugEnabled) { Log.Debug("Input SKIN string: " + skinData); } IDictionary <string, dynamic> headers = JWT.Headers(skinData); dynamic payload = JObject.Parse(JWT.Payload(skinData)); if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Header: {string.Join(";", headers)}"); } if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Payload:\n{payload.ToString()}"); } // Skin JWT Payload: //{ // "ADRole": 2, // "ClientRandomId": -1256727416, // "CurrentInputMode": 2, // "DefaultInputMode": 2, // "DeviceModel": "SAMSUNG GT-P5210", // "DeviceOS": 1, // "GameVersion": "1.0.3.0", // "GuiScale": 0, // "ServerAddress": "192.168.0.3:19132", // "SkinData": "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Af/3/wH/9/8B//f/Af/3/wAAAP/yrmj/8q5o//KuaP/yrmj/8q5o//KuaP/yrmj/8q5o/wMCAv8B//f/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD//wAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgD/AQEB/wEBAf8BAQH//fn2//359v/9+fb//fn2/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQUF/wkICP8JCQn/EhIS//KuaP/yrmj/AAAA/wkJCf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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", // "SkinId": "Standard_Custom", // "TenantId": "", // "UIProfile": 1 //} try { _playerInfo.ADRole = payload.ADRole; _playerInfo.ClientId = payload.ClientRandomId; _playerInfo.CurrentInputMode = payload.CurrentInputMode; _playerInfo.DefaultInputMode = payload.DefaultInputMode; _playerInfo.DeviceModel = payload.DeviceModel; _playerInfo.DeviceOS = payload.DeviceOS; _playerInfo.GameVersion = payload.GameVersion; _playerInfo.GuiScale = payload.GuiScale; _playerInfo.ServerAddress = payload.ServerAddress; _playerInfo.UIProfile = payload.UIProfile; _playerInfo.Skin = new Skin() { SkinType = payload.SkinId, Texture = Convert.FromBase64String((string)payload.SkinData), }; } catch (Exception e) { Log.Error("Skin info", e); } } { if (Log.IsDebugEnabled) { Log.Debug("Input JSON string: " + certificateChain); } dynamic json = JObject.Parse(certificateChain); if (Log.IsDebugEnabled) { Log.Debug($"JSON:\n{json}"); } string validationKey = null; foreach (dynamic o in json.chain) { IDictionary <string, dynamic> headers = JWT.Headers(o.ToString()); if (Log.IsDebugEnabled) { Log.Debug("Raw chain element:\n" + o.ToString()); Log.Debug($"JWT Header: {string.Join(";", headers)}"); dynamic jsonPayload = JObject.Parse(JWT.Payload(o.ToString())); Log.Debug($"JWT Payload:\n{jsonPayload}"); } // x5u cert (string): MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V if (headers.ContainsKey("x5u")) { string certString = headers["x5u"]; if (Log.IsDebugEnabled) { Log.Debug($"x5u cert (string): {certString}"); ECDiffieHellmanPublicKey publicKey = CryptoUtils.CreateEcDiffieHellmanPublicKey(certString); Log.Debug($"Cert:\n{publicKey.ToXmlString()}"); } // Validate CngKey newKey = CryptoUtils.ImportECDsaCngKeyFromString(certString); CertificateData data = JWT.Decode <CertificateData>(o.ToString(), newKey); if (data != null) { if (Log.IsDebugEnabled) { Log.Debug("Decoded token success"); } if (CertificateData.MojangRootKey.Equals(certString, StringComparison.InvariantCultureIgnoreCase)) { Log.Debug("Got Mojang key. Is valid = " + data.CertificateAuthority); validationKey = data.IdentityPublicKey; } else if (validationKey != null && validationKey.Equals(certString, StringComparison.InvariantCultureIgnoreCase)) { _playerInfo.CertificateData = data; } else { if (data.ExtraData == null) { continue; } // Self signed, make sure they don't fake XUID if (data.ExtraData.Xuid != null) { Log.Warn("Received fake XUID from " + data.ExtraData.DisplayName); data.ExtraData.Xuid = null; } _playerInfo.CertificateData = data; } } else { Log.Error("Not a valid Identity Public Key for decoding"); } } } //TODO: Implement disconnect here { _playerInfo.Username = _playerInfo.CertificateData.ExtraData.DisplayName; _session.Username = _playerInfo.Username; string identity = _playerInfo.CertificateData.ExtraData.Identity; if (Log.IsDebugEnabled) { Log.Debug($"Connecting user {_playerInfo.Username} with identity={identity}"); } _playerInfo.ClientUuid = new UUID(identity); _session.CryptoContext = new CryptoContext { UseEncryption = Config.GetProperty("UseEncryptionForAll", false) || (Config.GetProperty("UseEncryption", true) && !string.IsNullOrWhiteSpace(_playerInfo.CertificateData.ExtraData.Xuid)), }; if (_session.CryptoContext.UseEncryption) { ECDiffieHellmanPublicKey publicKey = CryptoUtils.CreateEcDiffieHellmanPublicKey(_playerInfo.CertificateData.IdentityPublicKey); if (Log.IsDebugEnabled) { Log.Debug($"Cert:\n{publicKey.ToXmlString()}"); } // Create shared shared secret ECDiffieHellmanCng ecKey = new ECDiffieHellmanCng(384); ecKey.HashAlgorithm = CngAlgorithm.Sha256; ecKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; ecKey.SecretPrepend = Encoding.UTF8.GetBytes("RANDOM SECRET"); // Server token byte[] secret = ecKey.DeriveKeyMaterial(publicKey); if (Log.IsDebugEnabled) { Log.Debug($"SECRET KEY (b64):\n{Convert.ToBase64String(secret)}"); } { RijndaelManaged rijAlg = new RijndaelManaged { BlockSize = 128, Padding = PaddingMode.None, Mode = CipherMode.CFB, FeedbackSize = 8, Key = secret, IV = secret.Take(16).ToArray(), }; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); MemoryStream inputStream = new MemoryStream(); CryptoStream cryptoStreamIn = new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read); ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); MemoryStream outputStream = new MemoryStream(); CryptoStream cryptoStreamOut = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write); _session.CryptoContext.Algorithm = rijAlg; _session.CryptoContext.Decryptor = decryptor; _session.CryptoContext.Encryptor = encryptor; _session.CryptoContext.InputStream = inputStream; _session.CryptoContext.OutputStream = outputStream; _session.CryptoContext.CryptoStreamIn = cryptoStreamIn; _session.CryptoContext.CryptoStreamOut = cryptoStreamOut; } var response = McpeServerToClientHandshake.CreateObject(); response.NoBatch = true; response.ForceClear = true; response.serverPublicKey = Convert.ToBase64String(ecKey.PublicKey.GetDerEncoded()); response.tokenLength = (short)ecKey.SecretPrepend.Length; response.token = ecKey.SecretPrepend; _session.SendPackage(response); if (Log.IsDebugEnabled) { Log.Warn($"Encryption enabled for {_session.Username}"); } } } } if (!_session.CryptoContext.UseEncryption) { _session.MessageHandler.HandleMcpeClientToServerHandshake(null); } } catch (Exception e) { Log.Error("Decrypt", e); } }
protected void DecodeCert(McpeLogin message) { // Get bytes byte[] buffer = message.payload; //Log.Debug($"Unknown byte in login packet is: {message.unknown}"); if (message.payload.Length != buffer.Length) { Log.Debug($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); throw new Exception($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); } // Decompress bytes Log.Debug("Lenght: " + message.payload.Length + ", Message: " + Convert.ToBase64String(buffer)); //MemoryStream stream = new MemoryStream(buffer); //if (stream.ReadByte() != 0x78) //{ // throw new InvalidDataException("Incorrect ZLib header. Expected 0x78 0x9C"); //} //stream.ReadByte(); string certificateChain; string skinData; //using (var defStream2 = new DeflateStream(stream, CompressionMode.Decompress, false)) { // Get actual package out of bytes //using (MemoryStream destination = MiNetServer.MemoryStreamManager.GetStream()) { //defStream2.CopyTo(destination); var destination = new MemoryStream(buffer); destination.Position = 0; NbtBinaryReader reader = new NbtBinaryReader(destination, false); try { var countCertData = reader.ReadInt32(); Log.Debug("Count cert: " + countCertData); certificateChain = Encoding.UTF8.GetString(reader.ReadBytes(countCertData)); Log.Debug("Decompressed certificateChain " + certificateChain); var countSkinData = reader.ReadInt32(); Log.Debug("Count skin: " + countSkinData); skinData = Encoding.UTF8.GetString(reader.ReadBytes(countSkinData)); Log.Debug("Decompressed skinData" + skinData); } catch (Exception e) { Log.Error("Parsing login", e); return; } } } try { { if (Log.IsDebugEnabled) { Log.Debug("Input SKIN string: " + skinData); } IDictionary <string, dynamic> headers = JWT.Headers(skinData); dynamic payload = JObject.Parse(JWT.Payload(skinData)); if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Header: {string.Join(";", headers)}"); } if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Payload:\n{payload.ToString()}"); } Console.WriteLine(payload); // Skin JWT Payload: //{ // "ClientRandomId": 1423700530444426768, // "CurrentInputMode": 1, // "DefaultInputMode": 1, // "DeviceModel": "ASUSTeK COMPUTER INC. N550JK", // "DeviceOS": 7, // "GameVersion": "1.2.0", // "GuiScale": 0, // "LanguageCode": "en_US", // "ServerAddress": "192.168.0.3:19132", // "SkinData": "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", // "SkinId": "Standard_Custom", // "UIProfile": 0 //} try { _playerInfo.ClientId = payload.ClientRandomId; _playerInfo.CurrentInputMode = payload.CurrentInputMode; _playerInfo.DefaultInputMode = payload.DefaultInputMode; _playerInfo.DeviceModel = payload.DeviceModel; _playerInfo.DeviceOS = payload.DeviceOS; _playerInfo.GameVersion = payload.GameVersion; _playerInfo.GuiScale = payload.GuiScale; _playerInfo.LanguageCode = payload.LanguageCode; _playerInfo.ServerAddress = payload.ServerAddress; _playerInfo.UIProfile = payload.UIProfile; _playerInfo.Skin = new Skin() { SkinType = payload.SkinId, Texture = Convert.FromBase64String((string)payload.SkinData), GeometryType = (string)payload.SkinGeometryName, GeometryData = Encoding.ASCII.GetString(Convert.FromBase64String((string)payload.SkinGeometryData)) }; } catch (Exception e) { Log.Error("Skin info", e); } } { if (Log.IsDebugEnabled) { Log.Debug("Input JSON string: " + certificateChain); } dynamic json = JObject.Parse(certificateChain); if (Log.IsDebugEnabled) { Log.Debug($"JSON:\n{json}"); } string validationKey = null; JArray chain = json.chain; var chainArray = chain.ToArray(); string identityPublicKey = null; foreach (dynamic o in chainArray) { IDictionary <string, dynamic> headers = JWT.Headers(o.ToString()); if (Log.IsDebugEnabled) { Log.Debug("Raw chain element:\n" + o.ToString()); Log.Debug($"JWT Header: {string.Join(";", headers)}"); dynamic jsonPayload = JObject.Parse(JWT.Payload(o.ToString())); Log.Debug($"JWT Payload:\n{jsonPayload}"); } // x5u cert (string): MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V if (headers.ContainsKey("x5u")) { string certString = headers["x5u"]; if (identityPublicKey == null && CertificateData.MojangRootKey.Equals(certString, StringComparison.InvariantCultureIgnoreCase)) { Log.Debug("Key is ok, and got Mojang root"); } else if (identityPublicKey == null) { if (chainArray.Length > 1) { Log.Debug("Got client cert (client root)"); continue; } else if (chainArray.Length == 1) { Log.Debug("Selfsigned chain"); } } else if (identityPublicKey.Equals(certString)) { Log.Debug("Derived Key is ok"); } if (Log.IsDebugEnabled) { Log.Debug($"x5u cert (string): {certString}"); ECDiffieHellmanPublicKey publicKey = CryptoUtils.CreateEcDiffieHellmanPublicKey(certString); Log.Debug($"Cert:\n{publicKey.ToXmlString()}"); } // Validate var newKey = CryptoUtils.ImportECDsaCngKeyFromString(certString); CertificateData data = JWT.Decode <CertificateData>(o.ToString(), newKey); if (data != null) { identityPublicKey = data.IdentityPublicKey; if (Log.IsDebugEnabled) { Log.Debug("Decoded token success"); } if (CertificateData.MojangRootKey.Equals(certString, StringComparison.InvariantCultureIgnoreCase)) { Log.Debug("Got Mojang key. Is valid = " + data.CertificateAuthority); validationKey = data.IdentityPublicKey; } else if (validationKey != null && validationKey.Equals(certString, StringComparison.InvariantCultureIgnoreCase)) { _playerInfo.CertificateData = data; } else { if (data.ExtraData == null) { continue; } // Self signed, make sure they don't fake XUID if (data.ExtraData.Xuid != null) { Log.Warn("Received fake XUID from " + data.ExtraData.DisplayName); data.ExtraData.Xuid = null; } _playerInfo.CertificateData = data; } } else { Log.Error("Not a valid Identity Public Key for decoding"); } } } //TODO: Implement disconnect here { _playerInfo.Username = _playerInfo.CertificateData.ExtraData.DisplayName; _session.Username = _playerInfo.Username; string identity = _playerInfo.CertificateData.ExtraData.Identity; if (Log.IsDebugEnabled) { Log.Debug($"Connecting user {_playerInfo.Username} with identity={identity}"); } _playerInfo.ClientUuid = new UUID(identity); _session.CryptoContext = new CryptoContext { UseEncryption = Config.GetProperty("UseEncryptionForAll", false) || (Config.GetProperty("UseEncryption", true) && !string.IsNullOrWhiteSpace(_playerInfo.CertificateData.ExtraData.Xuid)), }; if (_session.CryptoContext.UseEncryption) { ECDiffieHellmanPublicKey publicKey = CryptoUtils.CreateEcDiffieHellmanPublicKey(_playerInfo.CertificateData.IdentityPublicKey); if (Log.IsDebugEnabled) { Log.Debug($"Cert:\n{publicKey.ToXmlString()}"); } // Create shared shared secret ECDiffieHellmanCng ecKey = new ECDiffieHellmanCng(384); ecKey.HashAlgorithm = CngAlgorithm.Sha256; ecKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; ecKey.SecretPrepend = Encoding.UTF8.GetBytes("RANDOM SECRET"); // Server token byte[] secret = ecKey.DeriveKeyMaterial(publicKey); if (Log.IsDebugEnabled) { Log.Debug($"SECRET KEY (b64):\n{Convert.ToBase64String(secret)}"); } { RijndaelManaged rijAlg = new RijndaelManaged { BlockSize = 128, Padding = PaddingMode.None, Mode = CipherMode.CFB, FeedbackSize = 8, Key = secret, IV = secret.Take(16).ToArray(), }; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); MemoryStream inputStream = new MemoryStream(); CryptoStream cryptoStreamIn = new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read); ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); MemoryStream outputStream = new MemoryStream(); CryptoStream cryptoStreamOut = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write); _session.CryptoContext.Algorithm = rijAlg; _session.CryptoContext.Decryptor = decryptor; _session.CryptoContext.Encryptor = encryptor; _session.CryptoContext.InputStream = inputStream; _session.CryptoContext.OutputStream = outputStream; _session.CryptoContext.CryptoStreamIn = cryptoStreamIn; _session.CryptoContext.CryptoStreamOut = cryptoStreamOut; } var response = McpeServerToClientHandshake.CreateObject(); response.NoBatch = true; response.ForceClear = true; string b64Key = Convert.ToBase64String(ecKey.PublicKey.GetDerEncoded()); var j = CngKey.Create(CngAlgorithm.ECDiffieHellmanP384, null, new CngKeyCreationParameters() { ExportPolicy = CngExportPolicies.AllowPlaintextExport, KeyUsage = CngKeyUsages.AllUsages }); var jwt = JWT.Encode(new object[] { new [] { "salt", "sNUMIRcN2BSNRw93P5vGpg==" } }, j, JwsAlgorithm.ES384, new Dictionary <string, object> { { "x5u", b64Key } }); response.token = jwt; _session.SendPackage(response); if (Log.IsDebugEnabled) { Log.Warn($"Encryption enabled for {_session.Username}"); } } } } if (!_session.CryptoContext.UseEncryption) { _session.MessageHandler.HandleMcpeClientToServerHandshake(null); } } catch (Exception e) { Log.Error("Decrypt", e); } }
protected void DecodeCertMono(McpeLogin message) { byte[] buffer = message.payload; if (message.payload.Length != buffer.Length) { Log.Debug($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); throw new Exception($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); } if (Log.IsDebugEnabled) { Log.Debug("Lenght: " + message.payload.Length + ", Message: " + buffer.EncodeBase64()); } string certificateChain; string skinData; try { var destination = new MemoryStream(buffer); destination.Position = 0; NbtBinaryReader reader = new NbtBinaryReader(destination, false); var countCertData = reader.ReadInt32(); certificateChain = Encoding.UTF8.GetString(reader.ReadBytes(countCertData)); if (Log.IsDebugEnabled) { Log.Debug($"Certificate Chain (Lenght={countCertData})\n{certificateChain}"); } var countSkinData = reader.ReadInt32(); skinData = Encoding.UTF8.GetString(reader.ReadBytes(countSkinData)); if (Log.IsDebugEnabled) { Log.Debug($"Skin data (Lenght={countSkinData})\n{skinData}"); } } catch (Exception e) { Log.Error("Parsing login", e); return; } try { { IDictionary <string, dynamic> headers = JWT.Headers(skinData); dynamic payload = JObject.Parse(JWT.Payload(skinData)); if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Header: {string.Join(";", headers)}"); } if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Payload:\n{payload.ToString()}"); } try { _playerInfo.ClientId = payload.ClientRandomId; _playerInfo.CurrentInputMode = payload.CurrentInputMode; _playerInfo.DefaultInputMode = payload.DefaultInputMode; _playerInfo.DeviceModel = payload.DeviceModel; _playerInfo.DeviceOS = payload.DeviceOS; _playerInfo.GameVersion = payload.GameVersion; _playerInfo.GuiScale = payload.GuiScale; _playerInfo.LanguageCode = payload.LanguageCode; _playerInfo.ServerAddress = payload.ServerAddress; _playerInfo.UIProfile = payload.UIProfile; _playerInfo.Skin = new Skin() { CapeData = Convert.FromBase64String((string)payload.CapeData), SkinId = payload.SkinId, SkinData = Convert.FromBase64String((string)payload.SkinData), SkinGeometryName = payload.SkinGeometryName, SkinGeometry = Encoding.UTF8.GetString(Convert.FromBase64String((string)payload.SkinGeometry)), }; Log.Warn($"Cape data lenght={_playerInfo.Skin.CapeData.Length}"); } catch (Exception e) { Log.Error("Parsing skin data", e); } } //var chainArray = chain.ToArray(); string validationKey = null; string identityPublicKey = null; //if (!isMono) { dynamic json = JObject.Parse(certificateChain); if (Log.IsDebugEnabled) { Log.Debug($"Certificate JSON:\n{json}"); } JArray chain = json.chain; foreach (JToken token in chain) { IDictionary <string, dynamic> headers = JWT.Headers(token.ToString()); if (Log.IsDebugEnabled) { Log.Debug("Raw chain element:\n" + token.ToString()); Log.Debug($"JWT Header: {string.Join(";", headers)}"); dynamic jsonPayload = JObject.Parse(JWT.Payload(token.ToString())); Log.Debug($"JWT Payload:\n{jsonPayload}"); } // Mojang root x5u cert (string): MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V if (!headers.ContainsKey("x5u")) { continue; } string x5u = headers["x5u"]; if (identityPublicKey == null) { if (CertificateData.MojangRootKey.Equals(x5u, StringComparison.InvariantCultureIgnoreCase)) { Log.Debug("Key is ok, and got Mojang root"); } else if (chain.Count > 1) { Log.Debug("Got client cert (client root)"); continue; } else if (chain.Count == 1) { Log.Debug("Selfsigned chain"); } } else if (identityPublicKey.Equals(x5u)) { Log.Debug("Derived Key is ok"); } // Validate var key = PublicKeyFactory.CreateKey(x5u.DecodeBase64Url()); CertificateData data = CryptoUtils.Decode(token.ToString(), key); if (data != null) { identityPublicKey = data.IdentityPublicKey; if (Log.IsDebugEnabled) { Log.Debug("Decoded token success"); } if (CertificateData.MojangRootKey.Equals(x5u, StringComparison.InvariantCultureIgnoreCase)) { Log.Debug("Got Mojang key. Is valid = " + data.CertificateAuthority); validationKey = data.IdentityPublicKey; } else if (validationKey != null && validationKey.Equals(x5u, StringComparison.InvariantCultureIgnoreCase)) { _playerInfo.CertificateData = data; } else { if (data.ExtraData == null) { continue; } // Self signed, make sure they don't fake XUID if (data.ExtraData.Xuid != null) { Log.Warn("Received fake XUID from " + data.ExtraData.DisplayName); data.ExtraData.Xuid = null; } _playerInfo.CertificateData = data; } } else { Log.Error("Not a valid Identity Public Key for decoding"); } } } //TODO: Implement disconnect here _playerInfo.Username = _playerInfo.CertificateData.ExtraData.DisplayName; _session.Username = _playerInfo.Username; string identity = _playerInfo.CertificateData.ExtraData.Identity; if (Log.IsDebugEnabled) { Log.Debug($"Connecting user {_playerInfo.Username} with identity={identity}"); } _playerInfo.ClientUuid = new UUID(identity); bool useEncryption = (Config.GetProperty("UseEncryptionForAll", false) || (Config.GetProperty("UseEncryption", true) && !string.IsNullOrWhiteSpace(_playerInfo.CertificateData.ExtraData.Xuid))); if (useEncryption) { var publicKey = PublicKeyFactory.CreateKey(_playerInfo.CertificateData.IdentityPublicKey.DecodeBase64Url()); string namedCurve = "secp384r1"; ECKeyPairGenerator pGen = new ECKeyPairGenerator(); ECKeyGenerationParameters genParam = new ECKeyGenerationParameters( SecNamedCurves.GetOid(namedCurve), new SecureRandom()); pGen.Init(genParam); AsymmetricCipherKeyPair keyPair = pGen.GenerateKeyPair(); ECDHBasicAgreement agreement = new ECDHBasicAgreement(); agreement.Init(keyPair.Private); byte[] preHash = agreement.CalculateAgreement(publicKey).ToByteArray(); byte[] prepend = Encoding.UTF8.GetBytes("RANDOM SECRET"); byte[] secret; SHA256Managed sha = new SHA256Managed(); using (var memoryStream = new MemoryStream()) { memoryStream.Write(prepend, 0, prepend.Length); memoryStream.Write(preHash, 0, preHash.Length); memoryStream.Position = 0; secret = sha.ComputeHash(memoryStream); } sha.Dispose(); //if (Log.IsDebugEnabled) Log.Debug($"SECRET KEY (b64, {secret.Length}):\n{secret.EncodeBase64()}"); { RijndaelManaged rijAlg = new RijndaelManaged { BlockSize = 128, Padding = PaddingMode.None, Mode = CipherMode.CFB, FeedbackSize = 8, Key = secret, IV = secret.Take(16).ToArray(), }; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); MemoryStream inputStream = new MemoryStream(); CryptoStream cryptoStreamIn = new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read); ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); MemoryStream outputStream = new MemoryStream(); CryptoStream cryptoStreamOut = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write); _session.CryptoContext = new CryptoContext { UseEncryption = true, Algorithm = rijAlg, Decryptor = decryptor, Encryptor = encryptor, InputStream = inputStream, OutputStream = outputStream, CryptoStreamIn = cryptoStreamIn, CryptoStreamOut = cryptoStreamOut }; var pubKey1 = ((ECPublicKeyParameters)keyPair.Public); byte[] asn = new byte[24] { 0x30, 0x76, 0x30, 0x10, 0x6, 0x7, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x2, 0x1, 0x6, 0x5, 0x2b, 0x81, 0x4, 0x0, 0x22, 0x3, 0x62, 0x0, 0x4 }; string b64Key = asn.Concat(ConvertToNCryptEccPublicBlob(pubKey1.Q).Skip(8)).ToArray().EncodeBase64(); var handshakeJson = new HandshakeData() { salt = prepend.EncodeBase64() }; string val = CryptoUtils.Encode(handshakeJson, keyPair.Private, JwsAlgorithm.ES384, new Dictionary <string, object> { { "x5u", b64Key } }); var response = McpeServerToClientHandshake.CreateObject(); response.NoBatch = true; response.ForceClear = true; response.token = val; _session.SendPackage(response); if (Log.IsDebugEnabled) { Log.Warn($"Encryption enabled for {_session.Username}"); } } } else { _session.CryptoContext = new CryptoContext { UseEncryption = false }; _session.MessageHandler.HandleMcpeClientToServerHandshake(null); } } catch (Exception e) { Log.Error("Decrypt", e); } }
internal static bool Verify(string token, out string userID) { var headers = JWT.Headers <Dictionary <string, string> >(token); var kid = headers["kid"]; if (cryptoServiceProviders.TryGetValue(kid, out var key)) { string unparsedPayload; try { unparsedPayload = JWT.Decode(token, key, JwsAlgorithm.RS256); } catch (InvalidAlgorithmException) { userID = "INVALID ALGORITHM"; return(false); } catch (IntegrityException) { userID = "INVALID TOKEN"; return(false); } var payload = JsonConvert.DeserializeObject <TokenPayload>(unparsedPayload); var now = ToUnixTime(DateTime.Now); //Must be in the future if (payload.exp <= now) { userID = "TOKEN_EXPIRED"; return(false); } //Must be in the past if (payload.auth_time >= now) { userID = "INVALID AUTHENTICATION TIME"; return(false); } //Must be in the past if (payload.iat >= now) { userID = "INVALID ISSUE-AT-TIME"; return(false); } //Must correspond to projectId if (payload.aud != ProjectId) { userID = "INVALID AUDIENCE"; return(false); } if (payload.iss != "https://securetoken.google.com/" + ProjectId) { userID = "INVALID ISSUER"; return(false); } userID = payload.sub; return(true); } userID = "INVALID TOKEN KID"; return(false); }
protected void DecodeCert(McpeLogin message) { byte[] buffer = message.payload; if (message.payload.Length != buffer.Length) { Log.Debug($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); throw new Exception($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); } if (Log.IsDebugEnabled) { Log.Debug("Lenght: " + message.payload.Length + ", Message: " + Convert.ToBase64String(buffer)); } string certificateChain; string skinData; try { var destination = new MemoryStream(buffer); destination.Position = 0; NbtBinaryReader reader = new NbtBinaryReader(destination, false); var countCertData = reader.ReadInt32(); certificateChain = Encoding.UTF8.GetString(reader.ReadBytes(countCertData)); if (Log.IsDebugEnabled) { Log.Debug($"Certificate Chain (Lenght={countCertData})\n{certificateChain}"); } var countSkinData = reader.ReadInt32(); skinData = Encoding.UTF8.GetString(reader.ReadBytes(countSkinData)); if (Log.IsDebugEnabled) { Log.Debug($"Skin data (Lenght={countSkinData})\n{skinData}"); } } catch (Exception e) { Log.Error("Parsing login", e); return; } try { { IDictionary <string, dynamic> headers = JWT.Headers(skinData); dynamic payload = JObject.Parse(JWT.Payload(skinData)); if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Header: {string.Join(";", headers)}"); } if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Payload:\n{payload.ToString()}"); } try { _playerInfo.ClientId = payload.ClientRandomId; _playerInfo.CurrentInputMode = payload.CurrentInputMode; _playerInfo.DefaultInputMode = payload.DefaultInputMode; _playerInfo.DeviceModel = payload.DeviceModel; _playerInfo.DeviceOS = payload.DeviceOS; _playerInfo.GameVersion = payload.GameVersion; _playerInfo.GuiScale = payload.GuiScale; _playerInfo.LanguageCode = payload.LanguageCode; _playerInfo.ServerAddress = payload.ServerAddress; _playerInfo.UIProfile = payload.UIProfile; _playerInfo.Skin = new Skin() { CapeData = Convert.FromBase64String((string)payload.CapeData), SkinId = payload.SkinId, SkinData = Convert.FromBase64String((string)payload.SkinData), SkinGeometryName = payload.SkinGeometryName, SkinGeometry = Convert.FromBase64String((string)payload.SkinGeometry), }; Log.Warn($"Cape data lenght={_playerInfo.Skin.CapeData.Length}"); } catch (Exception e) { Log.Error("Parsing skin data", e); } } { dynamic json = JObject.Parse(certificateChain); if (Log.IsDebugEnabled) { Log.Debug($"Certificate JSON:\n{json}"); } JArray chain = json.chain; //var chainArray = chain.ToArray(); string validationKey = null; string identityPublicKey = null; foreach (JToken token in chain) { IDictionary <string, dynamic> headers = JWT.Headers(token.ToString()); if (Log.IsDebugEnabled) { Log.Debug("Raw chain element:\n" + token.ToString()); Log.Debug($"JWT Header: {string.Join(";", headers)}"); dynamic jsonPayload = JObject.Parse(JWT.Payload(token.ToString())); Log.Debug($"JWT Payload:\n{jsonPayload}"); } // Mojang root x5u cert (string): MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V if (!headers.ContainsKey("x5u")) { continue; } string x5u = headers["x5u"]; if (identityPublicKey == null) { if (CertificateData.MojangRootKey.Equals(x5u, StringComparison.InvariantCultureIgnoreCase)) { Log.Debug("Key is ok, and got Mojang root"); } else if (chain.Count > 1) { Log.Debug("Got client cert (client root)"); continue; } else if (chain.Count == 1) { Log.Debug("Selfsigned chain"); } } else if (identityPublicKey.Equals(x5u)) { Log.Debug("Derived Key is ok"); } if (Log.IsDebugEnabled) { Log.Debug($"x5u cert (string): {x5u}"); ECDiffieHellmanPublicKey publicKey = CryptoUtils.CreateEcDiffieHellmanPublicKey(x5u); Log.Debug($"Cert:\n{publicKey.ToXmlString()}"); } // Validate CngKey newKey = CryptoUtils.ImportECDsaCngKeyFromString(x5u); CertificateData data = JWT.Decode <CertificateData>(token.ToString(), newKey); if (data != null) { identityPublicKey = data.IdentityPublicKey; if (Log.IsDebugEnabled) { Log.Debug("Decoded token success"); } if (CertificateData.MojangRootKey.Equals(x5u, StringComparison.InvariantCultureIgnoreCase)) { Log.Debug("Got Mojang key. Is valid = " + data.CertificateAuthority); validationKey = data.IdentityPublicKey; } else if (validationKey != null && validationKey.Equals(x5u, StringComparison.InvariantCultureIgnoreCase)) { _playerInfo.CertificateData = data; } else { if (data.ExtraData == null) { continue; } // Self signed, make sure they don't fake XUID if (data.ExtraData.Xuid != null) { Log.Warn("Received fake XUID from " + data.ExtraData.DisplayName); data.ExtraData.Xuid = null; } _playerInfo.CertificateData = data; } } else { Log.Error("Not a valid Identity Public Key for decoding"); } } //TODO: Implement disconnect here { _playerInfo.Username = _playerInfo.CertificateData.ExtraData.DisplayName; _session.Username = _playerInfo.Username; string identity = _playerInfo.CertificateData.ExtraData.Identity; if (Log.IsDebugEnabled) { Log.Debug($"Connecting user {_playerInfo.Username} with identity={identity}"); } _playerInfo.ClientUuid = new UUID(identity); _session.CryptoContext = new CryptoContext { UseEncryption = Config.GetProperty("UseEncryptionForAll", false) || (Config.GetProperty("UseEncryption", true) && !string.IsNullOrWhiteSpace(_playerInfo.CertificateData.ExtraData.Xuid)), }; if (_session.CryptoContext.UseEncryption) { ECDiffieHellmanPublicKey publicKey = CryptoUtils.CreateEcDiffieHellmanPublicKey(_playerInfo.CertificateData.IdentityPublicKey); if (Log.IsDebugEnabled) { Log.Debug($"Cert:\n{publicKey.ToXmlString()}"); } // Create shared shared secret ECDiffieHellmanCng ecKey = new ECDiffieHellmanCng(384); ecKey.HashAlgorithm = CngAlgorithm.Sha256; ecKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; ecKey.SecretPrepend = Encoding.UTF8.GetBytes("RANDOM SECRET"); // Server token byte[] secret = ecKey.DeriveKeyMaterial(publicKey); if (Log.IsDebugEnabled) { Log.Debug($"SECRET KEY (b64):\n{Convert.ToBase64String(secret)}"); } { RijndaelManaged rijAlg = new RijndaelManaged { BlockSize = 128, Padding = PaddingMode.None, Mode = CipherMode.CFB, FeedbackSize = 8, Key = secret, IV = secret.Take(16).ToArray(), }; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); MemoryStream inputStream = new MemoryStream(); CryptoStream cryptoStreamIn = new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read); ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); MemoryStream outputStream = new MemoryStream(); CryptoStream cryptoStreamOut = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write); _session.CryptoContext.Algorithm = rijAlg; _session.CryptoContext.Decryptor = decryptor; _session.CryptoContext.Encryptor = encryptor; _session.CryptoContext.InputStream = inputStream; _session.CryptoContext.OutputStream = outputStream; _session.CryptoContext.CryptoStreamIn = cryptoStreamIn; _session.CryptoContext.CryptoStreamOut = cryptoStreamOut; } //TODO: JSON now. throw new Exception("JSON!!!"); //var response = McpeServerToClientHandshake.CreateObject(); // response.NoBatch = true; // response.ForceClear = true; //response.token = // response.serverPublicKey = Convert.ToBase64String(ecKey.PublicKey.GetDerEncoded()); // response.tokenLength = (short) ecKey.SecretPrepend.Length; // response.token = ecKey.SecretPrepend; //_session.SendPackage(response); if (Log.IsDebugEnabled) { Log.Warn($"Encryption enabled for {_session.Username}"); } } } } if (!_session.CryptoContext.UseEncryption) { _session.MessageHandler.HandleMcpeClientToServerHandshake(null); } } catch (Exception e) { Log.Error("Decrypt", e); } }
public void TestJWTHandling() { CngKey newKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP384, null, new CngKeyCreationParameters() { ExportPolicy = CngExportPolicies.AllowPlaintextExport, KeyUsage = CngKeyUsages.AllUsages }); byte[] t = CryptoUtils.ImportECDsaCngKeyFromCngKey(newKey.Export(CngKeyBlobFormat.EccPrivateBlob)); CngKey tk = CngKey.Import(t, CngKeyBlobFormat.EccPrivateBlob); Assert.AreEqual(CngAlgorithmGroup.ECDsa, tk.AlgorithmGroup); ECDiffieHellmanCng ecKey = new ECDiffieHellmanCng(newKey); ecKey.HashAlgorithm = CngAlgorithm.Sha256; ecKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; var b64Key = Base64Url.Encode(ecKey.PublicKey.GetDerEncoded()); string test = $@" {{ ""exp"": 1464983845, ""extraData"": {{ ""displayName"": ""gurunx"", ""identity"": ""af6f7c5e -fcea-3e43-bf3a-e005e400e578"" }}, ""identityPublicKey"": ""{b64Key}"", ""nbf"": 1464983844 }}"; CertificateData certificateData = new CertificateData { Exp = 1464983845, ExtraData = new ExtraData { DisplayName = "gurun", Identity = "af6f7c5e -fcea-3e43-bf3a-e005e400e578", }, IdentityPublicKey = b64Key, Nbf = 1464983844, }; JWT.JsonMapper = new NewtonsoftMapper(); string val = JWT.Encode(certificateData, tk, JwsAlgorithm.ES384, new Dictionary <string, object> { { "x5u", b64Key } }); Console.WriteLine(val); Assert.AreEqual(b64Key, JWT.Headers(val)["x5u"]); //Assert.AreEqual("", string.Join(";", JWT.Headers(val))); //Assert.AreEqual(test, JWT.Payload(val)); Console.WriteLine(JWT.Payload(val)); IDictionary <string, dynamic> headers = JWT.Headers(val); if (headers.ContainsKey("x5u")) { string certString = headers["x5u"]; // Validate CngKey importKey = CryptoUtils.ImportECDsaCngKeyFromString(certString); CertificateData data = JWT.Decode <CertificateData>(val, importKey); Assert.NotNull(data); Assert.AreEqual(certificateData.Exp, data.Exp); Assert.AreEqual(certificateData.IdentityPublicKey, data.IdentityPublicKey); Assert.AreEqual(certificateData.Nbf, data.Nbf); Assert.NotNull(data.ExtraData); Assert.AreEqual(certificateData.ExtraData.DisplayName, data.ExtraData.DisplayName); Assert.AreEqual(certificateData.ExtraData.Identity, data.ExtraData.Identity); } }
protected void DecodeCert(McpeLogin message) { _playerInfo = new PlayerInfo(); // Get bytes byte[] buffer = message.payload; //Log.Debug($"Unknown byte in login packet is: {message.unknown}"); if (message.payload.Length != buffer.Length) { Log.Debug($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); throw new Exception($"Wrong lenght {message.payload.Length} != {message.payload.Length}"); } // Decompress bytes Log.Debug("Lenght: " + message.payload.Length + ", Message: " + Convert.ToBase64String(buffer)); //MemoryStream stream = new MemoryStream(buffer); //if (stream.ReadByte() != 0x78) //{ // throw new InvalidDataException("Incorrect ZLib header. Expected 0x78 0x9C"); //} //stream.ReadByte(); string certificateChain; string skinData; //using (var defStream2 = new DeflateStream(stream, CompressionMode.Decompress, false)) { // Get actual package out of bytes //using (MemoryStream destination = MiNetServer.MemoryStreamManager.GetStream()) { //defStream2.CopyTo(destination); var destination = new MemoryStream(buffer); destination.Position = 0; NbtBinaryReader reader = new NbtBinaryReader(destination, false); try { var countCertData = reader.ReadInt32(); Log.Debug("Count cert: " + countCertData); certificateChain = Encoding.UTF8.GetString(reader.ReadBytes(countCertData)); Log.Debug("Decompressed certificateChain " + certificateChain); var countSkinData = reader.ReadInt32(); Log.Debug("Count skin: " + countSkinData); skinData = Encoding.UTF8.GetString(reader.ReadBytes(countSkinData)); Log.Debug("Decompressed skinData" + skinData); } catch (Exception e) { Log.Error("Parsing login", e); return; } } } try { { if (Log.IsDebugEnabled) { Log.Debug("Input SKIN string: " + skinData); } IDictionary <string, dynamic> headers = JWT.Headers(skinData); dynamic payload = JObject.Parse(JWT.Payload(skinData)); if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Header: {string.Join(";", headers)}"); } if (Log.IsDebugEnabled) { Log.Debug($"Skin JWT Payload:\n{payload.ToString()}"); } // Skin JWT Payload: //{ // "ADRole": 2, // "ClientRandomId": 1423700530444426768, // "CurrentInputMode": 1, // "DefaultInputMode": 1, // "DeviceModel": "ASUSTeK COMPUTER INC. N550JK", // "DeviceOS": 7, // "GameVersion": "1.1.0", // "GuiScale": 0, // "LanguageCode": "en_US", // "ServerAddress": "192.168.0.3:19132", // "SkinData": "SnNH/1+KUf97n2T/AAAAAAAAAAAAAAAAAAAAAAAAAACWlY//q6ur/5aVj/+WlY//q6ur/5aVj/+WlY//q6ur/1JSUv9zbmr/c25q/1JSUv9zbmr/UlJS/3Nuav9zbmr/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBfQ/+WlY//q6ur/7+/v/8AAAAAAAAAAAAAAAAAAAAAQF9D/0pzR/9filH/SnNH/0BfQ/9Kc0f/SnNH/0BfQ/9zbmr/c25q/3Nuav9SUlL/c25q/1JSUv9zbmr/c25q/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7Sz7/c25q/1QxKP9wTTr/jGVJ/wAAAAAAAAAAAAAAAEpzR/9Kc0f/X4pR/1+KUf9Kc0f/SnNH/1+KUf9Kc0f/UlJS/1JSUv9SUlL/UlJS/1JSUv9SUlL/UlJS/3Nuav8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJSUv87IBz/AAAAAAAAAAAAAAAAAAAAAAAAAABfilH/X4pR/1+KUf9filH/X4pR/1+KUf9Kc0f/X4pR/ztLPv87Sz7/O0s+/ztLPv87Sz7/O0s+/ztLPv87Sz7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIRMT/wAAAAAAAAAAAAAAAAAAAAAAAAAAX4pR/1+KUf9filH/e59k/1+KUf9filH/SnNH/1+KUf87Sz7/O0s+/ztLPv87Sz7/O0s+/ztLPv87Sz7/O0s+/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpzR/9Kc0f/X4pR/1+KUf9filH/SnNH/0BfQ/9Kc0f/O0s+/ztLPv87Sz7/O0s+/ztLPv87Sz7/O0s+/ztLPv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKc0f/QF9D/0pzR/9filH/X4pR/0pzR/9AX0P/SnNH/0BfQ/87Sz7/QF9D/0BfQ/9AX0P/QF9D/ztLPv9AX0P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQF9D/0pzR/9filH/X4pR/1+KUf9filH/SnNH/0BfQ/9AX0P/QF9D/0pzR/9Kc0f/SnNH/0pzR/9AX0P/QF9D/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACrq6v/QF9D/0pzR/9filH/X4pR/0pzR/9Kc0f/QF9D/0BfQ/9Kc0f/X4pR/1+KUf9filH/X4pR/0pzR/9AX0P/QF9D/0pzR/9Kc0f/X4pR/1+KUf9Kc0f/QF9D/5aVj/+rq6v/lpWP/5aVj/+rq6v/lpWP/5aVj/+rq6v/lpWP/1+KUf9filH/X4pR/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF+KUf9filH/X4pR/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq6ur/6urq/9filH/e59k/1+KUf9filH/SnNH/0pzR/9Kc0f/SnNH/0pzR/9filH/X4pR/0pzR/9Kc0f/SnNH/0pzR/9Kc0f/X4pR/1+KUf97n2T/X4pR/6urq/+WlY//q6ur/6urq/+WlY//lpWP/6urq/+WlY//q6ur/5aVj/9filH/QF9D/0pzR/9filH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF+KUf9Kc0f/QF9D/1+KUf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJaVj/+rq6v/X4pR/1+KUf9filH/SnNH/0BfQ/9AX0P/QF9D/0BfQ/9Kc0f/SnNH/0pzR/9Kc0f/QF9D/0BfQ/9AX0P/QF9D/0pzR/9filH/X4pR/1+KUf+rq6v/lpWP/5aVj/+rq6v/q6ur/5aVj/+WlY//q6ur/6urq/+rq6v/AAAAAEBfQ/9Kc0f/QF9D/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAX0P/SnNH/0BfQ/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzbmr/q6ur/0pzR/9filH/SnNH/0pzR/9AX0P/SnNH/0BfQ//Z2dD/AAAA/1+KUf9AX0P/AAAA/9nZ0P9AX0P/SnNH/0BfQ/9Kc0f/SnNH/1+KUf9Kc0f/q6ur/6urq/9zbmr/q6ur/6urq/+rq6v/lpWP/6urq/+WlY//q6ur/wAAAABKc0f/X4pR/0pzR/9AX0P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAX0P/SnNH/1+KUf9Kc0f/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc25q/5aVj/9AX0P/X4pR/1+KUf9Kc0f/QF9D/1+KUf9AX0P/X4pR/1+KUf9Kc0f/QF9D/1+KUf9filH/QF9D/1+KUf9AX0P/SnNH/1+KUf9filH/QF9D/5aVj/+rq6v/c25q/5aVj/+WlY//q6ur/3Nuav+rq6v/lpWP/5aVj/8AAAAAAAAAAEpzR/9AX0P/QF9D/0pzR/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKc0f/QF9D/0BfQ/9Kc0f/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJSUv+rq6v/lpWP/1+KUf9Kc0f/X4pR/0pzR/9filH/X4pR/1+KUf9Kc0f/SnNH/0pzR/9Kc0f/X4pR/1+KUf9filH/SnNH/1+KUf9Kc0f/X4pR/6urq/+WlY//q6ur/1JSUv+WlY//c25q/6urq/9zbmr/lpWP/6urq/9zbmr/AAAAAAAAAAAAAAAASnNH/0BfQ/9AX0P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQF9D/0BfQ/9Kc0f/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSUlL/lpWP/3Nuav+WlY//QF9D/0pzR/9Kc0f/SnNH/0pzR/9Kc0f/SnNH/wAAAP8AAAD/SnNH/0pzR/9Kc0f/SnNH/0pzR/9Kc0f/QF9D/5aVj/+rq6v/c25q/5aVj/9SUlL/c25q/3Nuav+WlY//UlJS/5aVj/+rq6v/c25q/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUlJS/5aVj/9SUlL/lpWP/1JSUv87Sz7/QF9D/0BfQ/9AX0P/QF9D/0pzR/9Kc0f/SnNH/0pzR/9AX0P/QF9D/0BfQ/9AX0P/O0s+/5aVj/9zbmr/lpWP/3Nuav+WlY//UlJS/3Nuav9SUlL/c25q/1JSUv9zbmr/lpWP/1JSUv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0Y1T/UktM/1JLTP9SS0z/SnNH/0pzR/9AX0P/O0s+/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUktM/5eQcv90Y1T/UktM/1JLTP90Y1T/l5By/1JLTP9SS0z/UktM/1JLTP9SS0z/UktM/1JLTP9SS0z/UktM/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqma/5eQcv+XkHL/dGNU/0BfQ/9AX0P/O0s+/0pzR/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl5By/3RjVP9SS0z/UktM/0pzR/9AX0P/O0s+/ztLPv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJLTP8hExP/IRMT/yETE/8hExP/IRMT/yETE/9SS0z/UktM/1JLTP9SS0z/UktM/1JLTP9SS0z/UktM/1JLTP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqpmv+qqZr/qqma/5eQcv9Kc0f/v7+4/0BfQ/9AX0P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJeQcv90Y1T/UktM/zsgHP9Kc0f/QF9D/ztLPv87Sz7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSS0z/IRMT/yETE/8hExP/IRMT/yETE/8hExP/UktM/1JLTP9SS0z/UktM/yETE/8hExP/UktM/1JLTP9SS0z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqZr/qqma/6qpmv+XkHL/QF9D/0BfQ/87Sz7/QF9D/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0Y1T/UktM/1JLTP87IBz/QF9D/0pzR/9AX0P/O0s+/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUktM/yETE/8hExP/IRMT/yETE/8hExP/IRMT/1JLTP9SS0z/UktM/1JLTP87IBz/IRMT/1JLTP9SS0z/UktM/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqma/5eQcv+XkHL/dGNU/0pzR/+/v7j/QF9D/0pzR/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXkHL/c2Rk/3NkZP+XkHL/l5By/3RjVP9SS0z/IRMT/yETE/8hExP/UktM/1JLTP9SS0z/UktM/3RjVP+XkHL/dGNU/1JLTP9SS0z/UktM/1JLTP8hExP/IRMT/zsgHP87IBz/IRMT/yETE/9SS0z/UktM/1JLTP9SS0z/dGNU/3RjVP+qqZr/l5By/3RjVP90Y1T/l5By/6qpmv90Y1T/qqma/7+/uP+/v7j/qqma/6qpmv+XkHL/dGNU/1JLTP9SS0z/UktM/1JLTP9SS0z/UktM/3RjVP+XkHL/qqma/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl5By/3NkZP9zZGT/l5By/6qpmv+XkHL/dGNU/yETE/8hExP/IRMT/1JLTP9SS0z/UktM/3RjVP+XkHL/qqma/5eQcv90Y1T/UktM/1JLTP90Y1T/OyAc/1QxKP9UMSj/VDEo/1QxKP87IBz/dGNU/1JLTP9SS0z/dGNU/5eQcv+qqZr/v7+4/6qpmv+XkHL/l5By/6qpmv+/v7j/qqma/7+/uP+/v7j/v7+4/7+/uP+/v7j/qqma/5eQcv9SS0z/UktM/1JLTP9SS0z/UktM/1JLTP+XkHL/qqma/7+/uP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqpmv9zZGT/c2Rk/6qpmv+qqZr/l5By/3RjVP87IBz/IRMT/yETE/9SS0z/UktM/1JLTP+XkHL/qqma/6qpmv+XkHL/dGNU/1JLTP90Y1T/l5By/1QxKP9wTTr/cE06/3BNOv9wTTr/OyAc/5eQcv90Y1T/UktM/3RjVP+XkHL/qqma/6qpmv90Y1T/qqma/6qpmv90Y1T/qqma/6qpmv+qqZr/v7+4/7+/uP+qqZr/qqma/6qpmv+XkHL/dGNU/1JLTP9SS0z/UktM/1JLTP90Y1T/l5By/6qpmv+qqZr/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqZr/c2Rk/7+/uP+qqZr/qqma/6qpmv+XkHL/OyAc/yETE/8hExP/UktM/1JLTP90Y1T/l5By/6qpmv+qqZr/dGNU/1JLTP90Y1T/l5By/6qpmv87IBz/cE06/4xlSf9wTTr/VDEo/zsgHP+qqZr/l5By/3RjVP9SS0z/dGNU/5eQcv+qqZr/dGNU/5eQcv+XkHL/dGNU/6qpmv+XkHL/l5By/6qpmv+qqZr/l5By/5eQcv+XkHL/l5By/3RjVP9SS0z/UktM/1JLTP9SS0z/dGNU/5eQcv+XkHL/l5By/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv7+4/7+/uP+/v7j/v7+4/6qpmv+XkHL/l5By/zsgHP8hExP/IRMT/1JLTP9SS0z/dGNU/5eQcv+XkHL/qqma/1JLTP9SS0z/UktM/3RjVP+XkHL/OyAc/1QxKP9wTTr/jGVJ/3BNOv+qqZr/l5By/3RjVP9SS0z/UktM/1JLTP9SS0z/dGNU/5eQcv+qqZr/qqma/5eQcv90Y1T/UktM/3RjVP+XkHL/l5By/3RjVP90Y1T/UktM/1JLTP9SS0z/UktM/1JLTP9SS0z/UktM/1JLTP9SS0z/UktM/3RjVP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqpmv+/v7j/v7+4/6qpmv+XkHL/dGNU/1JLTP8hExP/IRMT/yETE/9SS0z/UktM/1JLTP9SS0z/dGNU/5eQcv90Y1T/UktM/1JLTP9SS0z/dGNU/6qpmv9UMSj/cE06/3BNOv9UMSj/qqma/3RjVP9SS0z/UktM/1JLTP90Y1T/l5By/6qpmv+qqZr/v7+4/7+/uP+qqZr/qqma/5eQcv+XkHL/qqma/6qpmv+XkHL/l5By/5eQcv+XkHL/dGNU/1JLTP9SS0z/UktM/1JLTP90Y1T/l5By/5eQcv+XkHL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqZr/qqma/6qpmv+qqZr/qqma/5eQcv+XkHL/OyAc/yETE/8hExP/UktM/1JLTP90Y1T/l5By/5eQcv+qqZr/l5By/3RjVP9SS0z/dGNU/5eQcv+qqZr/VDEo/3BNOv9wTTr/OyAc/6qpmv+XkHL/dGNU/1JLTP90Y1T/l5By/6qpmv+/v7j/v7+4/7+/uP+/v7j/v7+4/7+/uP+qqZr/qqma/7+/uP+/v7j/qqma/6qpmv+qqZr/l5By/3RjVP9SS0z/UktM/1JLTP9SS0z/dGNU/5eQcv+qqZr/qqma/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl5By/6qpmv+qqZr/l5By/6qpmv+qqZr/l5By/zsgHP8hExP/IRMT/1JLTP9SS0z/dGNU/5eQcv+qqZr/qqma/3RjVP90Y1T/UktM/3RjVP+XkHL/qqma/zsgHP9wTTr/VDEo/zsgHP+qqZr/l5By/3RjVP9SS0z/UktM/3RjVP90Y1T/l5By/6qpmv+/v7j/v7+4/6qpmv+XkHL/dGNU/7+/uP+/v7j/v7+4/7+/uP+/v7j/qqma/5eQcv9SS0z/UktM/1JLTP9SS0z/UktM/1JLTP+XkHL/qqma/7+/uP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJeQcv+XkHL/l5By/5eQcv+XkHL/dGNU/1QxKP87IBz/IRMT/yETE/9SS0z/UktM/1JLTP9SS0z/dGNU/5eQcv90Y1T/dGNU/1JLTP90Y1T/dGNU/6qpmv87IBz/VDEo/3BNOv+qqZr/qqma/3RjVP9SS0z/UktM/1JLTP9SS0z/dGNU/5eQcv+qqZr/v7+4/7+/uP+qqZr/l5By/3RjVP+qqZr/v7+4/7+/uP+qqZr/qqma/5eQcv90Y1T/UktM/1JLTP9SS0z/UktM/1JLTP9SS0z/dGNU/5eQcv+qqZr/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKc0f/X4pR/1+KUf9Kc0f/SnNH/0BfQ/9AX0P/O0s+/ztLPv87Sz7/O0s+/ztLPv87Sz7/QF9D/0pzR/9Kc0f/dGNU/3RjVP9SS0z/dGNU/3RjVP+XkHL/qqma/3BNOv9UMSj/qqma/5eQcv90Y1T/UktM/1JLTP9SS0z/UktM/1JLTP+XkHL/qqma/6qpmv+qqZr/qqma/5eQcv9SS0z/X4pR/1+KUf9filH/X4pR/1+KUf9Kc0f/SnNH/0BfQ/87Sz7/O0s+/ztLPv87Sz7/QF9D/0pzR/9Kc0f/X4pR/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX4pR/1+KUf97n2T/X4pR/1+KUf9filH/SnNH/ztLPv87Sz7/O0s+/ztLPv87Sz7/O0s+/0pzR/9filH/X4pR/3RjVP90Y1T/UktM/3RjVP9SS0z/l5By/5eQcv9wTTr/OyAc/5eQcv+XkHL/UktM/1JLTP9SS0z/UktM/1JLTP9SS0z/dGNU/5eQcv+XkHL/l5By/5eQcv90Y1T/UktM/0pzR/9filH/SnNH/1+KUf9Kc0f/QF9D/ztLPv9Kc0f/QF9D/ztLPv87Sz7/QF9D/0pzR/87Sz7/QF9D/0pzR/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF+KUf9filH/X4pR/0pzR/+/v7j/QF9D/7+/uP87Sz7/O0s+/ztLPv87Sz7/O0s+/ztLPv9AX0P/QF9D/0pzR/90Y1T/dGNU/1JLTP90Y1T/UktM/1JLTP90Y1T/VDEo/zsgHP90Y1T/UktM/1JLTP9SS0z/UktM/1JLTP9SS0z/UktM/1JLTP9SS0z/dGNU/3RjVP9SS0z/UktM/1JLTP9AX0P/SnNH/0BfQ/9Kc0f/SnNH/0pzR/9AX0P/SnNH/0BfQ/87Sz7/O0s+/0BfQ/9Kc0f/QF9D/0pzR/9Kc0f/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // "SkinId": "Standard_Custom", // "TenantId": "", // "UIProfile": 0 //} try { _playerInfo.ADRole = payload.ADRole; _playerInfo.ClientId = payload.ClientRandomId; _playerInfo.CurrentInputMode = payload.CurrentInputMode; _playerInfo.DefaultInputMode = payload.DefaultInputMode; _playerInfo.DeviceModel = payload.DeviceModel; _playerInfo.DeviceOS = payload.DeviceOS; _playerInfo.GameVersion = payload.GameVersion; _playerInfo.GuiScale = payload.GuiScale; _playerInfo.LanguageCode = payload.LanguageCode; _playerInfo.ServerAddress = payload.ServerAddress; _playerInfo.UIProfile = payload.UIProfile; _playerInfo.Skin = new Skin() { SkinType = payload.SkinId, Texture = Convert.FromBase64String((string)payload.SkinData), }; } catch (Exception e) { Log.Error("Skin info", e); } } { if (Log.IsDebugEnabled) { Log.Debug("Input JSON string: " + certificateChain); } dynamic json = JObject.Parse(certificateChain); if (Log.IsDebugEnabled) { Log.Debug($"JSON:\n{json}"); } string validationKey = null; foreach (dynamic o in json.chain) { IDictionary <string, dynamic> headers = JWT.Headers(o.ToString()); dynamic jsonPayload = JObject.Parse(JWT.Payload(o.ToString())); if (Log.IsDebugEnabled) { Log.Debug("Raw chain element:\n" + o.ToString()); Log.Debug($"JWT Header: {string.Join(";", headers)}"); Log.Debug($"JWT Payload:\n{jsonPayload}"); } if (jsonPayload["extraData"] == null) { continue; } _playerInfo.Username = jsonPayload["extraData"]["displayName"]; _session.Username = _playerInfo.Username; string identity = jsonPayload["extraData"]["identity"]; _playerInfo.ClientUuid = new UUID(new Guid(identity).ToByteArray()); _session.CryptoContext = new CryptoContext { UseEncryption = false }; if (jsonPayload["extraData"]["XUID"] != null) { _playerInfo.CertificateData = new CertificateData { ExtraData = new ExtraData { Xuid = jsonPayload["extraData"]["XUID"] } }; } else { _playerInfo.CertificateData = new CertificateData { ExtraData = new ExtraData { Xuid = "" } }; } } } if (!_session.CryptoContext.UseEncryption) { _session.MessageHandler.HandleMcpeClientToServerHandshake(null); } } catch (Exception e) { Log.Error("Decrypt", e); } }
public static bool Verify(string token, out string tokenOutput) { Dictionary <string, string> headers = JWT.Headers <Dictionary <string, string> >(token); string kid = headers["kid"]; if (cryptoServiceProviders.TryGetValue(kid, out var key)) { string unparsedPayload; try { unparsedPayload = JWT.Decode(token, key, JwsAlgorithm.RS256); } catch (InvalidAlgorithmException) { tokenOutput = "INVALID ALGORITHM"; return(false); } catch (IntegrityException) { tokenOutput = "INVALID TOKEN"; return(false); } var payload = JsonConvert.DeserializeObject <TokenPayload>(unparsedPayload); var now = (int)ToUnixTime(DateTime.Now); //Must be in the future if (payload.exp < now) { tokenOutput = string.Format("TOKEN_EXPIRED {0} < {1}", payload.exp, now); return(false); } //Must be in the past if (payload.auth_time > now) { tokenOutput = string.Format("INVALID_AUTHENTICATION_TIME {0} > {1}", payload.auth_time, now); return(false); } //Must be in the past if (payload.iat > now) { tokenOutput = string.Format("INVALID_ISSUE-AT-TIME {0} > {1}", payload.iat, now); return(false); } //Must correspond to projectId if (payload.aud != ProjectId) { tokenOutput = string.Format("INVALID_AUDIENCE {0} != {1}", payload.aud, ProjectId); return(false); } if (payload.iss != Issuer) { tokenOutput = string.Format("INVALID_ISSUER {0} != {1}", payload.iss, Issuer); return(false); } tokenOutput = payload.sub; return(true); } tokenOutput = "INVALID TOKEN KID"; return(false); }
/// <summary> /// 解析jwe头部信息 /// </summary> /// <param name="token"></param> /// <returns></returns> public static JweHeader Headers(string token) { return(JWT.Headers <JweHeader>(token)); }