Exemple #1
0
        public bool InitaliseSymmettricKey(ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] signedPublicKey, RSAParameters otherPartyRSAPublicKey)
        {
            RSACryptoServiceProvider csp = new RSACryptoServiceProvider();

            csp.ImportParameters(otherPartyRSAPublicKey);
            if (csp.VerifyData(otherPartyPublicKey.ToByteArray(), SHA2, signedPublicKey))
            {
                symmettricKey = ECDH.DeriveKeyMaterial(otherPartyPublicKey);
                return(true);
            }
            else
            {
                return(false);
            }
        }
        public static void SymmetricDerivation_HashAppend(int keySize)
        {
            byte[] suffix = new byte[10];

            using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create(keySize))
                using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create(keySize))
                    using (ECDiffieHellmanPublicKey alicePublic = alice.PublicKey)
                        using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
                        {
                            byte[] aliceDerived = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA512, null, suffix);
                            byte[] bobDerived   = bob.DeriveKeyFromHash(alicePublic, HashAlgorithmName.SHA512, null, suffix);

                            Assert.Equal(aliceDerived, bobDerived);
                        }
        }
        public static void HashDerivationVariesOnPublicKey()
        {
            using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create())
                using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create())
                    using (ECDiffieHellmanPublicKey alicePublic = alice.PublicKey)
                        using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
                        {
                            byte[] aliceDerived     = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA512);
                            byte[] aliceSelfDerived = alice.DeriveKeyFromHash(alicePublic, HashAlgorithmName.SHA512);

                            // Alice and Alice is HASH(aaG) != HASH(abG)
                            // (Except for the fantastically small chance that Alice == Bob)
                            Assert.NotEqual(aliceDerived, aliceSelfDerived);
                        }
        }
Exemple #4
0
        public override void ProcessClientKeys(ProtocolVersion version, ProtocolVersion clientVersion, CertificatePrivateKey privateKey, byte[] data)
        {
            if (data[0] != data.Length - 1)
            {
                throw new Exception("Incorrect ECPoint length");
            }

            // Exctract the ECPoint
            byte[] ecPoint = new byte[data.Length - 1];
            Buffer.BlockCopy(data, 1, ecPoint, 0, ecPoint.Length);

            // Create key blob and public key
            byte[] keyBlob = Point2Blob(ecPoint);
            _publicKey = ECDiffieHellmanCngPublicKey.FromByteArray(keyBlob, CngKeyBlobFormat.EccPublicBlob);
        }
        public static void TlsPrfVariesOnLabel()
        {
            byte[] aliceLabel = s_fourByteLabel;
            byte[] bobLabel   = new byte[5];

            using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create())
                using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create())
                    using (ECDiffieHellmanPublicKey alicePublic = alice.PublicKey)
                        using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
                        {
                            byte[] aliceDerived = alice.DeriveKeyTls(bobPublic, aliceLabel, s_emptySeed);
                            byte[] bobDerived   = bob.DeriveKeyTls(alicePublic, bobLabel, s_emptySeed);

                            Assert.NotEqual(aliceDerived, bobDerived);
                        }
        }
Exemple #6
0
        public void VerifyDuplicateKey_ValidHandle()
        {
            using (var first = new ECDiffieHellmanOpenSsl())
                using (SafeEvpPKeyHandle firstHandle = first.DuplicateKeyHandle())
                    using (ECDiffieHellman second = new ECDiffieHellmanOpenSsl(firstHandle))
                        using (ECDiffieHellmanPublicKey firstPublic = first.PublicKey)
                            using (ECDiffieHellmanPublicKey secondPublic = second.PublicKey)
                            {
                                byte[] firstSecond = first.DeriveKeyFromHash(secondPublic, HashAlgorithmName.SHA256);
                                byte[] secondFirst = second.DeriveKeyFromHash(firstPublic, HashAlgorithmName.SHA256);
                                byte[] firstFirst  = first.DeriveKeyFromHash(firstPublic, HashAlgorithmName.SHA256);

                                Assert.Equal(firstSecond, secondFirst);
                                Assert.Equal(firstFirst, firstSecond);
                            }
        }
        public static void Main(string[] args)
        {
            using (ECDiffieHellmanOpenSsl alice = new ECDiffieHellmanOpenSsl())
            {
                AlicePublicKey = alice.PublicKey;

                Bob bob = new Bob();

                byte[] aliceKey         = alice.DeriveKeyMaterial(bob.BobPublicKey);
                byte[] encryptedMessage = null;
                byte[] iv = null;

                Send(aliceKey, "Secret message", out encryptedMessage, out iv);
                bob.Receive(encryptedMessage, iv);
            }
        }
        public static void HmacDerivationVariesOnKey()
        {
            byte[] hmacKeyAlice = { 0, 1, 2, 3, 4, 5 };
            byte[] hmacKeyBob   = { 10, 1, 2, 3, 4, 5 };

            using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create())
                using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create())
                    using (ECDiffieHellmanPublicKey alicePublic = alice.PublicKey)
                        using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
                        {
                            byte[] aliceDerived = alice.DeriveKeyFromHmac(bobPublic, HashAlgorithmName.SHA512, hmacKeyAlice);
                            byte[] bobDerived   = bob.DeriveKeyFromHmac(alicePublic, HashAlgorithmName.SHA512, hmacKeyBob);

                            Assert.NotEqual(aliceDerived, bobDerived);
                        }
        }
Exemple #9
0
        /// <summary>
        /// A client requested login.
        /// </summary>
        /// <param name="Client">NetworkClient instance.</param>
        /// <param name="Packet">ProcessedPacket instance.</param>
        public static void InitialClientConnect(NetworkClient Client, ProcessedPacket Packet)
        {
            Console.WriteLine("Server receives data - test 1");

            //AES is used to encrypt all further communication between client and server.
            AesCryptoServiceProvider AesCrypto = new AesCryptoServiceProvider();

            AesCrypto.GenerateKey();
            AesCrypto.GenerateIV();
            AES AesEncryptor = new AES(AesCrypto.Key, AesCrypto.IV);

            SessionKey = AesCrypto.Key;
            IV         = AesCrypto.IV;

            Guid Nonce = new Guid(Packet.ReadPascalString());

            //Username would normally be used to lookup client's public key in DB (only time such a use is valid).
            string Username = Packet.ReadPascalString();
            ECDiffieHellmanPublicKey ClientPub = StaticStaticDiffieHellman.ImportKey("ClientPublic.dat");

            PacketStream EncryptedPacket = new PacketStream(0x02, 0);

            EncryptedPacket.WriteHeader();

            MemoryStream StreamToEncrypt = new MemoryStream();
            BinaryWriter Writer          = new BinaryWriter(StreamToEncrypt);

            Writer.Write((byte)ChallengeResponse.ToByteArray().Length);
            Writer.Write(ChallengeResponse.ToByteArray(), 0, ChallengeResponse.ToByteArray().Length);
            Writer.Write((byte)SessionKey.Length);
            Writer.Write(SessionKey, 0, SessionKey.Length);
            Writer.Write((byte)IV.Length);
            Writer.Write(IV, 0, IV.Length);
            Writer.Flush();

            byte[] EncryptedData = StaticStaticDiffieHellman.Encrypt(ServerKey, ClientPub, Nonce.ToByteArray(),
                                                                     StreamToEncrypt.ToArray());

            EncryptedPacket.WriteUInt16((ushort)(PacketHeaders.UNENCRYPTED + 1 + EncryptedData.Length));

            EncryptedPacket.WriteByte((byte)EncryptedData.Length);
            EncryptedPacket.Write(EncryptedData, 0, EncryptedData.Length);

            Client.Send(EncryptedPacket.ToArray());

            Console.WriteLine("Test 1: passed!");
        }
Exemple #10
0
        public override byte[] DeriveKeyFromHmac(ECDiffieHellmanPublicKey otherPartyPublicKey, HashAlgorithmName hashAlgorithm, byte[]?hmacKey, byte[]?secretPrepend, byte[]?secretAppend)
        {
            if (this.publicKey == null)
            {
                CreateKeys();
            }
            if (this.privateKey == null)
            {
                throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
            }
            if (string.IsNullOrEmpty(hashAlgorithm.Name))
            {
                throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm));
            }

            return(ECDiffieHellmanDerivation.DeriveKeyFromHmac(otherPartyPublicKey, hashAlgorithm, hmacKey, secretPrepend, secretAppend, DeriveSecretAgreement));
        }
        public static void HashDerivationVariesOnPrepend()
        {
            byte[] alicePrefix = new byte[10];
            byte[] bobPrefix   = new byte[alicePrefix.Length];
            bobPrefix[0] = 0xFF;

            using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create())
                using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create())
                    using (ECDiffieHellmanPublicKey alicePublic = alice.PublicKey)
                        using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
                        {
                            byte[] aliceDerived = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA512, alicePrefix, null);
                            byte[] bobDerived   = alice.DeriveKeyFromHash(alicePublic, HashAlgorithmName.SHA512, bobPrefix, null);

                            Assert.NotEqual(aliceDerived, bobDerived);
                        }
        }
        public static void TlsPrfVariesOnSeed()
        {
            byte[] aliceSeed = s_emptySeed;
            byte[] bobSeed   = new byte[64];
            bobSeed[0] = 0x81;

            using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create())
                using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create())
                    using (ECDiffieHellmanPublicKey alicePublic = alice.PublicKey)
                        using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
                        {
                            byte[] aliceDerived = alice.DeriveKeyTls(bobPublic, s_fourByteLabel, aliceSeed);
                            byte[] bobDerived   = bob.DeriveKeyTls(alicePublic, s_fourByteLabel, bobSeed);

                            Assert.NotEqual(aliceDerived, bobDerived);
                        }
        }
        public static void HmacDerivationVariesOnAppend()
        {
            byte[] aliceSuffix = new byte[10];
            byte[] bobSuffix   = new byte[aliceSuffix.Length];
            bobSuffix[0] = 0xFF;

            using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create())
                using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create())
                    using (ECDiffieHellmanPublicKey alicePublic = alice.PublicKey)
                        using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
                        {
                            byte[] aliceDerived = alice.DeriveKeyFromHmac(bobPublic, HashAlgorithmName.SHA512, s_sampleHmacKey, null, aliceSuffix);
                            byte[] bobDerived   = bob.DeriveKeyFromHmac(alicePublic, HashAlgorithmName.SHA512, s_sampleHmacKey, null, bobSuffix);

                            Assert.NotEqual(aliceDerived, bobDerived);
                        }
        }
Exemple #14
0
        public void TestGenerateSecret()
        {
            string keyString = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEDEKneqEvcqUqqFMM1HM1A4zWjJC+I8Y+aKzG5dl+6wNOHHQ4NmG2PEXRJYhujyodFH+wO0dEr4GM1WoaWog8xsYQ6mQJAC0eVpBM96spUB1eMN56+BwlJ4H3Qx4TAvAs";

            ECDiffieHellmanPublicKey clientKey = CryptoUtils.FromDerEncoded(keyString.DecodeBase64Url());

            ECDiffieHellmanCng ecKey = new ECDiffieHellmanCng(384);

            ecKey.HashAlgorithm         = CngAlgorithm.Sha256;
            ecKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
            ecKey.SecretPrepend         = new byte[128];     // Server token
            //ecKey.SecretPrepend = new byte[0]; // Server token

            Console.WriteLine(ecKey.HashAlgorithm);
            Console.WriteLine(ecKey.KeyExchangeAlgorithm);

            byte[] secret = ecKey.DeriveKeyMaterial(clientKey);
        }
Exemple #15
0
        // Serializes an ECDiffieHellmanPublicKey to an ECC public key blob
        // "ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export
        // format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific"
        // from https://github.com/dotnet/runtime/issues/27276
        // => ECDiffieHellmanPublicKey.ToByteArray() is not supported in Unix
        internal static byte[] ECDHPublicKeyToECCKeyBlob(ECDiffieHellmanPublicKey publicKey)
        {
            byte[] keyBlob = new byte[ECCPublicKeyBlob.Size];

            // Set magic number
            Array.Copy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4);
            // Set key size
            keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize;

            ECPoint ecPoint = publicKey.ExportParameters().Q;

            Debug.Assert(ecPoint.X.Length == ECCPublicKeyBlob.KeySize && ecPoint.Y.Length == ECCPublicKeyBlob.KeySize,
                         $"ECDH public key was not the expected length. Actual (X): {ecPoint.X.Length}. Actual (Y): {ecPoint.Y.Length} Expected: {ECCPublicKeyBlob.Size}");
            // Copy x and y coordinates to key blob
            Array.Copy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize);
            Array.Copy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize);
            return(keyBlob);
        }
Exemple #16
0
        public static void Equivalence_TlsPrf(int labelSize)
        {
            using (ECDiffieHellmanCng ecdh = NewDefaultECDHCng())
                using (ECDiffieHellmanPublicKey publicKey = ecdh.PublicKey)
                {
                    byte[] label = new byte[labelSize];
                    byte[] seed  = new byte[64];

                    byte[] newWay = ecdh.DeriveKeyTls(publicKey, label, seed);

                    ecdh.Label = label;
                    ecdh.Seed  = seed;
                    ecdh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Tls;

                    byte[] oldWay = ecdh.DeriveKeyMaterial(publicKey);

                    Assert.Equal(newWay, oldWay);
                }
        }
Exemple #17
0
        public void TestKeyMangling()
        {
            string keyString = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEDEKneqEvcqUqqFMM1HM1A4zWjJC+I8Y+aKzG5dl+6wNOHHQ4NmG2PEXRJYhujyodFH+wO0dEr4GM1WoaWog8xsYQ6mQJAC0eVpBM96spUB1eMN56+BwlJ4H3Qx4TAvAs";

            byte[] keyBytes = Base64Url.Decode(keyString);

            ECDiffieHellmanPublicKey clientKey = CryptoUtils.CreateEcDiffieHellmanPublicKey(keyString);

            byte[] outBytes = clientKey.GetDerEncoded();

            string outString = Convert.ToBase64String(outBytes);

            Console.WriteLine(Package.HexDump(keyBytes));
            Console.WriteLine(Package.HexDump(outBytes));

            Assert.AreEqual(keyBytes, outBytes);

            Assert.AreEqual(keyString, outString);
        }
        public static void HashDerivation_KnownResults(
            HashAlgorithmName hashAlgorithm,
            string prependBytes,
            string appendBytes,
            string answerBytes)
        {
            byte[] prepend = prependBytes?.HexToByteArray();
            byte[] append  = appendBytes?.HexToByteArray();
            byte[] answer  = answerBytes.HexToByteArray();
            byte[] output;

            using (ECDiffieHellman ecdh = OpenKnownKey())
                using (ECDiffieHellmanPublicKey publicKey = ecdh.PublicKey)
                {
                    output = ecdh.DeriveKeyFromHash(publicKey, hashAlgorithm, prepend, append);
                }

            Assert.Equal(answer, output);
        }
Exemple #19
0
        /// <summary>
        /// Initial response from server to client.
        /// </summary>
        /// <param name="Client">A NetworkClient instance.</param>
        /// <param name="Packet">A ProcessedPacket instance.</param>
        public static void HandleServerChallenge(NetworkClient Client, ProcessedPacket Packet)
        {
            Console.WriteLine("Client receives encrypted data - test 2");

            byte[] PacketBuf = new byte[Packet.ReadByte()];
            Packet.Read(PacketBuf, 0, (int)PacketBuf.Length);

            ECDiffieHellmanPublicKey ServerPub = StaticStaticDiffieHellman.ImportKey("ServerPublic.dat");

            MemoryStream DecryptedStream = new MemoryStream(StaticStaticDiffieHellman.Decrypt(ClientKey, ServerPub,
                                                                                              ClientNOnce.ToByteArray(), PacketBuf));
            BinaryReader Reader = new BinaryReader(DecryptedStream);

            Guid ChallengeResponse = new Guid(Reader.ReadBytes(Reader.ReadByte()));

            SessionKey = Reader.ReadBytes(Reader.ReadByte());
            IV         = Reader.ReadBytes(Reader.ReadByte());

            //Yay, we have key and IV, we can now start encryption with AES!
            AES AesEncryptor = new AES(SessionKey, IV);

            PacketStream EncryptedPacket = new PacketStream(0x03, 0);

            EncryptedPacket.WriteHeader();

            MemoryStream StreamToEncrypt = new MemoryStream();
            BinaryWriter Writer          = new BinaryWriter(StreamToEncrypt);

            Writer.Write((byte)ChallengeResponse.ToByteArray().Length);
            Writer.Write(ChallengeResponse.ToByteArray(), 0, ChallengeResponse.ToByteArray().Length);

            //Encrypt data using key and IV from server, hoping that it'll be decrypted correctly at the other end...
            byte[] EncryptedData = AesEncryptor.Encrypt(StreamToEncrypt.ToArray());

            EncryptedPacket.WriteUInt16((ushort)(PacketHeaders.UNENCRYPTED + EncryptedData.Length + 1));
            EncryptedPacket.WriteByte((byte)EncryptedData.Length);
            EncryptedPacket.Write(EncryptedData, 0, EncryptedData.Length);

            Client.Send(EncryptedPacket.ToArray());

            Console.WriteLine("Test 2: passed!");
        }
Exemple #20
0
        public static void Equivalence_Hash(HashAlgorithmName algorithm, bool prepend, bool append)
        {
            using (ECDiffieHellmanCng ecdh = NewDefaultECDHCng())
                using (ECDiffieHellmanPublicKey publicKey = ecdh.PublicKey)
                {
                    byte[] secretPrepend = prepend ? new byte[3] : null;
                    byte[] secretAppend  = append ? new byte[4] : null;

                    byte[] newWay = ecdh.DeriveKeyFromHash(publicKey, algorithm, secretPrepend, secretAppend);

                    ecdh.HashAlgorithm         = new CngAlgorithm(algorithm.Name);
                    ecdh.SecretPrepend         = secretPrepend;
                    ecdh.SecretAppend          = secretAppend;
                    ecdh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;

                    byte[] oldWay = ecdh.DeriveKeyMaterial(publicKey);

                    Assert.Equal(newWay, oldWay);
                }
        }
Exemple #21
0
    private static Aes DeriveKeyAndIv(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce)
    {
        privateKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
        privateKey.HashAlgorithm         = CngAlgorithm.Sha256;
        privateKey.SecretAppend          = nonce;
        byte[] keyAndIv = privateKey.DeriveKeyMaterial(publicKey);
        byte[] key      = new byte[16];
        Array.Copy(keyAndIv, 0, key, 0, 16);
        byte[] iv = new byte[16];
        Array.Copy(keyAndIv, 16, iv, 0, 16);

        Aes aes = new AesManaged();

        aes.Key     = key;
        aes.IV      = iv;
        aes.Mode    = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        return(aes);
    }
Exemple #22
0
        public void VerifyDuplicateKey_RefCounts()
        {
            byte[]          derived;
            ECDiffieHellman second;

            using (var first = new ECDiffieHellmanOpenSsl())
                using (SafeEvpPKeyHandle firstHandle = first.DuplicateKeyHandle())
                    using (ECDiffieHellmanPublicKey firstPublic = first.PublicKey)
                    {
                        derived = first.DeriveKeyFromHmac(firstPublic, HashAlgorithmName.SHA384, null);
                        second  = new ECDiffieHellmanOpenSsl(firstHandle);
                    }

            // Now show that second still works, despite first and firstHandle being Disposed.
            using (second)
                using (ECDiffieHellmanPublicKey secondPublic = second.PublicKey)
                {
                    byte[] derived2 = second.DeriveKeyFromHmac(secondPublic, HashAlgorithmName.SHA384, null);
                    Assert.Equal(derived, derived2);
                }
        }
    public async Task ReadMessageAsync(byte[] iv, byte[] encryptedData, ECDiffieHellmanPublicKey otherPublicKey)
    {
        _logger.LogInformation("Bob receives encrypted data");
        byte[] symmKey = _algorithm.DeriveKeyMaterial(otherPublicKey);
        _logger.LogInformation($"Bob creates this symmetric key with " +
                               $"Alice public key information: {Convert.ToBase64String(symmKey)}");

        Aes aes = Aes.Create();

        aes.Key = symmKey;
        aes.IV  = iv;
        using ICryptoTransform decryptor = aes.CreateDecryptor();
        using MemoryStream ms            = new();
        using (CryptoStream cs = new(ms, decryptor, CryptoStreamMode.Write))
        {
            await cs.WriteAsync(encryptedData.AsMemory());
        } // close the cryptostream before using the memorystream
        byte[] rawData = ms.ToArray();
        _logger.LogInformation($"Bob decrypts message to: {Encoding.UTF8.GetString(rawData)}");
        aes.Clear();
    }
    static byte[] getDeriveKey(byte[] key1, ECDiffieHellman alice)
    {
        byte[] keyX = new byte[key1.Length / 2];
        byte[] keyY = new byte[keyX.Length];
        Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
        Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);
        ECParameters parameters = new ECParameters
        {
            Curve = ECCurve.NamedCurves.brainpoolP256r1,
            Q     =
            {
                X = keyX,
                Y = keyY,
            },
        };

        byte[] derivedKey;
        using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
            using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
            {
                return(derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256));
            }
    }
Exemple #25
0
        private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, System.Security.Cryptography.IncrementalHash?hash)
        {
            PublicKey otherPartyNSecPublicKey;

            if (otherPartyPublicKey is X25519PublicKey x25519PublicKey)
            {
                otherPartyNSecPublicKey = x25519PublicKey.publicKey;
            }
            else
            {
                var parameters = otherPartyPublicKey.ExportParameters();
                otherPartyNSecPublicKey = NSec.Cryptography.PublicKey.Import(KeyAgreementAlgorithm.X25519, parameters.Q.X, KeyBlobFormat.RawPublicKey);
            }

            Debug.Assert(this.privateKey != null);
            Debug.Assert(hash != null);

            using var sharedSecret = KeyAgreementAlgorithm.X25519.Agree(this.privateKey, otherPartyNSecPublicKey);

            // NSec doesn't offer a way to export the shared secret. Unfortunately it also doesn't provide
            // the correct key derivation function so we have to resort to using the private API.
            var memoryHandle = (SafeHandle?)typeof(SharedSecret).GetProperty("Handle", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetMethod?.Invoke(sharedSecret, null);

            Debug.Assert(memoryHandle != null);
            byte[] secretBytes = new byte[32];
            try
            {
                Marshal.Copy(memoryHandle.DangerousGetHandle(), secretBytes, 0, 32);
                hash.AppendData(secretBytes);
            }
            finally
            {
                CryptographicOperations.ZeroMemory(secretBytes);
            }

            return(null);
        }
Exemple #26
0
 public void SetReceiverPublicKey(ECDiffieHellmanPublicKey receiverPublicKey)
 {
     SenderPrivateKey = ecDiffieHellman.DeriveKeyMaterial(receiverPublicKey);
     ecDiffieHellman.Dispose();
 }
Exemple #27
0
    public static byte[] Decrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] encryptedData)
    {
        Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);

        return(aes.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length));
    }
Exemple #28
0
        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 abstract byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey);
        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);
            }
        }