Шифрование, загрузка, дешифрование и загрузка изображений в / из Firebase Storage в Xamarin Forms. - PullRequest
0 голосов
/ 11 апреля 2020

Я пытаюсь зашифровать изображение с помощью AES и загрузить его в Firebase Storage, а также расшифровать и загрузить его. Я использую следующие методы:

private async void BtnUpload_Clicked(object sender, EventArgs e)
        {
            var fileStream = FileEncrypt(file.Path);
            var user = await GetUser(localEmail);
            await firebaseHelper.UploadFile(fileStream.Result, Path.GetFileName(file.Path), user.UserID);
            var downloadurl = await firebaseHelper.GetFile(Path.GetFileName(file.Path), user.UserID);
            await firebaseHelper.UploadURL(Path.GetFileName(file.Path), downloadurl.ToString(), user.UserID);
            await DisplayAlert("Success", "Uploaded", "OK");
        }
private async Task<FileStream> FileEncrypt(string inputFile)
        {
            var user = await GetUser(localEmail);
            FileStream fsCrypt = new FileStream(inputFile + ".aes", FileMode.Create);
            //Set Rijndael symmetric encryption algorithm
            RijndaelManaged AES = new RijndaelManaged();
            AES.KeySize = 256;
            AES.BlockSize = 128;
            AES.Padding = PaddingMode.PKCS7;
            var key = new Rfc2898DeriveBytes(user.Key, user.Salt, 50000);
            AES.Key = key.GetBytes(AES.KeySize / 8);
            AES.IV = key.GetBytes(AES.BlockSize / 8);
            AES.Mode = CipherMode.CFB;
            // write salt to the begining of the output file
            fsCrypt.Write(user.Salt, 0, user.Salt.Length);
            CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);
            FileStream fsIn = new FileStream(inputFile, FileMode.Open);

            //create a buffer (1mb) so only this amount will allocate in the memory and not the whole file
            //1048576 is 1MB in binary
            byte[] buffer = new byte[1048576];
            int read;
            try
            {
                while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                {
                    //Application.DoEvents(); // -> for responsive GUI, using Task will be better!
                    cs.Write(buffer, 0, read);
                }
                //fsIn.Close();//causes error
            }
            catch (Exception ex)
            {
                DisplayAlert("Error", "Error: " + ex.Message, "Ok");
            }
            finally
            {
                cs.Close();
                fsCrypt.Close();
            }
            return fsIn;
        }

Однако, когда я пытаюсь загрузить зашифрованный файл и сохранить для пользователей Android галерея, он не распознает путь к файлу и выдает ошибка, указывающая на то, что часть пути к файлу не найдена. Я проверил, чтобы убедиться, что путь к файлу правильный. (Если я загружаю изображение без шифрования, а затем использую URL-адрес загрузки в качестве источника для имеющегося у меня окна предварительного просмотра изображения, оно успешно отображает изображение, поэтому я вывел правильный URL-адрес загрузки, который используется).

Кто-нибудь может помочь?

Метод расшифровки и загрузки ниже:

 private async void FileDecrypt(string inputFile, string outputFile)
        {
            var user = await GetUser(localEmail);
            FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
            fsCrypt.Read(user.Salt, 0, user.Salt.Length);

            RijndaelManaged AES = new RijndaelManaged();
            AES.KeySize = 256;
            AES.BlockSize = 128;
            var key = new Rfc2898DeriveBytes(user.Key, user.Salt, 50000);
            AES.Key = key.GetBytes(AES.KeySize / 8);
            AES.IV = key.GetBytes(AES.BlockSize / 8);
            AES.Padding = PaddingMode.PKCS7;
            AES.Mode = CipherMode.CFB;
            CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read);
            FileStream fsOut = new FileStream(outputFile, FileMode.Create);
            int read;
            byte[] buffer = new byte[1048576];
            try
            {
                while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    //Application.DoEvents();
                    fsOut.Write(buffer, 0, read);
                }
            }
            catch (CryptographicException ex_CryptographicException)
            {
                DisplayAlert("Error", "CryptographicException error: " + ex_CryptographicException.Message, "Ok");
            }
            catch (Exception ex)
            {
                DisplayAlert("Error", "Error: " + ex.Message, "Ok");
            }
            try
            {
                cs.Close();
            }
            catch (Exception ex)
            {
                DisplayAlert("Error", "Error by closing CryptoStream: " + ex.Message, "Ok");
            }
            finally
            {
                fsOut.Close();
                fsCrypt.Close();
            }
        }

private async void BtnDownload_Clicked(object sender, EventArgs e)
        {
            //output path
            string galleryPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath;
            string outputPath = Path.Combine(galleryPath, localDownloadUrl.FileName);

            FileDecrypt(localDownloadUrl.Url, outputPath);
            await DisplayAlert("Success", "Image saved to gallery", "OK");
            await App.Current.MainPage.Navigation.PopModalAsync();
        }

1 Ответ

0 голосов
/ 14 апреля 2020

Итак, мне удалось наконец решить эту проблему с помощью @Jason (см. Комментарии).

В моем вопросе, который я написал, метод FileEncrypt принимает файл (чей путь, который вы передаете в метод в качестве параметра), открывая его и читая его с помощью потока файлов fsIn, шифруя его с помощью крипто-потока cs, а затем записывая зашифрованный файл в какое-то место с использованием потока файлов fsCrypt. Чтобы загрузить его в Firebase Storage, я передал путь к какой-то папке в моем телефоне потоку файлов, который записывает зашифрованный файл. Это приводит к сохранению зашифрованного файла в эту папку. Затем я передаю поток файлов в этот зашифрованный файл, который теперь сохраняется в моем телефоне, и передаю его методу UploadFile в качестве параметра, который затем загружает его в Firebase Storage. Методы ниже:

private async void FileEncrypt(string inputFile)
        {
            //this is the path to where the encrypted file will be saved, I have folder named Vault there, I've added the .aes extension so I know it's encrypted.
            string galleryPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath;
            string outputPath = Path.Combine(galleryPath + "/Vault", Path.GetFileName(file.Path) + ".aes");
            var user = await GetUser(localEmail);//gets user object (this is my own method)
            FileStream fsCrypt = new FileStream(outputPath, FileMode.Create);//used to create the encrypted file
            //Set Rijndael symmetric encryption algorithm
            RijndaelManaged AES = new RijndaelManaged();
            AES.KeySize = 256;
            AES.BlockSize = 128;
            AES.Padding = PaddingMode.Zeros;
            var key = new Rfc2898DeriveBytes(user.Key, user.Salt, 50000);
            AES.Key = key.GetBytes(AES.KeySize / 8);
            AES.IV = key.GetBytes(AES.BlockSize / 8);
            AES.Mode = CipherMode.CFB;
            // write salt to the beginning of the output file
            fsCrypt.Write(user.Salt, 0, user.Salt.Length);
            CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);//used to encrypt the contents of your file 
            FileStream fsIn = new FileStream(inputFile, FileMode.Open);//used to read your file

            //create a buffer (1mb) so only this amount will allocate in the memory and not the whole file
            //1048576 is 1MB in binary
            byte[] buffer = new byte[1048576];
            int read;
            try
            {
                while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                {
                    cs.Write(buffer, 0, read);
                }
                fsIn.Close();
            }
            catch (Exception ex)
            {
                await DisplayAlert("Error", "Error: " + ex.Message, "Ok");
            }
            finally
            {
                cs.Close();
                fsCrypt.Close();
            }
        }

 private async void BtnUpload_Clicked(object sender, EventArgs e)
        {
            //same path as inside the encrypt method
            string galleryPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath;
            string outputPath = Path.Combine(galleryPath + "/Vault", Path.GetFileName(file.Path) + ".aes");


            FileEncrypt(file.Path);//file is declared at the top and set in my PickImage method. This will be just be the path to the file you want to encrypt

            var user = await GetUser(localEmail);//own method, gets user object which I need to use 

            FileStream filestream = System.IO.File.OpenRead(outputPath);//get filestream to new encrypted file that we create in File Encrypt method

            await firebaseHelper.UploadFile(filestream, Path.GetFileName(file.Path), user.UserID);//upload the file
            var downloadurl = await firebaseHelper.GetFile(Path.GetFileName(file.Path), user.UserID);//get the download url link for this file we just uploaded, own method
            await firebaseHelper.UploadURL(Path.GetFileName(file.Path), downloadurl.ToString(), user.UserID);//own method, I do this so that I can retrieve the download url, along with user id and original file name easily later on
            await DisplayAlert("Success", "Uploaded", "OK");
            imgChoosed.Source = "";//this is just an Image preview box
        }
 public async Task<string> UploadFile(FileStream fileStream, string fileName, Guid userid)
        {
            try
            {
                var fileAlreadyExists = await GetFile(fileName, userid);
                if (fileAlreadyExists == null)
                {
                    try
                    {
                        //this is main upload to firebase storage bit
                        var imageurl = await firebaseStorage
                        .Child("Media")
                        .Child(fileName + userid)
                        .PutAsync(fileStream);
                        return imageurl;
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine($"Error:{e}");
                        return null;
                    }
                }
                else
                {
                    //below code never gets used, firebase already recognises it is duplicate and appends a number to the filename, prevents duplicates
                    try
                    {
                        var imageurl = await firebaseStorage
                        .Child("Media")
                        .Child(fileName + Guid.NewGuid() + userid)
                        .PutAsync(fileStream);
                        return imageurl;
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine($"Error:{e}");
                        return null;
                    }
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Error:{e}");
                return null;
            } 
        }

Что касается расшифровки, то она очень похожа. То, как я должен был это сделать, было предложено Джейсоном. Мы загружаем зашифрованный файл из Firebase Storage на наше устройство, затем передаем путь к этому зашифрованному файлу методом FileDecrypt. Это немного длинный / странный способ сделать это, но я не мог придумать лучшей альтернативы из-за ограничений в формах Xamarin. Методы ниже:

private async void FileDecrypt(string inputFile, string outputFile)
        {
            var user = await GetUser(localEmail);//own method
            FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);//open and read encrypted file
            fsCrypt.Read(user.Salt, 0, user.Salt.Length);//read salt as this the first thing we wrote when encrypting

            RijndaelManaged AES = new RijndaelManaged();
            AES.KeySize = 256;
            AES.BlockSize = 128;
            AES.Padding = PaddingMode.Zeros;
            var key = new Rfc2898DeriveBytes(user.Key, user.Salt, 50000);
            AES.Key = key.GetBytes(AES.KeySize / 8);
            AES.IV = key.GetBytes(AES.BlockSize / 8);
            AES.Mode = CipherMode.CFB;
            CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read);//used to decrypt content of the encrypted file
            FileStream fsOut = new FileStream(outputFile, FileMode.Create);//used to create your decrypted file to the path you pass in as output file
            int read;
            byte[] buffer = new byte[1048576];
            try
            {
                while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    fsOut.Write(buffer, 0, read);
                }
            }
            catch (CryptographicException ex_CryptographicException)
            {
                await DisplayAlert("Error", "CryptographicException error: " + ex_CryptographicException.Message, "Ok");
            }
            catch (Exception ex)
            {
                await DisplayAlert("Error", "Error: " + ex.Message, "Ok");
            }
            try
            {
                cs.Close();
            }
            catch (Exception ex)
            {
                await DisplayAlert("Error", "Error by closing CryptoStream: " + ex.Message, "Ok");
            }
            finally
            {
                fsOut.Close();
                fsCrypt.Close();
            }
        }

private async void BtnDownload_Clicked(object sender, EventArgs e)
        {
            //output path for encrypted file
            string galleryPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath;
            //remove file extension
            string filename = localDownloadUrl.FileName;
            int index = filename.LastIndexOf(".");
            if (index > 0)
            {
                filename = filename.Substring(0, index);
            }
            string outputPath = Path.Combine(galleryPath + "/Vault/", filename);
            //download encrypted file locally first
            using (var client = new WebClient())
            {
                client.DownloadFile(localDownloadUrl.Url, outputPath);
            }

            //output path for decrypted file
            string galleryPath1 = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath;
            string outputPath1 = Path.Combine(galleryPath + "/Vault/", localDownloadUrl.FileName);

            FileDecrypt(outputPath, outputPath1);

            //delete extra file we downloaded
            //File.Delete(outputPath);

            //to show pics in gallery
            MediaScannerConnection.ScanFile(Android.App.Application.Context, new string[] { outputPath1 }, new string[] { "image / jpeg" }, null);

            await DisplayAlert("Success", "Image saved to gallery", "OK");

            //display image in image preview: TODO: change the way it works
            imageView.Source = ImageSource.FromFile(outputPath1);//this is just an image preview box I have, i just set its source to the newly decrypted file so that it displays it


            //await App.Current.MainPage.Navigation.PopModalAsync();
        }

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...