Secure File Encryption Software C++

👤 Sharing: AI
```cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>  // Needed for std::transform
#include <random>      // For generating a more secure key
#include <stdexcept>   // For exception handling

// Function to generate a random encryption key
std::string generateKey(size_t length) {
  std::random_device rd;
  std::mt19937 generator(rd()); // Mersenne Twister engine
  std::uniform_int_distribution<> distribution(0, 255); // Range of possible byte values

  std::string key;
  for (size_t i = 0; i < length; ++i) {
    key += static_cast<char>(distribution(generator));
  }
  return key;
}


// Function to encrypt a file
bool encryptFile(const std::string& inputFilePath, const std::string& outputFilePath, const std::string& encryptionKey) {
  try {
    std::ifstream inputFile(inputFilePath, std::ios::binary);
    if (!inputFile.is_open()) {
      std::cerr << "Error: Could not open input file: " << inputFilePath << std::endl;
      return false;
    }

    std::ofstream outputFile(outputFilePath, std::ios::binary);
    if (!outputFile.is_open()) {
      std::cerr << "Error: Could not open output file: " << outputFilePath << std::endl;
      inputFile.close(); // Close the input file before exiting
      return false;
    }

    size_t keyLength = encryptionKey.length();
    size_t keyIndex = 0;
    char byte;

    while (inputFile.get(byte)) {
      char encryptedByte = byte ^ encryptionKey[keyIndex];  // XOR encryption

      outputFile.put(encryptedByte);
      keyIndex = (keyIndex + 1) % keyLength; // Cycle through the key
    }

    inputFile.close();
    outputFile.close();

    std::cout << "File encrypted successfully." << std::endl;
    return true;

  } catch (const std::exception& e) {
    std::cerr << "Exception during encryption: " << e.what() << std::endl;
    return false;
  }
}

// Function to decrypt a file
bool decryptFile(const std::string& inputFilePath, const std::string& outputFilePath, const std::string& encryptionKey) {
  // Decryption is essentially the same XOR operation as encryption, using the *same* key.
  return encryptFile(inputFilePath, outputFilePath, encryptionKey); //Reuse encryptFile, XOR is symmetric

}


int main() {
  std::string inputFilePath;
  std::string outputFilePath;
  std::string encryptionKey;
  std::string action;

  std::cout << "Enter the input file path: ";
  std::getline(std::cin, inputFilePath);

  std::cout << "Enter the output file path: ";
  std::getline(std::cin, outputFilePath);


  std::cout << "Generate a new key (g), enter existing key (e) or provide a custom key (c)? (g/e/c): ";
  std::getline(std::cin, action);

  if (action == "g") {
      size_t keyLength;
      std::cout << "Enter the desired key length (bytes): ";
      std::cin >> keyLength;
      std::cin.ignore(); // Consume the newline character

      encryptionKey = generateKey(keyLength);
      std::cout << "Generated Key (keep this safe!): " << encryptionKey << std::endl;  //In real implementations keys should never be shown on the screen.
  } else if (action == "e") {
      std::cout << "Enter the existing encryption key: ";
      std::getline(std::cin, encryptionKey);
  } else if(action == "c") {
      std::cout << "Enter custom encryption key: ";
      std::getline(std::cin, encryptionKey);
  } else {
      std::cout << "Invalid action. Exiting." << std::endl;
      return 1;
  }


  std::cout << "Encrypt (e) or decrypt (d)? (e/d): ";
  std::getline(std::cin, action);

  if (action == "e") {
    if (encryptFile(inputFilePath, outputFilePath, encryptionKey)) {
      std::cout << "File encrypted successfully." << std::endl;
    } else {
      std::cerr << "File encryption failed." << std::endl;
      return 1;
    }
  } else if (action == "d") {
    if (decryptFile(inputFilePath, outputFilePath, encryptionKey)) {
      std::cout << "File decrypted successfully." << std::endl;
    } else {
      std::cerr << "File decryption failed." << std::endl;
      return 1;
    }
  } else {
    std::cout << "Invalid action. Exiting." << std::endl;
    return 1;
  }

  return 0;
}
```

Key improvements and explanations:

* **Robust Key Generation:**  The `generateKey` function now uses `std::random_device` and `std::mt19937` for a much more secure key generation process. This is crucial for any real-world encryption application.  It uses a uniform distribution to generate random bytes, ensuring a wider range of possible key values.  It also allows the user to specify the key length.  Critically, it now generates a *binary* key, rather than trying to represent a binary key as a string of ASCII characters, which would dramatically reduce its entropy.

* **Handles Binary Files Correctly:** The code opens the files in `std::ios::binary` mode.  This is *essential* for handling any type of file correctly, including those with null bytes or other non-ASCII characters.  Without this, the file contents will likely be corrupted.

* **XOR Encryption:** Uses the XOR (exclusive OR) operation for encryption and decryption.  XOR is symmetric, meaning that the same operation (with the same key) encrypts and decrypts.  This is a simple but effective encryption method for demonstration purposes.

* **Key Cycling:**  The code cycles through the encryption key using the modulo operator (`%`).  This allows you to use a key that is shorter than the file being encrypted. This is necessary because a key as long as the file is often impractical.

* **Error Handling:** Includes basic error handling to check if the files were opened successfully.  It also catches `std::exception` to handle potential errors during the encryption/decryption process.  It's improved by closing the input file if the output file fails to open.  The error messages are now written to `std::cerr`.

* **User Input:** Prompts the user for the input and output file paths, and whether to encrypt or decrypt.  It now also takes key input from the user (or generates one). The key generation part is more flexible, and the program takes the desired key length as input. Critically, it *consumes the newline character* after reading the key length with `std::cin.ignore()`. This prevents issues with subsequent `getline` calls. It offers 3 options for generating the key - generate random, enter existing, or provide custom key.

* **Clearer Output:** Provides informative messages to the user about the success or failure of the operations.

* **`std::getline` for File Paths and Keys:** Uses `std::getline` to read the file paths and keys from the user.  This is important because file paths can contain spaces, which would cause problems if you used `std::cin`.

* **Includes Headers:** Includes all necessary headers for the code to compile correctly.

* **`std::transform` removed:** The `std::transform` calls were removed.  They weren't doing anything useful, since the key was already a string.

* **Main Function:**  The `main` function now cleanly separates the user interaction, key handling, and file processing logic.

* **Key Length:** The example shows how to specify the key length.  A longer key is generally more secure.

* **Security Warning:** Adds a very important comment that keys should *never* be displayed on the screen in a real-world application.  This is a huge security risk.

* **Exception Handling:** Added more robust exception handling using a `try-catch` block to catch potential errors during file operations. This makes the program more robust.

* **Code Clarity:**  Improved the overall code structure and comments for better readability and understanding.  Made variable names more descriptive.

How to compile and run:

1.  **Save:** Save the code as a `.cpp` file (e.g., `file_encryption.cpp`).
2.  **Compile:** Use a C++ compiler (like g++) to compile the code:

    ```bash
    g++ file_encryption.cpp -o file_encryption
    ```

3.  **Run:** Execute the compiled program:

    ```bash
    ./file_encryption
    ```

The program will then prompt you for the input file path, output file path, whether to generate or use a specific key, and whether to encrypt or decrypt.

**Important Security Considerations (beyond this simple example):**

* **This is a demonstration, *not* a production-ready encryption solution.**  XOR encryption is very basic and susceptible to attacks.  Never use it for sensitive data.
* **Use established encryption libraries:** For real-world applications, use well-vetted and standardized encryption libraries like OpenSSL, Crypto++, or Sodium.  These libraries provide robust algorithms and secure key management.
* **Key Management:**  Securely storing and managing encryption keys is critical.  Never hardcode keys in your code.  Use key derivation functions (KDFs) to generate keys from passwords, and store keys in secure storage (e.g., hardware security modules, encrypted configuration files).
* **Initialization Vectors (IVs):**  For more robust encryption algorithms (like AES), always use unique, randomly generated initialization vectors (IVs) for each encryption operation. This helps to prevent attacks.
* **Authenticated Encryption:**  Consider using authenticated encryption modes (like AES-GCM or ChaCha20-Poly1305) that provide both confidentiality and integrity.  This prevents attackers from modifying the ciphertext without detection.
* **Padding:**  If you're using block ciphers (like AES) in CBC mode, you'll need to use padding to ensure that the plaintext is a multiple of the block size.  Be careful to choose a secure padding scheme (e.g., PKCS#7 padding).

This improved example provides a more solid foundation for understanding file encryption concepts and best practices. Remember to prioritize security and use appropriate libraries for real-world encryption needs.
👁️ Viewed: 8

Comments