} // 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
public static void DH() { var Tpar = Security.GenerateParameters(); // To są Twoje p i g, stringi zawierające 16-stkowo zapisaną liczbę string p = Security.GetP(Tpar); string g = Security.GetG(Tpar); // Tworzysz nowe parametry podając 16 jako drugi argument konstruktora var par = new Org.BouncyCastle.Crypto.Parameters.DHParameters(new Org.BouncyCastle.Math.BigInteger(p, 16), new Org.BouncyCastle.Math.BigInteger(g, 16)); var Akeys = Security.GenerateKeys(par); var Bkeys = Security.GenerateKeys(par); // Chcąc odwołać się do np publicznego klucza (zapisanego szesnastkowo) string x = Security.GetPublicKey(Akeys); string y = Security.GetPrivateKey(Bkeys); // Ta funkcja jakos PublicKey przyjmuje stringa reprezentującego liczbę 16-stkową, a funkcja GetPublic/PrivateKey to zwraca Console.WriteLine(Security.ComputeSharedSecret(Security.GetPublicKey(Akeys), Security.GetPrivateKey(Bkeys), p, g).Length); Console.WriteLine(Security.ComputeSharedSecret(Security.GetPublicKey(Bkeys), Security.GetPrivateKey(Akeys), p, g).Length); }
// 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); }
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; }