Skip to content

Repository files navigation

Information Security Project

Webhub

NAME NRP
Muhammad Fadhlan Ashila Harashta 5025211168
Pascal Roger Junior Tauran 5025211072
Faraihan Rafi Adityawarman 5025211074
Fauzan Ahmad Faisal 5025211067

Outline

  • We created a simple website that allows the user to register, login and then store their data. All these files are then encrypted before being stored.

Set Up

  • Before running program, please edit the env file and add
AES_KEY_KEY=9328e2bce387ed16a42f46c780fe1f64
  • Or you can use any key you want

Features:

User register & login screen

  • The user is able to register themselves into the into the website providing information such as their name, username, email, password as ID card image. These data will be encrypted and then stored in a database.

  • Once the user has registered, the website will redirect the user to the login screen where the user can enter their email and password to log in.

Encryption of user data

  • The users data are encrypted using AES-128-ECB this is done using openssl_encrypt.

  • The users ID image is also encrypted and stored in the database. This done by first converting the image to a base64 longtext using a imageToBase64 function:

public function imageToBase64($imagePath) {
        try {
            $imageData = file_get_contents($imagePath);
            if ($imageData === false) {
                throw new Exception("Failed to read the image file.");
            }

            $base64Encoded = base64_encode($imageData);
            return $base64Encoded;
        } catch (Exception $e) {
            echo "An error occurred: " . $e->getMessage();
            return null;
        }
    }
  • Once this is done, the image longtext is encrypted using AES-128-ECB, RC4, and DES. Then the one encrypted with AES-128-ECB stored in the database.
// AES Encryption for ID card
    $IDAESstart = microtime(true);
    $imageBase64 = openssl_encrypt($imageBase64, $cipher, $secret);
    $IDAESend = microtime(true);
    $IDAEStime_taken = ($IDAESend - $IDAESstart) * 1000;
    echo "Time taken to encrypt ID with AES-128-ECB: " . $IDAEStime_taken . " ms";

    // RC4 Encryption for ID card
    $IDRC4start = microtime(true);
    $imageBase64rc4 = openssl_encrypt($imageBase64, $rc4, $rc4key);
    $IDRC4end = microtime(true);
    $IDRC4time_taken = ($IDRC4end - $IDRC4start) * 1000;
    echo "Time taken to encrypt ID with RC4: " . $IDRC4time_taken . " ms";;

    // DES Encryption for ID Card
    $IDDESstart = microtime(true);
    $imageBase64des = openssl_encrypt($imageBase64, $des, $deskey);
    $IDDESend = microtime(true);
    $IDDEStime_taken = ($IDDESend - $IDDESstart) * 1000;
    echo "Time taken to encrypt ID with DES-ECB: " . $IDDEStime_taken . " ms";
  • IDAESstart represents the start time before the file is encrpyted and IDEASend represents the end time after the file is encrypted with AES-128-ECB.
  • $IDAEStime_taken is calculated by subtracting IDAESstart from IDAESend. It is then multiplied by 1000 to obtain the time in ms.

AESvsRC4vsDES

  • As we can see, here is the comparison between the performance of the three encryption algorithms. As we can see DES was the fastest with RC4 being second fastest and AES being the slowest among the three. We still chose to use AES in this case as it is the most secure among the three.

Database:
UserDatabase UserDatabase

Analysis on Encryption Time

encryptiontime

  • In our encryption, it turns out that RC4 has the slowest encryption time. This occurs because PHP doesn't directly support this encryption algorithm. Because of this, we have to manually make this encryption type ourself. This is why RC4 has the slowest time in our website with average encryption time of 0.209 ms, it was followed by AES with an average encryption time of 43.089 ms and then DES with an average encryption time of 0.1411 ms

Analysis on Encryption Size

encryption size

  • We also have measured the size of the encrypted images, in this case, the image encrypted with RC4 has the largest size with an average size of 376056 bytes followed by AES with an an average size 250717.3333 bytes and then there is DES with an average size of 188043 bytes

Viewing user profile / data

  • After the user logs-in, they are able to view their data by going to the profile menu.
  • Once they click it, the website will redirect them to their profile where it will pull their data from the database and decrypt it using openssl_decrypt with the same cipher and key as it was encrypted. Their ID card image remains in base64 after the decryption as html supports displaying base64 images.

User profile web page:
UserProfile

DES-ECB Encryption:

$secret = hex2bin("1B6D4B4A5254AC");;
$iv = openssl_random_pseudo_bytes(8); // Generate a random IV
$paddedName = str_pad($request->name, 8, "\0"); // Pad the name to 8 bytes if needed
$encryptedName = openssl_encrypt($paddedName, 'des-ecb', $secret, OPENSSL_RAW_DATA, $iv);

// Store the encrypted name in the database
$encryptedName = base64_encode($iv . $encryptedName);

Animals::create([
    'name' => $encryptedName, // Store the encrypted name
    'center_id' => $request->center_id,
    'breed' => $request->breed,
    'age' => $request->age,
    'desc' => $request->desc,
    'image' => $imageBase64
]);
  • First decide on the secret key for encryption
  • Here, a variable $iv is defined, and it's assigned a random 8-byte value generated using the openssl_random_pseudo_bytes function. This is the Initialization Vector (IV) used for encryption
  • paddedName = str_pad($request->name, 8, "\0") This line creates a variable $paddedName. It takes the name field from the $request object, and if the length of the name is less than 8 characters, it pads it with null bytes ("\0") to make it exactly 8 bytes long
  • encryptedName = openssl_encrypt($paddedName, 'des-ecb', $secret, OPENSSL_RAW_DATA, $iv); This is to create variable to for the encrypted name and put the value in
  • Store the encrypted name in the database
  • Lastly, we put it on the table

File Downloading

  • The user is able to upload and download the files that they have uploaded

UserFiles

public function download($id) {
        $file = Files::find($id);

        if (!$file) {
            abort(404); // File not found
        }

        // Check if the file is a duplicate
        if ($file->isDuplicate) {
            // Return a view with a form to ask for the private key
            return view('files.request_key', ['file' => $file]);
        }

        // Fetch the encrypted file content and decrypt it
        $cipher = "AES-256-CBC";
        $aeskey = $file->secret;
        $options = 0;
        $iv = $file->iv;

        $decrypted_AESBase64 = openssl_decrypt($file->file_base64, $cipher, $aeskey, $options, $iv);
        $fileContent = base64_decode($decrypted_AESBase64);

        // Set headers for file download
        $headers = [
            'Content-Type' => 'application/octet-stream',
            'Content-Disposition' => 'attachment; filename=' . $file->filename,
        ];

        // Return the download response
        return response()->make($fileContent, 200, $headers);
    }

User Page & File Sharing

UsersPage

  • The user is able to view other users who are registerd to the website
  • Below their profiles theres a Request Files button. Clicking on it will make a request towards the requested_user for their files. And return a response that request has been created.

FileRequest

  • This creates a new table file_requests with the parameters requester_id, requested_id and has_access which has a default value of false.

FileRequestTable

public function store(Request $request, User $requested)
    {
        $user = Auth::user();

        $existingRequest = FileRequest::where('requested_id', $request->input('requested_id'))
            ->where('requester_id', $user->id)
            ->first();

            if (!$existingRequest) {
                $filerequest = FileRequest::create([
                    'requested_id' => $request->input('requested_id'),
                    'requester_id' => $user->id,
                    'has_access' => false,
                ]);

                return redirect('/users')->with('success', 'Successfully requested files!');
            } else {
                return redirect('/users')->with('error', 'File request already exists!');
            }
    }

User Inbox for File Requests & File Sharing

  • The user has an inbox page where the requests that they received are stored

UserInbox

  • The requested user can click on the Accept Request button and the file_requests table will be updated changing the has_access value from false to true.

requestedInbox

  • The requested_user's files will then be duplicated and modified so that their fileOwner is now the requester_user. During the duplication the files, the file will first be decrypted and then a new key will be generated to encrypt the duplicated files. Once this happens the encrypted files will then be stored and the newly generated key will be encrypted again by a master key that is stored in the env. The newly duplicated files will also have their isDuplicate value changed from false to true, This prevents shared files to be reshared again to other users.
  • The requester_user will be notified that the requested_user has given them access to the files via the inbox and the encrypted key private_key will also be sent to the inbox of the requester_user which will be used when downloading the shared files.

requesterFiles

public function update(Request $request, FileRequest $fileRequest)
    {
        $fileRequest->has_access = true;
        $fileRequest->save();

        // Fetch all files owned by the requested_id
        $files = Files::where('fileOwner', $fileRequest->requested_id)->get();

        // Define the cipher
        $cipher = "AES-256-CBC";
        $options = 0;

        // Generating a new secret
        $newAESkey = bin2hex(openssl_random_pseudo_bytes(16));
        // $AESkeykey = bin2hex(openssl_random_pseudo_bytes(16));
        $AESkeykey = env('AES_KEY_KEY');

        // Duplicate each file and change the fileOwner to the requester_id
        foreach ($files as $file) {
            //Checks if file is a duplicate and does not duplicate if isDuplicate value is true
            if(!$file->isDuplicate){
                $newFile = $file->replicate();
                $newFile->fileOwner = $fileRequest->requester_id;
                $newFile->isDuplicate = true;

                // Fetch the iv from the file
                $iv = $file->iv;
                // Decrypting the FileBase64
                $decryptedFileBase64 = openssl_decrypt($file->file_base64, $cipher, $file->secret, $options, $iv);
                // Re-encrypting the file with the new secret
                $encryptedFileBase64 = openssl_encrypt($decryptedFileBase64, $cipher,  $newAESkey, $options, $iv);

                $encrypedAESkey = openssl_encrypt($newAESkey, $cipher, $AESkeykey, $options, $iv);
                $AESkeyMessage = openssl_encrypt($encrypedAESkey, $cipher, $AESkeykey, $options, $iv);

                // Storing the encrpyted file and the new secret
                $newFile->file_base64 = $encryptedFileBase64;

                // secret is holding the value of the encrypted encrypted key
                $newFile->secret = $AESkeyMessage;

                $newFile->save();
            }
        }

        $requester = User::find($fileRequest->requester_id);
        $notification = "User A has accepted your request.";

        return redirect('/inbox')->with('success', 'You have accepted the file request.');
    }

Downloading Shared Files

  • The requester_user is able to download the files that have been shared to him by clicking the download button
  • If the file is a shared file it will promt the user to enter the private key that was generated when the file was shared.
  • After the private key is entered the key will be decrypted with the master key stored in the env and then the encrypted shared_file will be decrypted by the decrypted private key. The file will then be downloaded.

PrivateKeyPrompt

public function decryptWithKey(Request $request, $id) {
        $file = Files::find($id);

        if (!$file) {
            abort(404); // File not found
        }

        // Fetch the private key from the request
        $privateKey = $request->input('private_key');


        // Fetch the encrypted file content and decrypt it
        $cipher = "AES-256-CBC";
        $options = 0;
        $iv = $file->iv;
        $decryptedPrivateKey = openssl_decrypt($privateKey, $cipher, env('AES_KEY_KEY'), $options, $iv);

        $decrypted_AESBase64 = openssl_decrypt($file->file_base64, $cipher, $decryptedPrivateKey, $options, $iv);
        $fileContent = base64_decode($decrypted_AESBase64);

        // Set headers for file download
        $headers = [
            'Content-Type' => 'application/octet-stream',
            'Content-Disposition' => 'attachment; filename=' . $file->filename,
        ];

        // Return the download response
        return response()->make($fileContent, 200, $headers);
    }

Key Pair Generation

        // Generate two random prime numbers, p and q
        $p = findRandomPrime(10000, 20000);
        $q = findRandomPrime(10000, 20000);

        // Calculate n (modulus)
        $n = $p * $q;

        // Calculate phi(n) (Euler's totient function)
        $phiN = ($p - 1) * ($q - 1);

        // Find a public exponent (e)
        $e = findPublicExponent($phiN);

        // Calculate private exponent (d) using the modular multiplicative inverse
        $d = modInverse($e, $phiN);

        // Convert keys to PEM format
        $publicKeyPEM = "-----BEGIN CERTIFICATE REQUEST-----\n" .
            wordwrap(base64_encode(pack('N', $e) . pack('N', $n)), 64, "\n", true) .
            "\n-----END CERTIFICATE REQUEST-----\n";

        $privateKeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap(base64_encode(pack('N', $n) . pack('N', $e) . pack('N', $d)), 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----\n";

        // Read the contents of the certificate, private key, and public key
        $certificatePath = storage_path('app/certificates/Webhub.cer');
        $certificateContent = file_get_contents($certificatePath);

        // Combine the contents in the desired order
        $combinedContent = $certificateContent . $privateKeyPEM . $publicKeyPEM;

        // Path to the new combined certificate file in /storage/app/certificates
        $newCertificatePath = storage_path("app/certificates/{$username}.crt");

        // Save the combined content to the new certificate file
        file_put_contents($newCertificatePath, $combinedContent);
  • When a new account is created, a key pair is generated.
  • They key pair is in the PEM format.
  • The key pair is then combined with the certificate and then a new certificate is created named {$username}.crt.
  • It is then stored in app/certtificates.

Digitally Signing PDF files

  • In the navbar, a new nav that leads to the signature page is added.
  • Clicking this leads to a new page whre the user can upload a pdf and have it digitally signed.
  • After the pdf is uploaded, the pdf file will be digitally signed and then the newly digitally signed pdf will be downloaded.

DigitalSignature

DigitalSignatureCreate

ProofofSignature

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use PDF;
use TCPDF;
use setasign\Fpdi\Tcpdf\Fpdi;

class DigitalSignatureController extends Controller
{
    public function downloadPdf(Request $request){
        $cipher = "AES-256-CBC";
        $options = 0;
        $iv = str_repeat("0", openssl_cipher_iv_length($cipher));
        $decryptedEmail = openssl_decrypt(Auth::user()->email, $cipher, Auth::user()->keyAES, $options, $iv);
        $username = Auth::user()->username;

        $certificate = 'file://'.base_path().'/storage/app/certificates/Webhub.crt';
        // $certificate = base_path("/storage/app/certificates/{$username}.crt");

        // signature information
        $info = array(
            'Name' => Auth::user()->username,
            'Location' => 'Indonesia',
            'Reason' => 'Generate Digitally Signed PDF',
            'ContactInfo' => $decryptedEmail,
        );

        $request->validate([
            'file' => 'required|file|mimes:pdf',
        ]);
        $file = $request->file('file');
        Log::info('Processing file: ' . $file->getClientOriginalName());


        $pdf = new Fpdi(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
        $pdf->AddPage();

        // Set signature
        $pdf->setSignature($certificate, $certificate, 'PDFSecurity', '', 2, $info);
        // Add content to the PDF
        $pdf->setSourceFile($file->getRealPath());
        $tplIdx = $pdf->importPage(1);
        $pdf->useTemplate($tplIdx, 10, 10, 200, 200);

        // Output the PDF
        $pdf->Output(public_path($file->getClientOriginalName().'-digitally-signed.pdf'), 'D');
    }
}

About

A simple website with register and login features that encrypts the user data

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages