} // End Function GenerateElGamalKeyPair

        public static Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair GenerateDHKeyPair(
            int length
            , Org.BouncyCastle.Security.SecureRandom secureRandom
            )
        {
            // Org.BouncyCastle.Math.BigInteger p = new BigInteger("123", 10);
            // Org.BouncyCastle.Math.BigInteger g = new BigInteger("456", 10);

            // Org.BouncyCastle.Crypto.Parameters.DHParameters dhParams =
            //     new Org.BouncyCastle.Crypto.Parameters.DHParameters(p, g);

            Org.BouncyCastle.Crypto.Generators.DHParametersGenerator pg =
                new Org.BouncyCastle.Crypto.Generators.DHParametersGenerator();
            pg.Init(length, 100, secureRandom);

            Org.BouncyCastle.Crypto.Parameters.DHParameters dhp = pg.GenerateParameters();

            Org.BouncyCastle.Crypto.Parameters.DHKeyGenerationParameters pars =
                new Org.BouncyCastle.Crypto.Parameters.DHKeyGenerationParameters(secureRandom, dhp);

            Org.BouncyCastle.Crypto.Generators.DHKeyPairGenerator keyGenerator =
                new Org.BouncyCastle.Crypto.Generators.DHKeyPairGenerator();
            keyGenerator.Init(pars);

            return(keyGenerator.GenerateKeyPair());
        } // End Function GenerateDHKeyPair
Example #2
0
        // https://stackoverflow.com/questions/33813108/bouncycastle-diffie-hellman
        public static Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair GenerateDiffieHellmanKeyPair(Org.BouncyCastle.Security.SecureRandom random
                                                                                                   , int keystrength
                                                                                                   )
        {
            const int DefaultPrimeProbability = 30;

            Org.BouncyCastle.Crypto.IAsymmetricCipherKeyPairGenerator keypairGen = new Org.BouncyCastle.Crypto.Generators.DHKeyPairGenerator();

            Org.BouncyCastle.Crypto.Generators.DHParametersGenerator pGen = new Org.BouncyCastle.Crypto.Generators.DHParametersGenerator();
            pGen.Init(keystrength, DefaultPrimeProbability, random);

            Org.BouncyCastle.Crypto.Parameters.DHParameters parameters = pGen.GenerateParameters();
            Org.BouncyCastle.Crypto.KeyGenerationParameters genParam   = new Org.BouncyCastle.Crypto.Parameters.DHKeyGenerationParameters(new Org.BouncyCastle.Security.SecureRandom(), parameters);
            keypairGen.Init(genParam);

            Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair kp = keypairGen.GenerateKeyPair();
            return(kp);
        }
Example #3
0
        bool HandShake(bool FP9HandShake)
        {
            // use the same seed everytime to have the same random numbers everytime (as rtmpdump)
            Random rand = new Random(0);

            // generate Diffie-Hellmann parameters
            Org.BouncyCastle.Crypto.Parameters.DHParameters dhParams = new Org.BouncyCastle.Crypto.Parameters.DHParameters(
                new Org.BouncyCastle.Math.BigInteger(1, DH_MODULUS_BYTES),
                Org.BouncyCastle.Math.BigInteger.ValueOf(2),
                new Org.BouncyCastle.Math.BigInteger(1, DH_Q));

            int offalg = 0;
            int dhposClient = 0;
            int digestPosClient = 0;
            bool encrypted = Link.protocol == Protocol.RTMPE || Link.protocol == Protocol.RTMPTE;

            byte[] clientsig = new byte[RTMP_SIG_SIZE + 1];
            byte[] serversig = new byte[RTMP_SIG_SIZE];
            byte[] clientResp;

            if (encrypted || Link.SWFSize > 0)
                FP9HandShake = true;
            else
                FP9HandShake = false;

            if (encrypted)
            {
                clientsig[0] = 0x06; // 0x08 is RTMPE as well
                offalg = 1;
            }
            else clientsig[0] = 0x03;

            int uptime = System.Environment.TickCount;
            byte[] uptime_bytes = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(uptime));
            Array.Copy(uptime_bytes, 0, clientsig, 1, uptime_bytes.Length);

            if (FP9HandShake)
            {
                /* set version to at least 9.0.115.0 */
                if (encrypted)
                {
                    clientsig[5] = 128;
                    clientsig[7] = 3;
                }
                else
                {
                    clientsig[5] = 10;
                    clientsig[7] = 45;
                }
                clientsig[6] = 0;
                clientsig[8] = 2;

                Logger.Log(string.Format("Client type: {0}", clientsig[0]));
            }
            else
            {
                clientsig[5] = 0; clientsig[6] = 0; clientsig[7] = 0; clientsig[8] = 0;
            }

            // generate random data
            for (int i = 9; i < RTMP_SIG_SIZE; i += 4) Array.Copy(BitConverter.GetBytes(rand.Next(ushort.MaxValue)), 0, clientsig, i, 4);

            byte[] keyIn = null;
            byte[] keyOut = null;

            // set handshake digest
            if (FP9HandShake)
            {
                if (encrypted)
                {
                    Org.BouncyCastle.Crypto.Parameters.DHKeyGenerationParameters keySpec = new Org.BouncyCastle.Crypto.Parameters.DHKeyGenerationParameters(new Org.BouncyCastle.Security.SecureRandom(), dhParams);
                    Org.BouncyCastle.Crypto.Generators.DHBasicKeyPairGenerator keyGen = new Org.BouncyCastle.Crypto.Generators.DHBasicKeyPairGenerator();
                    keyGen.Init(keySpec);
                    Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair pair = keyGen.GenerateKeyPair();
                    Org.BouncyCastle.Crypto.Agreement.DHBasicAgreement keyAgreement = new Org.BouncyCastle.Crypto.Agreement.DHBasicAgreement();
                    keyAgreement.Init(pair.Private);
                    Link.keyAgreement = keyAgreement;

                    byte[] publicKey = (pair.Public as Org.BouncyCastle.Crypto.Parameters.DHPublicKeyParameters).Y.ToByteArray();

                    byte[] temp = new byte[128];
              Logger.Log("public key length: " + publicKey.Length);
                    if (publicKey.Length < 128)
                    {
                        Array.Copy(publicKey, 0, temp, 128 - publicKey.Length, publicKey.Length);
                        publicKey = temp;
                        Logger.Log("padded public key length to 128 bytes with zeros");
                    }
                    else if (publicKey.Length > 128)
                    {
                        Array.Copy(publicKey, publicKey.Length - 128, temp, 0, 128);
                        publicKey = temp;
                        Logger.Log("truncated public key length to 128");
                    }

                    dhposClient = (int)GetDHOffset(offalg, clientsig, 1, RTMP_SIG_SIZE);
                    Logger.Log(string.Format("DH pubkey position: {0}", dhposClient));

                    Array.Copy(publicKey, 0, clientsig, 1 + dhposClient, 128);
                }

                digestPosClient = (int)GetDigestOffset(offalg, clientsig, 1, RTMP_SIG_SIZE); // reuse this value in verification
                Logger.Log(string.Format("Client digest offset: {0}", digestPosClient));

                CalculateDigest(digestPosClient, clientsig, 1, GenuineFPKey, 30, clientsig, 1 + digestPosClient);

                Logger.Log("Initial client digest: ");
                string digestAsHexString = "";
                for (int i = 1 + digestPosClient; i < 1 + digestPosClient + SHA256_DIGEST_LENGTH; i++) digestAsHexString += clientsig[i].ToString("X2") + " ";
                Logger.Log(digestAsHexString);
            }

            WriteN(clientsig, 0, RTMP_SIG_SIZE + 1);

            byte[] singleByteToReadBuffer = new byte[1];
            if (ReadN(singleByteToReadBuffer, 0, 1) != 1) return false;
            byte type = singleByteToReadBuffer[0]; // 0x03 or 0x06

            Logger.Log(string.Format("Type Answer   : {0}", type.ToString()));

            if (type != clientsig[0]) Logger.Log(string.Format("Type mismatch: client sent {0}, server answered {1}", clientsig[0], type));

            if (ReadN(serversig, 0, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) return false;

            // decode server response
            uint server_uptime = (uint)ReadInt32(serversig, 0);

            Logger.Log(string.Format("Server Uptime : {0}", server_uptime));
            Logger.Log(string.Format("FMS Version   : {0}.{1}.{2}.{3}", serversig[4], serversig[5], serversig[6], serversig[7]));

            if (FP9HandShake && type == 3 && serversig[4] == 0) FP9HandShake = false;

            if (FP9HandShake)
            {
                // we have to use this signature now to find the correct algorithms for getting the digest and DH positions
                int digestPosServer = (int)GetDigestOffset(offalg, serversig, 0, RTMP_SIG_SIZE);

                if (!VerifyDigest(digestPosServer, serversig, GenuineFMSKey, 36))
                {
                    Logger.Log("Trying different position for server digest!");
                    offalg ^= 1;
                    digestPosServer = (int)GetDigestOffset(offalg, serversig, 0, RTMP_SIG_SIZE);

                    if (!VerifyDigest(digestPosServer, serversig, GenuineFMSKey, 36))
                    {
                        Logger.Log("Couldn't verify the server digest");//,  continuing anyway, will probably fail!\n");
                        return false;
                    }
                }

                // generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake)
                if (Link.SWFHash != null)
                {
                    byte[] swfVerify = new byte[2] { 0x01, 0x01 };
                    Array.Copy(swfVerify, Link.SWFVerificationResponse, 2);
                    List<byte> data = new List<byte>();
                    EncodeInt32(data, Link.SWFSize);
                    EncodeInt32(data, Link.SWFSize);
                    Array.Copy(data.ToArray(), 0, Link.SWFVerificationResponse, 2, data.Count);
                    byte[] key = new byte[SHA256_DIGEST_LENGTH];
                    Array.Copy(serversig, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, key, 0, SHA256_DIGEST_LENGTH);
                    HMACsha256(Link.SWFHash, 0, SHA256_DIGEST_LENGTH, key, SHA256_DIGEST_LENGTH, Link.SWFVerificationResponse, 10);
                }

                // do Diffie-Hellmann Key exchange for encrypted RTMP
                if (encrypted)
                {
                    int dhposServer = (int)GetDHOffset(offalg, serversig, 0, RTMP_SIG_SIZE);
                    Logger.Log(string.Format("Server DH public key offset: {0}", dhposServer));

                    // compute secret key
                    byte[] serverKey = new byte[128];
                    Array.Copy(serversig, dhposServer, serverKey, 0, 128);

                    Org.BouncyCastle.Crypto.Parameters.DHPublicKeyParameters dhPubKey =
                        new Org.BouncyCastle.Crypto.Parameters.DHPublicKeyParameters(
                            new Org.BouncyCastle.Math.BigInteger(serverKey),
                            dhParams);

                    byte[] secretKey = Link.keyAgreement.CalculateAgreement(dhPubKey).ToByteArray();

                    byte[] temp = new byte[128];
                    if (secretKey.Length < 128)
                    {
                        Array.Copy(secretKey, 0, temp, 128 - secretKey.Length, secretKey.Length);
                        secretKey = temp;
                        Logger.Log("padded secret key length to 128 bytes with zeros");
                    }
                    else if (secretKey.Length > 128)
                    {
                        Array.Copy(secretKey, secretKey.Length - 128, temp, 0, 128);
                        secretKey = temp;
                        Logger.Log("truncated secret key length to 128");
                    }

                    Logger.Log("DH SecretKey:");
                    Logger.LogHex(secretKey, 0, secretKey.Length);

                    InitRC4Encryption(
                        secretKey,
                        serversig, dhposServer,
                        clientsig, 1 + dhposClient,
                        out keyIn, out keyOut);
                }

                clientResp = new byte[RTMP_SIG_SIZE];

                // generate random data
                for (int i = 0; i < RTMP_SIG_SIZE; i += 4) Array.Copy(BitConverter.GetBytes(rand.Next(ushort.MaxValue)), 0, clientResp, i, 4);

                // calculate response now
                byte[] signatureResp = new byte[SHA256_DIGEST_LENGTH];
                byte[] digestResp = new byte[SHA256_DIGEST_LENGTH];

                HMACsha256(serversig, digestPosServer, SHA256_DIGEST_LENGTH, GenuineFPKey, GenuineFPKey.Length, digestResp, 0);
                HMACsha256(clientResp, 0, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digestResp, SHA256_DIGEST_LENGTH, signatureResp, 0);

                // some info output
                Logger.Log("Calculated digest key from secure key and server digest: ");
                Logger.LogHex(digestResp, 0, SHA256_DIGEST_LENGTH);

                // FP10 stuff
                if (type == 8)
                {
                    // encrypt signatureResp
                    for (int i = 0; i < SHA256_DIGEST_LENGTH; i += 8)
                        rtmpe8_sig(signatureResp, i, digestResp[i] % 15);
                }
                else if (type == 9)
                {
                    // encrypt signatureResp
                    for (int i = 0; i < SHA256_DIGEST_LENGTH; i += 8)
                        rtmpe9_sig(signatureResp, i, digestResp[i] % 15);
                }

                Logger.Log("Client signature calculated:");
                Logger.LogHex(signatureResp, 0, SHA256_DIGEST_LENGTH);

                Array.Copy(signatureResp, 0, clientResp, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, SHA256_DIGEST_LENGTH);
            }
            else
            {
                clientResp = serversig;
            }

            WriteN(clientResp, 0, RTMP_SIG_SIZE);

            // 2nd part of handshake
            byte[] resp = new byte[RTMP_SIG_SIZE];
            if (ReadN(resp, 0, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) return false;

            if (FP9HandShake)
            {
                if (resp[4] == 0 && resp[5] == 0 && resp[6] == 0 && resp[7] == 0)
                {
                    Logger.Log("Wait, did the server just refuse signed authentication?");
                }

                // verify server response
                byte[] signature = new byte[SHA256_DIGEST_LENGTH];
                byte[] digest = new byte[SHA256_DIGEST_LENGTH];

                Logger.Log(string.Format("Client signature digest position: {0}", digestPosClient));
                HMACsha256(clientsig, 1 + digestPosClient, SHA256_DIGEST_LENGTH, GenuineFMSKey, GenuineFMSKey.Length, digest, 0);
                HMACsha256(resp, 0, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digest, SHA256_DIGEST_LENGTH, signature, 0);

                // show some information
                Logger.Log("Digest key: ");
                Logger.LogHex(digest, 0, SHA256_DIGEST_LENGTH);

                // FP10 stuff
                if (type == 8)
                {
                    // encrypt signatureResp
                    for (int i = 0; i < SHA256_DIGEST_LENGTH; i += 8)
                        rtmpe8_sig(signature, i, digest[i] % 15);
                }
                else if (type == 9)
                {
                    // encrypt signatureResp
                    for (int i = 0; i < SHA256_DIGEST_LENGTH; i += 8)
                        rtmpe9_sig(signature, i, digest[i] % 15);
                }

                Logger.Log("Signature calculated:");
                Logger.LogHex(signature, 0, SHA256_DIGEST_LENGTH);

                Logger.Log("Server sent signature:");
                Logger.LogHex(resp, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, SHA256_DIGEST_LENGTH);

                for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
                    if (signature[i] != resp[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH + i])
                    {
                        Logger.Log("Server not genuine Adobe!");
                        return false;
                    }
                Logger.Log("Genuine Adobe Flash Media Server");

                if (encrypted)
                {
                    // set keys for encryption from now on
                    Link.rc4In = new Org.BouncyCastle.Crypto.Engines.RC4Engine();
                    Link.rc4In.Init(false, new Org.BouncyCastle.Crypto.Parameters.KeyParameter(keyIn));

                    Link.rc4Out = new Org.BouncyCastle.Crypto.Engines.RC4Engine();
                    Link.rc4Out.Init(true, new Org.BouncyCastle.Crypto.Parameters.KeyParameter(keyOut));

                    // update 'encoder / decoder state' for the RC4 keys
                    // both parties *pretend* as if handshake part 2 (1536 bytes) was encrypted
                    // effectively this hides / discards the first few bytes of encrypted session
                    // which is known to increase the secure-ness of RC4
                    // RC4 state is just a function of number of bytes processed so far
                    // that's why we just run 1536 arbitrary bytes through the keys below
                    byte[] dummyBytes = new byte[RTMP_SIG_SIZE];
                    Link.rc4In.ProcessBytes(dummyBytes, 0, RTMP_SIG_SIZE, new byte[RTMP_SIG_SIZE], 0);
                    Link.rc4Out.ProcessBytes(dummyBytes, 0, RTMP_SIG_SIZE, new byte[RTMP_SIG_SIZE], 0);
                }
            }
            else
            {
                for (int i = 0; i < RTMP_SIG_SIZE; i++)
                    if (resp[i] != clientsig[i + 1])
                    {
                        Logger.Log("client signature does not match!");
                        break; //return false; - continue anyway
                    }
            }

            Logger.Log("Handshaking finished....");
            return true;
        }