// This is the only method about exchange data between this software and AirVPN infrastructure. // We don't use SSL. Useless layer in our case, and we need to fetch hostname and direct IP that don't permit common-name match. // 'S' is the AES 256 bit one-time session key, crypted with a RSA 4096 public-key. // 'D' is the data from the client to our server, crypted with the AES. // The server answer is XML decrypted with the same AES session. public static XmlDocument FetchUrl(string authPublicKey, string url, Dictionary <string, string> parameters) { // AES using (RijndaelManaged rijAlg = new RijndaelManaged()) { rijAlg.KeySize = 256; rijAlg.GenerateKey(); rijAlg.GenerateIV(); // Generate S // Bug workaround: Xamarin 6.1.2 macOS throw an 'Default constructor not found for type System.Diagnostics.FilterElement' error. // in 'new System.Xml.Serialization.XmlSerializer', so i avoid that. /* * StringReader sr = new System.IO.StringReader(authPublicKey); * System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters)); * RSAParameters publicKey = (RSAParameters)xs.Deserialize(sr); */ RSAParameters publicKey = new RSAParameters(); XmlDocument docAuthPublicKey = new XmlDocument(); docAuthPublicKey.LoadXml(authPublicKey); publicKey.Modulus = Convert.FromBase64String(docAuthPublicKey.DocumentElement["Modulus"].InnerText); publicKey.Exponent = Convert.FromBase64String(docAuthPublicKey.DocumentElement["Exponent"].InnerText); Dictionary <string, byte[]> assocParamS = new Dictionary <string, byte[]>(); assocParamS["key"] = rijAlg.Key; assocParamS["iv"] = rijAlg.IV; byte[] bytesParamS = null; using (RSACryptoServiceProvider csp = new RSACryptoServiceProvider()) { csp.ImportParameters(publicKey); bytesParamS = csp.Encrypt(UtilsCore.AssocToUtf8Bytes(assocParamS), false); } // Generate D byte[] aesDataIn = UtilsCore.AssocToUtf8Bytes(parameters); byte[] bytesParamD = null; { MemoryStream aesCryptStream = null; CryptoStream aesCryptStream2 = null; try { aesCryptStream = new MemoryStream(); using (ICryptoTransform aesEncryptor = rijAlg.CreateEncryptor()) { aesCryptStream2 = new CryptoStream(aesCryptStream, aesEncryptor, CryptoStreamMode.Write); aesCryptStream2.Write(aesDataIn, 0, aesDataIn.Length); aesCryptStream2.FlushFinalBlock(); bytesParamD = aesCryptStream.ToArray(); } } finally { if (aesCryptStream2 != null) { aesCryptStream2.Dispose(); } else if (aesCryptStream != null) { aesCryptStream.Dispose(); } } } // HTTP Fetch HttpRequest request = new HttpRequest(); request.Url = url; request.Parameters["s"] = UtilsString.Base64Encode(bytesParamS); request.Parameters["d"] = UtilsString.Base64Encode(bytesParamD); HttpResponse response = Engine.Instance.FetchUrl(request); try { byte[] fetchResponse = response.BufferData; byte[] fetchResponsePlain = null; MemoryStream aesDecryptStream = null; CryptoStream aesDecryptStream2 = null; // Decrypt answer try { aesDecryptStream = new MemoryStream(); using (ICryptoTransform aesDecryptor = rijAlg.CreateDecryptor()) { aesDecryptStream2 = new CryptoStream(aesDecryptStream, aesDecryptor, CryptoStreamMode.Write); aesDecryptStream2.Write(fetchResponse, 0, fetchResponse.Length); aesDecryptStream2.FlushFinalBlock(); fetchResponsePlain = aesDecryptStream.ToArray(); } } finally { if (aesDecryptStream2 != null) { aesDecryptStream2.Dispose(); } else if (aesDecryptStream != null) { aesDecryptStream.Dispose(); } } string finalData = System.Text.Encoding.UTF8.GetString(fetchResponsePlain); XmlDocument doc = new XmlDocument(); doc.LoadXml(finalData); return(doc); } catch (Exception ex) { string message = ""; if (response.GetHeader("location") != "") { message = MessagesFormatter.Format(Messages.ManifestFailedUnexpected302, response.GetHeader("location")); } else { message = ex.Message + " - " + response.GetLineReport(); } throw new Exception(message); } } }