From 144044a5516141417012750f469aa7f3becca12d Mon Sep 17 00:00:00 2001 From: netkas Date: Mon, 9 Sep 2024 19:18:39 -0400 Subject: [PATCH] Improved Cryptography section and included samples in Java, C, PHP And Python --- README.md | 70 +++++++++++++++------- examples/cryptography.c | 69 +++++++++++++++++++++ examples/cryptography.java | 69 +++++++++++++++++++++ examples/cryptography.php | 120 +++++++++++++++++++++++++++++++++++++ examples/cryptography.py | 101 +++++++++++++++++++++++++++++++ 5 files changed, 407 insertions(+), 22 deletions(-) create mode 100644 examples/cryptography.c create mode 100644 examples/cryptography.java create mode 100644 examples/cryptography.php create mode 100644 examples/cryptography.py diff --git a/README.md b/README.md index dd64715..5fd9ec4 100644 --- a/README.md +++ b/README.md @@ -69,14 +69,16 @@ This project is licensed under GNU Free Documentation License v1.3, see the [LIC * [Signing and Signature Verification](#signing-and-signature-verification) * [Temporary Signature & Verification](#temporary-signature--verification) * [Encryption and Decryption](#encryption-and-decryption) + * [Error Handling](#error-handling) * [Methods](#methods) * [generateKeyPair()](#generatekeypair) * [signContent(content: String, privateKey: String): String](#signcontentcontent-string-privatekey-string-string) * [verifyContent(content: String, signature: String, publicKey: String): Boolean](#verifycontentcontent-string-signature-string-publickey-string-boolean) * [temporarySignContent(content: String, privateKey: String): String](#temporarysigncontentcontent-string-privatekey-string-string) - * [verifyTemporarySignature(content: String, signature: String, publicKey: String, frames: Integer): Boolean](#verifytemporarysignaturecontent-string-signature-string-publickey-string-frames-integer-boolean) + * [verifyTemporarySignature(content: String, signature: String, publicKey: String, frames: Integer): Boolean](#verifytemporarysignaturecontent-string-signature-string-publickey-string-frames-integer-boolean) * [encryptContent(content: String, publicKey: String): String](#encryptcontentcontent-string-publickey-string-string) * [decryptContent(ciphertext: String, privateKey: String): String](#decryptcontentciphertext-string-privatekey-string-string) + * [Example Implementations](#example-implementations) * [Authentication](#authentication) * [First-Level Authentication](#first-level-authentication) * [Password (LOGIN)](#password-login) @@ -350,21 +352,24 @@ The fields in the error response object are as follows: ## Cryptography -Cryptography is a crucial part of the Socialbox standard, it serves many purposes such as session integrity to prevent -session hijacking, data integrity to prevent data tampering, and confidentiality to prevent data eavesdropping. To keep -things simple, every Socialbox client & server must implement the same cryptographic methods to ensure compatibility -with other systems. Mainly, the Socialbox standard uses the following cryptographic methods: +Cryptography is a crucial part of the Socialbox standard. It serves many purposes such as session integrity to prevent +session hijacking, data integrity to prevent data tampering, and confidentiality to prevent data eavesdropping. Every +Socialbox client & server must implement the same cryptographic methods to ensure compatibility with other systems. +Mainly, the Socialbox standard uses the following cryptographic methods: * **RSA**: Used for asymmetric encryption and decryption. * **SHA256**: Used for hashing data and generating secure signatures. * **Base64 Encoding**: All binary data (such as keys, signatures, and encrypted data) is encoded into Base64 for safe transmission. + > Note: Base64 encoding is necessary because binary data cannot be safely transmitted in certain contexts like URLs or + JSON. Base64 converts binary data into a text-safe format. + ### Key Generation -Key generation is the process of generating cryptographic keys for encryption and decryption. In Socialbox, RSA keys -are used for asymmetric encryption and decryption. The key generation process involves generating a public key and a -private key. The public key can be shared with others to encrypt/verify data, while the private key is kept secret and -used to decrypt/sign data. These are the following conditions for key generation: +Key generation is the process of generating cryptographic keys for encryption and decryption. In Socialbox, RSA keys are +used for asymmetric encryption and decryption. The key generation process involves generating a public key and a private +key. The public key can be shared with others to encrypt/verify data, while the private key is kept secret and used to +decrypt/sign data. These are the following conditions for key generation: * **Key Size**: The key size must be 2048 bits. * **Algorithm**: The algorithm used for key generation must be RSA. @@ -399,10 +404,9 @@ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQ... ### Signing and Signature Verification -Signing is the process of generating a digital signature for a message using a private key. The signature can be -verified by others using the corresponding public key to ensure the message's integrity and authenticity. In Socialbox, -signing and signature verification are used to ensure the integrity of data exchanged between peers. The following -conditions apply to signing and signature verification: +Signing is the process of generating a digital signature for a message using a private key. The signature can be verified +by others using the corresponding public key to ensure the message's integrity and authenticity. In Socialbox, signing +and signature verification are used to ensure the integrity of data exchanged between peers. * **Algorithm**: RSA with SHA256 hash function (SHA256withRSA) * **Padding**: PKCS#1 v1.5 padding for signatures. @@ -432,9 +436,10 @@ echo -n "Hello, World!" | openssl dgst -sha256 -verify public.der -signature sig ### Temporary Signature & Verification -Temporary signatures work the same way as regular signatures, but the keypair used to sign the data is temporary and -is only used for a block of time, usually within 60 seconds. Temporary signatures are used to ensure the integrity of -data exchanged between peers within a short period. The following conditions apply to temporary signatures: +Temporary signatures work similarly to regular signatures, but the keypair used to sign the data is temporary and only +valid for a limited time (typically 60 seconds). This feature ensures the integrity of data exchanged between peers +within a short time window, adding protection against replay attacks. To ensure time synchronization, developers should +use NTP (Network Time Protocol). * **Algorithm**: RSA with SHA256 hash function (SHA256withRSA) * **Padding**: PKCS#1 v1.5 for signatures. @@ -490,10 +495,9 @@ Hello, World!|28765051 ### Encryption and Decryption -Encryption is the process of converting plaintext data into ciphertext using a public key, while decryption is the -process of converting ciphertext back into plaintext using the corresponding private key. In Socialbox, encryption and -decryption are used to ensure the confidentiality of data exchanged between peers. The following conditions apply to -encryption and decryption: +Encryption is the process of converting plaintext data into ciphertext using a public key, while decryption converts +ciphertext back into plaintext using the corresponding private key. In Socialbox, encryption and decryption ensure the +confidentiality of data exchanged between peers. * **Algorithm**: RSA with OAEP (Optimal Asymmetric Encryption Padding) * **Hash**: SHA256 @@ -517,6 +521,13 @@ openssl rsautl -decrypt -oaep -inkey private_base64.txt -in ciphertext.bin > Note: In this example, the produced ciphertext is binary data, but if you want to transmit the ciphertext over the network, you must encode it using Base64 encoding. + > + +### Error Handling + +For real-world implementations, error handling should be a priority. For example, signature verification and decryption +can fail if the data is tampered with, keys don’t match, or the data has expired. Ensure that invalid signatures or +decryption errors are caught and handled appropriately to avoid security vulnerabilities ### Methods @@ -525,12 +536,13 @@ The following methods should be implemented by all Socialbox servers and clients #### generateKeyPair() ![generateKeyPair Diagram](images/generateKeyPair.png "generateKeyPair Diagram") -Generates a new RSA key pair for encryption and decryption, returns the public and private keys in Base64 encoding. +Generates a new RSA key pair for encryption and decryption, returning the public and private keys in Base64 encoding. #### signContent(content: String, privateKey: String): String ![signContent Diagram](images/signContent.png "signContent Diagram") Signs the content using the private key and returns the signature in Base64 encoding. +The content is expected to be a UTF-8 encoded string. #### verifyContent(content: String, signature: String, publicKey: String): Boolean @@ -542,7 +554,7 @@ Verifies the signature of the content using the public key and returns true if t ![temporarySignContent Diagram](images/temporarySignContent.png "temporarySignContent Diagram") Signs the content using a temporary private key and returns the temporary signature in Base64 encoding. -### verifyTemporarySignature(content: String, signature: String, publicKey: String, frames: Integer): Boolean +#### verifyTemporarySignature(content: String, signature: String, publicKey: String, frames: Integer): Boolean ![verifyTemproarySignature Diagram](images/verifyTemproarySignature.png "verifyTemporarySignature") Verifies the temporary signature of the content using the public key and returns true if the signature is valid @@ -558,6 +570,20 @@ Encrypts the content using the public key and returns the ciphertext in Base64 e ![decryptContent Diagram](images/decryptContent.png "decryptContent Diagram") Decrypts the ciphertext using the private key and returns the plaintext content. +### Example Implementations + +This standard provides examples of how to implement the cryptographic methods using OpenSSL or any other cryptographic +library that supports RSA encryption and decryption. The examples demonstrate how to generate RSA key pairs, sign and +verify content, and encrypt and decrypt data using RSA. + + > Note: The examples provided are for demonstration purposes only. Developers should use a secure cryptographic library + and follow best practices to ensure the security of their implementations. + + - [Python](examples/cryptography.py) + - [Java](examples/Cryptography.java) + - [PHP](examples/cryptography.php) + - [C](examples/cryptography.c) + ------------------------------------------------------------------------------------------------------------------------ # Authentication diff --git a/examples/cryptography.c b/examples/cryptography.c new file mode 100644 index 0000000..b60b50b --- /dev/null +++ b/examples/cryptography.c @@ -0,0 +1,69 @@ +import java.security.*; +import java.util.Base64; +import javax.crypto.Cipher; +import java.time.Instant; + +public class Cryptography { + + // Generate a new RSA key pair + public static KeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + return keyGen.generateKeyPair(); + } + + // Sign the content using the private key + public static String signContent(String content, PrivateKey privateKey) throws Exception { + Signature privateSignature = Signature.getInstance("SHA256withRSA"); + privateSignature.initSign(privateKey); + privateSignature.update(content.getBytes("UTF-8")); + byte[] signature = privateSignature.sign(); + return Base64.getEncoder().encodeToString(signature); + } + + // Verify the signature of the content using the public key + public static boolean verifyContent(String content, String signature, PublicKey publicKey) throws Exception { + Signature publicSignature = Signature.getInstance("SHA256withRSA"); + publicSignature.initVerify(publicKey); + publicSignature.update(content.getBytes("UTF-8")); + byte[] signatureBytes = Base64.getDecoder().decode(signature); + return publicSignature.verify(signatureBytes); + } + + // Sign the content with a temporary signature based on time blocks + public static String temporarySignContent(String content, PrivateKey privateKey, int frames) throws Exception { + long timeBlock = Instant.now().getEpochSecond() / 60; + String contentWithTime = content + "|" + timeBlock; + return signContent(contentWithTime, privateKey); + } + + // Verify a temporary signature with time blocks + public static boolean verifyTemporarySignature(String content, String signature, PublicKey publicKey, int frames) throws Exception { + long timeBlock = Instant.now().getEpochSecond() / 60; + + for (int i = 0; i < frames; i++) { + String contentWithTime = content + "|" + (timeBlock - i); + if (verifyContent(contentWithTime, signature, publicKey)) { + return true; + } + } + + return false; + } + + // Encrypt the content using the public key + public static String encryptContent(String content, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] ciphertext = cipher.doFinal(content.getBytes("UTF-8")); + return Base64.getEncoder().encodeToString(ciphertext); + } + + // Decrypt the content using the private key + public static String decryptContent(String ciphertext, PrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext)); + return new String(decryptedBytes, "UTF-8"); + } +} diff --git a/examples/cryptography.java b/examples/cryptography.java new file mode 100644 index 0000000..b60b50b --- /dev/null +++ b/examples/cryptography.java @@ -0,0 +1,69 @@ +import java.security.*; +import java.util.Base64; +import javax.crypto.Cipher; +import java.time.Instant; + +public class Cryptography { + + // Generate a new RSA key pair + public static KeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + return keyGen.generateKeyPair(); + } + + // Sign the content using the private key + public static String signContent(String content, PrivateKey privateKey) throws Exception { + Signature privateSignature = Signature.getInstance("SHA256withRSA"); + privateSignature.initSign(privateKey); + privateSignature.update(content.getBytes("UTF-8")); + byte[] signature = privateSignature.sign(); + return Base64.getEncoder().encodeToString(signature); + } + + // Verify the signature of the content using the public key + public static boolean verifyContent(String content, String signature, PublicKey publicKey) throws Exception { + Signature publicSignature = Signature.getInstance("SHA256withRSA"); + publicSignature.initVerify(publicKey); + publicSignature.update(content.getBytes("UTF-8")); + byte[] signatureBytes = Base64.getDecoder().decode(signature); + return publicSignature.verify(signatureBytes); + } + + // Sign the content with a temporary signature based on time blocks + public static String temporarySignContent(String content, PrivateKey privateKey, int frames) throws Exception { + long timeBlock = Instant.now().getEpochSecond() / 60; + String contentWithTime = content + "|" + timeBlock; + return signContent(contentWithTime, privateKey); + } + + // Verify a temporary signature with time blocks + public static boolean verifyTemporarySignature(String content, String signature, PublicKey publicKey, int frames) throws Exception { + long timeBlock = Instant.now().getEpochSecond() / 60; + + for (int i = 0; i < frames; i++) { + String contentWithTime = content + "|" + (timeBlock - i); + if (verifyContent(contentWithTime, signature, publicKey)) { + return true; + } + } + + return false; + } + + // Encrypt the content using the public key + public static String encryptContent(String content, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] ciphertext = cipher.doFinal(content.getBytes("UTF-8")); + return Base64.getEncoder().encodeToString(ciphertext); + } + + // Decrypt the content using the private key + public static String decryptContent(String ciphertext, PrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext)); + return new String(decryptedBytes, "UTF-8"); + } +} diff --git a/examples/cryptography.php b/examples/cryptography.php new file mode 100644 index 0000000..9d24f94 --- /dev/null +++ b/examples/cryptography.php @@ -0,0 +1,120 @@ + OPENSSL_KEYTYPE_RSA, + "private_key_bits" => 2048, + ]; + $res = openssl_pkey_new($config); + + // Extract the private key to a variable + openssl_pkey_export($res, $privateKey); + + // Extract the public key from the private key + $publicKeyDetails = openssl_pkey_get_details($res); + $publicKey = $publicKeyDetails['key']; + + // Return both keys in Base64 encoding (without PEM headers) + return [ + 'privateKey' => base64_encode($privateKey), + 'publicKey' => base64_encode($publicKey), + ]; + } + + // Sign the content using the private key + public static function signContent($content, $privateKey) + { + // Decode the Base64 private key + $privateKeyDecoded = openssl_pkey_get_private(base64_decode($privateKey)); + + // Sign the content + openssl_sign($content, $signature, $privateKeyDecoded, OPENSSL_ALGO_SHA256); + + // Return the signature in Base64 encoding + return base64_encode($signature); + } + + // Verify the signature of the content using the public key + public static function verifyContent($content, $signature, $publicKey) + { + // Decode the Base64 public key + $publicKeyDecoded = openssl_pkey_get_public(base64_decode($publicKey)); + + // Decode the Base64 signature + $signatureDecoded = base64_decode($signature); + + // Verify the signature + $result = openssl_verify($content, $signatureDecoded, $publicKeyDecoded, OPENSSL_ALGO_SHA256); + + // Return true if the signature is valid, false otherwise + return $result === 1; + } + + // Sign the content with a temporary signature based on time blocks + public static function temporarySignContent($content, $privateKey, $frames = 1) + { + // Calculate the current time block + $timeBlock = intdiv(time(), 60); + + // Append the time block to the content + $contentWithTime = $content . '|' . $timeBlock; + + // Sign the content with the time block + return self::signContent($contentWithTime, $privateKey); + } + + // Verify a temporary signature with time blocks + public static function verifyTemporarySignature($content, $signature, $publicKey, $frames = 1) + { + // Calculate the current time block + $timeBlock = intdiv(time(), 60); + + // Check for each time block within the frame range + for ($i = 0; $i < $frames; $i++) { + // Append the time block to the content + $contentWithTime = $content . '|' . ($timeBlock - $i); + + // Verify the signature + if (self::verifyContent($contentWithTime, $signature, $publicKey)) { + return true; + } + } + + // Return false if none of the frames matched + return false; + } + + // Encrypt the content using the public key + public static function encryptContent($content, $publicKey) + { + // Decode the Base64 public key + $publicKeyDecoded = openssl_pkey_get_public(base64_decode($publicKey)); + + // Encrypt the content + openssl_public_encrypt($content, $ciphertext, $publicKeyDecoded, OPENSSL_PKCS1_OAEP_PADDING); + + // Return the ciphertext in Base64 encoding + return base64_encode($ciphertext); + } + + // Decrypt the content using the private key + public static function decryptContent($ciphertext, $privateKey) + { + // Decode the Base64 private key + $privateKeyDecoded = openssl_pkey_get_private(base64_decode($privateKey)); + + // Decode the Base64 ciphertext + $ciphertextDecoded = base64_decode($ciphertext); + + // Decrypt the content + openssl_private_decrypt($ciphertextDecoded, $plaintext, $privateKeyDecoded, OPENSSL_PKCS1_OAEP_PADDING); + + // Return the decrypted plaintext + return $plaintext; + } + } diff --git a/examples/cryptography.py b/examples/cryptography.py new file mode 100644 index 0000000..0a32a37 --- /dev/null +++ b/examples/cryptography.py @@ -0,0 +1,101 @@ +import base64 +import time +from Crypto.PublicKey import RSA +from Crypto.Signature import pkcs1_15 +from Crypto.Hash import SHA256 +from Crypto.Cipher import PKCS1_OAEP + +class Cryptography: + + @staticmethod + def generate_key_pair(): + # Generate RSA key pair + key = RSA.generate(2048) + private_key = key.export_key(format='DER') + public_key = key.publickey().export_key(format='DER') + + # Return keys in Base64 encoding + return { + 'privateKey': base64.b64encode(private_key).decode('utf-8'), + 'publicKey': base64.b64encode(public_key).decode('utf-8') + } + + @staticmethod + def sign_content(content, private_key_base64): + # Decode the Base64 private key + private_key = RSA.import_key(base64.b64decode(private_key_base64)) + + # Hash the content using SHA256 + h = SHA256.new(content.encode('utf-8')) + + # Sign the hash + signature = pkcs1_15.new(private_key).sign(h) + + # Return the signature in Base64 encoding + return base64.b64encode(signature).decode('utf-8') + + @staticmethod + def verify_content(content, signature_base64, public_key_base64): + # Decode the Base64 public key and signature + public_key = RSA.import_key(base64.b64decode(public_key_base64)) + signature = base64.b64decode(signature_base64) + + # Hash the content + h = SHA256.new(content.encode('utf-8')) + + try: + # Verify the signature + pkcs1_15.new(public_key).verify(h, signature) + return True + except (ValueError, TypeError): + return False + + @staticmethod + def temporary_sign_content(content, private_key_base64, frames=1): + # Calculate the current time block + time_block = int(time.time() // 60) + + # Append the time block to the content + content_with_time = f"{content}|{time_block}" + + # Sign the content with the time block + return Cryptography.sign_content(content_with_time, private_key_base64) + + @staticmethod + def verify_temporary_signature(content, signature_base64, public_key_base64, frames=1): + # Calculate the current time block + time_block = int(time.time() // 60) + + # Check for each time block within the frame range + for i in range(frames): + content_with_time = f"{content}|{time_block - i}" + if Cryptography.verify_content(content_with_time, signature_base64, public_key_base64): + return True + + return False + + @staticmethod + def encrypt_content(content, public_key_base64): + # Decode the Base64 public key + public_key = RSA.import_key(base64.b64decode(public_key_base64)) + + # Encrypt the content using RSA-OAEP + cipher = PKCS1_OAEP.new(public_key) + ciphertext = cipher.encrypt(content.encode('utf-8')) + + # Return the ciphertext in Base64 encoding + return base64.b64encode(ciphertext).decode('utf-8') + + @staticmethod + def decrypt_content(ciphertext_base64, private_key_base64): + # Decode the Base64 private key and ciphertext + private_key = RSA.import_key(base64.b64decode(private_key_base64)) + ciphertext = base64.b64decode(ciphertext_base64) + + # Decrypt the content using RSA-OAEP + cipher = PKCS1_OAEP.new(private_key) + plaintext = cipher.decrypt(ciphertext) + + # Return the decrypted content + return plaintext.decode('utf-8') +