internal string convertPassword(string password, AppConst.MPE_Flag flag) { if (flag.HasFlag(AppConst.MPE_Flag.isRemoveSpace)) { password = Util.toRemoveInnerSpaces(password); } if (flag.HasFlag(AppConst.MPE_Flag.isIgnoreCase)) { password = Util.toUpperCase(password); } if (flag.HasFlag(AppConst.MPE_Flag.isIgnoreZenHan)) { password = Util.toZenkaku(password); } if (flag.HasFlag(AppConst.MPE_Flag.isIgnoreHiraKata)) { password = Util.toHiragana(password); } return(password); }
/** * First zip the file and then encrypt it. * (J) 最初にファイルをzip圧縮し、その後に暗号化する。 * * encTargetList : Files or directories for encryption. * hints : question's hint * passwordLists : password List for hint. * nRequiredPasswords : number of passwords are required for decryption. * flag : Encryption flag * outputFS : output file stream. */ internal static void Encrypt( List <string> encTargetList, string[] hints, List <string>[] passwordLists, int nRequiredPasswords, AppConst.MPE_Flag flag, FileStream outputFS, IProgress <string> progress = null) { var tmpFileCompress = System.IO.Path.GetRandomFileName(); // temporary file name for zip file. try { progress?.Report("(1/4) creating archive."); // zip CompressionLevel compressionLevel = flag.HasFlag(AppConst.MPE_Flag.isNoCompress) ? CompressionLevel.NoCompression : CompressionLevel.Optimal; using (FileStream zipFS = File.Create(tmpFileCompress)) { using (ZipArchive archive = new ZipArchive(zipFS, ZipArchiveMode.Create)) { foreach (var item in encTargetList) { string rootPathFile = item.ToString(); if (File.Exists(rootPathFile)) { // Add the file as it is. //(J) ファイルはそのまま追加 archive.CreateEntryFromFile(rootPathFile, System.IO.Path.GetFileName(rootPathFile), compressionLevel); } else if (Directory.Exists(rootPathFile)) { // Add directory recursively //(J) ディレクトリは再帰的に追加 string parentPath = System.IO.Directory.GetParent(rootPathFile).ToString(); string[] pathFiles = Directory.GetFileSystemEntries(rootPathFile, "*", System.IO.SearchOption.AllDirectories); if (pathFiles.Length == 0) { // Empty directory added only entry. //(J) 空ディレクトリはエントリのみ追加 archive.CreateEntry(System.IO.Path.GetFullPath(rootPathFile).Substring(parentPath.Length + 1) + "/"); } foreach (var pathFile in pathFiles) { string entryPath = pathFile.Substring(parentPath.Length + 1); // Archive the specified directory as root. (J)指定したディレクトリをルートとしてアーカイブ if (File.Exists(pathFile)) { archive.CreateEntryFromFile(pathFile, entryPath, compressionLevel); } else if (Directory.Exists(pathFile)) { archive.CreateEntry(entryPath + "/"); } } } } } } // File is not properly terminated without Dispose(). // Dispose() archive, zipFS will also be closed(), so open it again. //(J) 一旦 archive を Dispose() しないと正しくファイルが終端化されない。 //(J) archive を Dispose() すると zipFS も Close()されてしまうため、開きなおす。 progress?.Report("(2/4) encrypting archive."); var tmpFileEncrypted = System.IO.Path.GetRandomFileName(); // temporary file name for encrypted file. try { // encryption RNGCryptoServiceProvider r = new RNGCryptoServiceProvider(); byte[] dataKey = new byte[Crypto.AES_KEY_SIZE_BYTE]; // Encryption key for data. r.GetBytes(dataKey); using (FileStream zipFS = File.Open(tmpFileCompress, FileMode.Open)) { using (FileStream encryptedFS = File.Create(tmpFileEncrypted)) { Crypto.encryptStream(zipFS, encryptedFS, dataKey); } } // Since it became unnecessary, try to delete here //(J) 不要になったのでここで削除を試みる File.Delete(tmpFileCompress); using (FileStream encryptedFS = File.Open(tmpFileEncrypted, FileMode.Open)) { Encrypt_2(encryptedFS, hints, passwordLists, nRequiredPasswords, flag, r, dataKey, outputFS, progress); } progress?.Report("(4/4) finished."); } finally { File.Delete(tmpFileEncrypted); } } finally { File.Delete(tmpFileCompress); } }
// Encrypt private async void EncryptButton_Click(object sender, RoutedEventArgs e) { DisableUIs(); // input file check string message; if (EncFileNameLb.Items.Count == 0) { message = ResourceService.Current.getMessage("MsgETargetNotSpecified"); // Encryption target file is not specified. MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK); goto EXIT; } if (EncOutputFileTxt.Text.Trim() == "") { message = ResourceService.Current.getMessage("MsgEOutputNotSpecified"); // Output file is not specified. MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK); goto EXIT; } var fileList = new List <string>(); var dirList = new List <string>(); var notExistFileList = new List <string>(); var encTargetList = new List <string>(); foreach (var item in EncFileNameLb.Items) { string pathFile = item.ToString(); if (File.Exists(pathFile)) { fileList.Add(System.IO.Path.GetFileName(pathFile)); encTargetList.Add(pathFile); } else if (Directory.Exists(pathFile)) { var dir = new DirectoryInfo(pathFile); dirList.Add(dir.Name); encTargetList.Add(pathFile); } else { notExistFileList.Add(pathFile); } } if (fileList.Count != fileList.Distinct().Count() || dirList.Count != dirList.Distinct().Count()) { message = ResourceService.Current.getMessage("MsgETargetDuplicated"); // Encryption is impossible due to duplicate file name or folder name. MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK); goto EXIT; } if (notExistFileList.Count > 0) { message = ResourceService.Current.getMessage("MsgWFileNotExisted"); // The following file does not exist. foreach (var s in notExistFileList) { message += "\r\n\t" + s; } if (EncFileNameLb.Items.Count == notExistFileList.Count) { message += "\r\n\r\n" + ResourceService.Current.getMessage("MsgETargetNotExisted"); // There is no file to encrypt. MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK); goto EXIT; } else { message += "\r\n\r\n" + ResourceService.Current.getMessage("MsgCEncryptOnlyExistFile"); // Encrypt with existing files only? if (MessageBox.Show(message, "Confirmation", MessageBoxButton.YesNo, MessageBoxImage.Exclamation, MessageBoxResult.No) == MessageBoxResult.No) { goto EXIT; } } } // input password check int nQuestions = _encQuestionList.Count; int nRequiredPasswords = int.Parse(EncNumRequiredPasswordsCmb.Text); if ((bool)EncTrimChk.IsChecked) { foreach (var question in _encQuestionList) { foreach (var passwordText in question._passwordStackPanel.Children.OfType <TextBox>()) { passwordText.Text = passwordText.Text.Trim(); } } } string[] hintList = new string[nQuestions]; List <string>[] passwordList = new List <string> [nQuestions]; int nDummyQuestion = 0; for (int i = 0; i < _encQuestionList.Count; ++i) { var question = _encQuestionList[i]; hintList[i] = question._hintTextBox.Text; passwordList[i] = new List <string>(); foreach (var passwordText in question._passwordStackPanel.Children.OfType <TextBox>()) { if (passwordText.Text != "") { passwordList[i].Add(passwordText.Text); } } if (passwordList[i].Count == 0) { nDummyQuestion += 1; } } if (nDummyQuestion > 0) { message = ResourceService.Current.getMessage("MsgWDummyQuestion"); // A Question without a password is a dummy Question. if (nQuestions - nDummyQuestion < nRequiredPasswords) { message += "\r\n" + ResourceService.Current.getMessage("MsgWUndecrytableArchive"); // Because it is less than the number of passwords required for decryption, an archive that can not be decrypted is created. } MessageBox.Show(message, "Warning", MessageBoxButton.OK, MessageBoxImage.Exclamation, MessageBoxResult.OK); } if (File.Exists(EncOutputFileTxt.Text)) { message = ResourceService.Current.getMessage("MsgCOverwriteOutputFile"); // The output file exists. Do you want to overwrite? if (MessageBox.Show(message, "Confirmation", MessageBoxButton.YesNo, MessageBoxImage.Exclamation, MessageBoxResult.No) == MessageBoxResult.No) { goto EXIT; } } FileStream outputFS; try { outputFS = File.Create(EncOutputFileTxt.Text); } catch (Exception ex) { message = ResourceService.Current.getMessage("MsgEFailCreateFile") + ex.Message; // Failed to create the output file.\r\n\r\nDetailed information: MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK); goto EXIT; } message = ResourceService.Current.getMessage("MsgCStartEncryption"); // Start encryption. if (MessageBox.Show(message, "Confirmation", MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel) == MessageBoxResult.Cancel) { outputFS.Close(); goto EXIT; } AppConst.MPE_Flag flag = createFlag(); foreach (var list in passwordList) { for (int i = 0; i < list.Count; ++i) { list[i] = convertPassword(list[i], flag); } } var currentCursor = this.Cursor; try { this.Cursor = Cursors.Wait; var p = new Progress <string>(ShowProgress); await Task.Run(() => { MPECore.Encrypt(encTargetList, hintList, passwordList, nRequiredPasswords, flag, outputFS, p); }); } catch (Exception ex) { message = ResourceService.Current.getMessage("MsgEFailEncryption") + ex.Message; // Encryption failed.\r\n\r\nDetailed information: MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK); goto EXIT; } finally { outputFS.Close(); this.Cursor = currentCursor; } message = ResourceService.Current.getMessage("MsgIEndEncryption"); // Encryption is complete. MessageBox.Show(message, "Succeed", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK); EXIT: EnableUIs(); }
/** * encryption */ internal static void Encrypt_2( FileStream encryptedFS, string[] hintList, List <string>[] passwordList, int nRequiredPasswords, AppConst.MPE_Flag flag, RNGCryptoServiceProvider r, byte[] dataKey, FileStream outputFS, IProgress <string> progress = null) { // generate questionKeys, encryptedQuestionKeys int nQuestions = hintList.Length; byte[][] questionKeys = new byte[nQuestions][]; byte[][][] encryptedQuestionKeys = new byte[nQuestions][][]; for (int i = 0; i < nQuestions; ++i) { questionKeys[i] = new byte[Crypto.AES_KEY_SIZE_BYTE]; // Encryption key for dataKey r.GetBytes(questionKeys[i]); // encrypt questionKey // Based on the number of passwords, assign the location of the password to encrypt questionKeys. In order to hide the number of passwords, dummy indexes are also included. // For example, when three passwords are set in the Question being processed, the following values are randomly set in passwordEncryptIndexes. // passwordEncryptIndexes = {1, -1, 0, 2, -1} // In this case, encryptedQuestionKeys is [question 0] encrypted questionKey with password 1, [1]: dummy, [2]: questionKey encrypted with password 0, [3]: questionKey encrypted with password 2 , [4]: Dummy is stored. //(J) パスワードの個数をもとに、questionKeysを暗号化するパスワードの位置を割り当てる。パスワード個数を隠蔽するため、ダミーのインデクスも含まれる。 //(J) たとえば、処理中の Question にパスワードが3つ設定されている場合に、passwordEncryptIndexes には以下のような値をランダムに設定する。 //(J) passwordEncryptIndexes = {1, -1, 0, 2, -1} //(J) この場合はencryptedQuestionKeys は、[0]:パスワード1で暗号化されたquestionKey, [1]:ダミー, [2]:パスワード0で暗号化されたquestionKey, [3]:パスワード2で暗号化されたquestionKey, [4]:ダミー が格納される。 int[] passwordEncryptIndexes = alllocPasswordEncryptIndexes(passwordList[i].Count); encryptedQuestionKeys[i] = new byte[passwordEncryptIndexes.Length][]; for (int j = 0; j < passwordEncryptIndexes.Length; ++j) { if (passwordEncryptIndexes[j] >= 0) { // real password String password = passwordList[i][passwordEncryptIndexes[j]]; byte[] passwordBytes = Encoding.UTF8.GetBytes(password); encryptedQuestionKeys[i][j] = Crypto.encryptByte(questionKeys[i], passwordBytes, Crypto.createSalt(i, j)); if (encryptedQuestionKeys[i][j].Length != Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING) { // assertion throw new Exception("system error"); //! } } else { // dummy key byte[] dummyKey = new byte[Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING]; r.GetBytes(dummyKey); encryptedQuestionKeys[i][j] = dummyKey; } } } // generate encryptedDataKey int[][] combinaiton = Combination.enumerateCombination(nQuestions, nRequiredPasswords); byte[] combinedQuestionKey = new byte[nRequiredPasswords * Crypto.AES_KEY_SIZE_BYTE]; byte[][] encryptedDataKey = new byte[combinaiton.Length][]; for (int i = 0; i < combinaiton.Length; ++i) { // Create a password for dataKey from multiple question keys. //(J) 複数のquestion key から dataKey 用のパスワードを作成する for (int j = 0; j < combinaiton[i].Length; ++j) { int qkIdx = combinaiton[i][j]; Buffer.BlockCopy(questionKeys[qkIdx], 0, combinedQuestionKey, j * Crypto.AES_KEY_SIZE_BYTE, Crypto.AES_KEY_SIZE_BYTE); } encryptedDataKey[i] = Crypto.encryptByte(dataKey, combinedQuestionKey); if (encryptedDataKey[i].Length != Crypto.AES_KEY_SIZE_BYTE_WITH_PADDING) { // assertion throw new Exception("system error"); //! } } progress?.Report("(3/4) creating output file."); // create question Header (for MPE File) using (var questionHeaderMS = new MemoryStream()) { byte[] bytes; // flag 1 questionHeaderMS.WriteByte((byte)flag); // nQuestions 4 bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(nQuestions)); questionHeaderMS.Write(bytes, 0, bytes.Length); // nRequiredPasswords 4 bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(nRequiredPasswords)); questionHeaderMS.Write(bytes, 0, bytes.Length); // nPasswordCombinations 4 bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(encryptedDataKey.Length)); questionHeaderMS.Write(bytes, 0, bytes.Length); // encryptedDataKeys n * 48 for (int i = 0; i < encryptedDataKey.Length; ++i) { questionHeaderMS.Write(encryptedDataKey[i], 0, encryptedDataKey[i].Length); } // Question n for (int i = 0; i < nQuestions; ++i) { // hint string len 4 byte[] hintStrBytes = Encoding.UTF8.GetBytes(hintList[i]); bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(hintStrBytes.Length)); questionHeaderMS.Write(bytes, 0, bytes.Length); // hint string n questionHeaderMS.Write(hintStrBytes, 0, hintStrBytes.Length); // nPasswords 4 bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(encryptedQuestionKeys[i].Length)); questionHeaderMS.Write(bytes, 0, bytes.Length); // encryptedQuestionKeys n * 48 for (int j = 0; j < encryptedQuestionKeys[i].Length; ++j) { questionHeaderMS.Write(encryptedQuestionKeys[i][j], 0, encryptedQuestionKeys[i][j].Length); } } Encrypt_3(questionHeaderMS, encryptedFS, outputFS); } }