Created
June 21, 2025 19:56
-
-
Save viperadnan-git/c046926899438cbf3061d2057a9b8fed to your computer and use it in GitHub Desktop.
Encrypt/Decrypt Class for Web and Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>JavaScript Crypt Class</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
max-width: 800px; | |
margin: 0 auto; | |
padding: 20px; | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
min-height: 100vh; | |
} | |
.container { | |
background: white; | |
border-radius: 10px; | |
padding: 30px; | |
box-shadow: 0 10px 30px rgba(0,0,0,0.2); | |
} | |
h1 { | |
color: #333; | |
text-align: center; | |
margin-bottom: 30px; | |
} | |
.form-group { | |
margin-bottom: 20px; | |
} | |
label { | |
display: block; | |
margin-bottom: 5px; | |
font-weight: bold; | |
color: #555; | |
} | |
input, textarea { | |
width: 100%; | |
padding: 10px; | |
border: 2px solid #ddd; | |
border-radius: 5px; | |
font-size: 14px; | |
box-sizing: border-box; | |
} | |
textarea { | |
height: 100px; | |
resize: vertical; | |
} | |
button { | |
background: linear-gradient(45deg, #667eea, #764ba2); | |
color: white; | |
border: none; | |
padding: 12px 24px; | |
border-radius: 5px; | |
cursor: pointer; | |
font-size: 16px; | |
margin-right: 10px; | |
margin-bottom: 10px; | |
transition: transform 0.2s; | |
} | |
button:hover { | |
transform: translateY(-2px); | |
} | |
.result { | |
background: #f8f9fa; | |
border: 1px solid #e9ecef; | |
border-radius: 5px; | |
padding: 15px; | |
margin-top: 20px; | |
word-break: break-all; | |
} | |
.error { | |
background: #f8d7da; | |
border: 1px solid #f5c6cb; | |
color: #721c24; | |
} | |
.success { | |
background: #d4edda; | |
border: 1px solid #c3e6cb; | |
color: #155724; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>JavaScript Crypt Class Demo</h1> | |
<div class="form-group"> | |
<label for="passphrase">Passphrase:</label> | |
<input type="password" id="passphrase" placeholder="Enter your passphrase"> | |
</div> | |
<div class="form-group"> | |
<label for="plaintext">Text to Encrypt:</label> | |
<textarea id="plaintext" placeholder="Enter text to encrypt"></textarea> | |
</div> | |
<div class="form-group"> | |
<label for="encrypted">Encrypted Text (hex_code-iv):</label> | |
<textarea id="encrypted" placeholder="Enter encrypted text to decrypt"></textarea> | |
</div> | |
<button onclick="encryptText()">Encrypt</button> | |
<button onclick="decryptText()">Decrypt</button> | |
<button onclick="testCrypt()">Test Example</button> | |
<div id="result"></div> | |
<div style="margin-top: 30px; padding: 20px; background: #f8f9fa; border-radius: 5px;"> | |
<h3>Code:</h3> | |
<pre style="background: #2d3748; color: #e2e8f0; padding: 15px; border-radius: 5px; overflow-x: auto; font-size: 12px;"> | |
class Crypt { | |
constructor(passphrase) { | |
this.passphrase = passphrase; | |
} | |
async deriveKey(salt) { | |
const encoder = new TextEncoder(); | |
const keyMaterial = await crypto.subtle.importKey( | |
'raw', | |
encoder.encode(this.passphrase), | |
{ name: 'PBKDF2' }, | |
false, | |
['deriveKey'] | |
); | |
return await crypto.subtle.deriveKey( | |
{ | |
name: 'PBKDF2', | |
salt: salt, | |
iterations: 100000, | |
hash: 'SHA-256' | |
}, | |
keyMaterial, | |
{ name: 'AES-GCM', length: 256 }, | |
false, | |
['encrypt', 'decrypt'] | |
); | |
} | |
async encrypt(text) { | |
const encoder = new TextEncoder(); | |
const data = encoder.encode(text); | |
const salt = crypto.getRandomValues(new Uint8Array(16)); | |
const iv = crypto.getRandomValues(new Uint8Array(12)); | |
const key = await this.deriveKey(salt); | |
const encrypted = await crypto.subtle.encrypt( | |
{ name: 'AES-GCM', iv: iv }, | |
key, | |
data | |
); | |
const combined = new Uint8Array(salt.length + encrypted.byteLength); | |
combined.set(salt); | |
combined.set(new Uint8Array(encrypted), salt.length); | |
const hexCode = Array.from(combined) | |
.map(b => b.toString(16).padStart(2, '0')) | |
.join(''); | |
const ivHex = Array.from(iv) | |
.map(b => b.toString(16).padStart(2, '0')) | |
.join(''); | |
return `${hexCode}-${ivHex}`; | |
} | |
async decrypt(encryptedText) { | |
const [hexCode, ivHex] = encryptedText.split('-'); | |
if (!hexCode || !ivHex) { | |
throw new Error('Invalid encrypted text format'); | |
} | |
const combined = new Uint8Array( | |
hexCode.match(/.{2}/g).map(byte => parseInt(byte, 16)) | |
); | |
const iv = new Uint8Array( | |
ivHex.match(/.{2}/g).map(byte => parseInt(byte, 16)) | |
); | |
const salt = combined.slice(0, 16); | |
const encrypted = combined.slice(16); | |
const key = await this.deriveKey(salt); | |
const decrypted = await crypto.subtle.decrypt( | |
{ name: 'AES-GCM', iv: iv }, | |
key, | |
encrypted | |
); | |
const decoder = new TextDecoder(); | |
return decoder.decode(decrypted); | |
} | |
} | |
</pre> | |
</div> | |
</div> | |
<script> | |
class Crypt { | |
constructor(passphrase) { | |
this.passphrase = passphrase; | |
} | |
async deriveKey(salt) { | |
const encoder = new TextEncoder(); | |
const keyMaterial = await crypto.subtle.importKey( | |
'raw', | |
encoder.encode(this.passphrase), | |
{ name: 'PBKDF2' }, | |
false, | |
['deriveKey'] | |
); | |
return await crypto.subtle.deriveKey( | |
{ | |
name: 'PBKDF2', | |
salt: salt, | |
iterations: 100000, | |
hash: 'SHA-256' | |
}, | |
keyMaterial, | |
{ name: 'AES-GCM', length: 256 }, | |
false, | |
['encrypt', 'decrypt'] | |
); | |
} | |
async encrypt(text) { | |
const encoder = new TextEncoder(); | |
const data = encoder.encode(text); | |
const salt = crypto.getRandomValues(new Uint8Array(16)); | |
const iv = crypto.getRandomValues(new Uint8Array(12)); | |
const key = await this.deriveKey(salt); | |
const encrypted = await crypto.subtle.encrypt( | |
{ name: 'AES-GCM', iv: iv }, | |
key, | |
data | |
); | |
// Combine salt and encrypted data | |
const combined = new Uint8Array(salt.length + encrypted.byteLength); | |
combined.set(salt); | |
combined.set(new Uint8Array(encrypted), salt.length); | |
// Convert to hex | |
const hexCode = Array.from(combined) | |
.map(b => b.toString(16).padStart(2, '0')) | |
.join(''); | |
const ivHex = Array.from(iv) | |
.map(b => b.toString(16).padStart(2, '0')) | |
.join(''); | |
return `${hexCode}-${ivHex}`; | |
} | |
async decrypt(encryptedText) { | |
const [hexCode, ivHex] = encryptedText.split('-'); | |
if (!hexCode || !ivHex) { | |
throw new Error('Invalid encrypted text format'); | |
} | |
// Convert hex back to bytes | |
const combined = new Uint8Array( | |
hexCode.match(/.{2}/g).map(byte => parseInt(byte, 16)) | |
); | |
const iv = new Uint8Array( | |
ivHex.match(/.{2}/g).map(byte => parseInt(byte, 16)) | |
); | |
// Extract salt and encrypted data | |
const salt = combined.slice(0, 16); | |
const encrypted = combined.slice(16); | |
const key = await this.deriveKey(salt); | |
const decrypted = await crypto.subtle.decrypt( | |
{ name: 'AES-GCM', iv: iv }, | |
key, | |
encrypted | |
); | |
const decoder = new TextDecoder(); | |
return decoder.decode(decrypted); | |
} | |
} | |
function showResult(message, isError = false) { | |
const resultDiv = document.getElementById('result'); | |
resultDiv.className = `result ${isError ? 'error' : 'success'}`; | |
resultDiv.textContent = message; | |
} | |
async function encryptText() { | |
try { | |
const passphrase = document.getElementById('passphrase').value; | |
const plaintext = document.getElementById('plaintext').value; | |
if (!passphrase || !plaintext) { | |
showResult('Please enter both passphrase and text to encrypt', true); | |
return; | |
} | |
const crypt = new Crypt(passphrase); | |
const encrypted = await crypt.encrypt(plaintext); | |
document.getElementById('encrypted').value = encrypted; | |
showResult(`Encrypted: ${encrypted}`); | |
} catch (error) { | |
showResult(`Encryption error: ${error.message}`, true); | |
} | |
} | |
async function decryptText() { | |
try { | |
const passphrase = document.getElementById('passphrase').value; | |
const encrypted = document.getElementById('encrypted').value; | |
if (!passphrase || !encrypted) { | |
showResult('Please enter both passphrase and encrypted text', true); | |
return; | |
} | |
const crypt = new Crypt(passphrase); | |
const decrypted = await crypt.decrypt(encrypted); | |
document.getElementById('plaintext').value = decrypted; | |
showResult(`Decrypted: ${decrypted}`); | |
} catch (error) { | |
showResult(`Decryption error: ${error.message}`, true); | |
} | |
} | |
async function testCrypt() { | |
try { | |
const testPassphrase = 'my-secret-passphrase-123'; | |
const testText = 'Hello, World! This is a test message.'; | |
document.getElementById('passphrase').value = testPassphrase; | |
document.getElementById('plaintext').value = testText; | |
const crypt = new Crypt(testPassphrase); | |
const encrypted = await crypt.encrypt(testText); | |
const decrypted = await crypt.decrypt(encrypted); | |
document.getElementById('encrypted').value = encrypted; | |
if (decrypted === testText) { | |
showResult(`Test successful! Encrypted: ${encrypted.substring(0, 50)}...`); | |
} else { | |
showResult('Test failed: Decrypted text does not match original', true); | |
} | |
} catch (error) { | |
showResult(`Test error: ${error.message}`, true); | |
} | |
} | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import hashlib | |
import os | |
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC | |
from cryptography.hazmat.primitives import hashes | |
from cryptography.hazmat.backends import default_backend | |
class Crypt: | |
def __init__(self, passphrase): | |
""" | |
Initialize the Crypt class with a passphrase | |
Args: | |
passphrase (str): The passphrase used for encryption/decryption | |
""" | |
if not isinstance(passphrase, str): | |
raise ValueError("Passphrase must be a string") | |
self.passphrase = passphrase.encode('utf-8') | |
def _derive_key(self, salt): | |
""" | |
Derive a 256-bit key from the passphrase using PBKDF2 | |
Args: | |
salt (bytes): Salt for key derivation | |
Returns: | |
bytes: 32-byte derived key | |
""" | |
kdf = PBKDF2HMAC( | |
algorithm=hashes.SHA256(), | |
length=32, # 256 bits | |
salt=salt, | |
iterations=100000, | |
backend=default_backend() | |
) | |
return kdf.derive(self.passphrase) | |
def encrypt(self, text): | |
""" | |
Encrypt text using AES-256-GCM | |
Args: | |
text (str): The text to encrypt | |
Returns: | |
str: Encrypted text in format (hex_code)-(iv) | |
""" | |
if not isinstance(text, str): | |
raise ValueError("Text must be a string") | |
# Generate random salt and IV | |
salt = os.urandom(16) # 128-bit salt | |
iv = os.urandom(12) # 96-bit IV for GCM mode | |
# Derive key from passphrase | |
key = self._derive_key(salt) | |
# Create cipher and encrypt | |
cipher = Cipher( | |
algorithms.AES(key), | |
modes.GCM(iv), | |
backend=default_backend() | |
) | |
encryptor = cipher.encryptor() | |
# Encrypt the text | |
plaintext = text.encode('utf-8') | |
ciphertext = encryptor.update(plaintext) + encryptor.finalize() | |
# Get the authentication tag | |
auth_tag = encryptor.tag | |
# Combine salt, ciphertext, and auth_tag | |
combined = salt + ciphertext + auth_tag | |
# Convert to hex | |
hex_code = combined.hex() | |
iv_hex = iv.hex() | |
return f"{hex_code}-{iv_hex}" | |
def decrypt(self, encrypted_text): | |
""" | |
Decrypt text that was encrypted with the encrypt method | |
Args: | |
encrypted_text (str): Encrypted text in format (hex_code)-(iv) | |
Returns: | |
str: Decrypted plaintext | |
""" | |
if not isinstance(encrypted_text, str): | |
raise ValueError("Encrypted text must be a string") | |
try: | |
# Split the encrypted text | |
hex_code, iv_hex = encrypted_text.split('-') | |
# Convert hex back to bytes | |
combined = bytes.fromhex(hex_code) | |
iv = bytes.fromhex(iv_hex) | |
# Extract components | |
salt = combined[:16] # First 16 bytes are salt | |
auth_tag = combined[-16:] # Last 16 bytes are auth tag | |
ciphertext = combined[16:-16] # Middle part is ciphertext | |
# Derive key from passphrase | |
key = self._derive_key(salt) | |
# Create cipher and decrypt | |
cipher = Cipher( | |
algorithms.AES(key), | |
modes.GCM(iv, auth_tag), | |
backend=default_backend() | |
) | |
decryptor = cipher.decryptor() | |
# Decrypt the ciphertext | |
plaintext = decryptor.update(ciphertext) + decryptor.finalize() | |
return plaintext.decode('utf-8') | |
except ValueError as e: | |
raise ValueError(f"Invalid encrypted text format: {e}") | |
except Exception as e: | |
raise ValueError(f"Decryption failed: {e}") | |
# Example usage and testing | |
if __name__ == "__main__": | |
# Test the Crypt class | |
print("Testing Python Crypt Class") | |
print("=" * 40) | |
# Initialize with a passphrase | |
passphrase = "my-secret-passphrase-123" | |
crypt = Crypt(passphrase) | |
# Test text | |
original_text = "Hello, World! This is a test message with special characters: àáâãäåæçèé" | |
print(f"Original text: {original_text}") | |
# Encrypt | |
try: | |
encrypted = crypt.encrypt(original_text) | |
print(f"Encrypted: {encrypted}") | |
print(f"Encrypted length: {len(encrypted)} characters") | |
# Decrypt | |
decrypted = crypt.decrypt(encrypted) | |
print(f"Decrypted: {decrypted}") | |
# Verify | |
if original_text == decrypted: | |
print("✅ Test PASSED: Encryption and decryption successful!") | |
else: | |
print("❌ Test FAILED: Decrypted text doesn't match original") | |
except Exception as e: | |
print(f"❌ Test FAILED: {e}") | |
print("\n" + "=" * 40) | |
print("Testing with different passphrases") | |
# Test with wrong passphrase | |
try: | |
wrong_crypt = Crypt("wrong-passphrase") | |
wrong_crypt.decrypt(encrypted) | |
print("❌ Security FAILED: Wrong passphrase should not decrypt") | |
except ValueError: | |
print("✅ Security PASSED: Wrong passphrase correctly rejected") | |
# Test edge cases | |
print("\nTesting edge cases:") | |
# Empty string | |
try: | |
empty_encrypted = crypt.encrypt("") | |
empty_decrypted = crypt.decrypt(empty_encrypted) | |
print(f"✅ Empty string test: '{empty_decrypted}' (length: {len(empty_decrypted)})") | |
except Exception as e: | |
print(f"❌ Empty string test failed: {e}") | |
# Unicode text | |
try: | |
unicode_text = "🔒 Encryption test with emojis 🌟 and Unicode: αβγδε" | |
unicode_encrypted = crypt.encrypt(unicode_text) | |
unicode_decrypted = crypt.decrypt(unicode_encrypted) | |
if unicode_text == unicode_decrypted: | |
print("✅ Unicode test passed") | |
else: | |
print("❌ Unicode test failed") | |
except Exception as e: | |
print(f"❌ Unicode test failed: {e}") | |
# Long text | |
try: | |
long_text = "A" * 10000 # 10KB of text | |
long_encrypted = crypt.encrypt(long_text) | |
long_decrypted = crypt.decrypt(long_encrypted) | |
if long_text == long_decrypted: | |
print(f"✅ Long text test passed (10KB encrypted to {len(long_encrypted)} chars)") | |
else: | |
print("❌ Long text test failed") | |
except Exception as e: | |
print(f"❌ Long text test failed: {e}") | |
""" | |
Installation Requirements: | |
pip install cryptography | |
Note: This implementation uses the 'cryptography' library which is the recommended | |
cryptographic library for Python. It provides secure implementations of AES encryption. | |
Key Features: | |
- Uses AES-256-GCM for authenticated encryption | |
- PBKDF2 for key derivation with 100,000 iterations | |
- Random salt for each encryption (prevents rainbow table attacks) | |
- Random IV for each encryption (prevents pattern analysis) | |
- Authentication tag prevents tampering | |
- Handles Unicode text properly | |
- Comprehensive error handling | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment