Encryption Helpers
Client-side encryption utilities for secure data transmission in the WorkPayCore frontend application.
Overview
The encryption helpers provide RSA + AES hybrid encryption for secure API
communication, matching PHP's openssl_encrypt behavior for backend
compatibility.
Core Functions
encryptData<T>(data)
Encrypts data using hybrid RSA + AES encryption for secure transmission.
Parameters:
data(T): The data to encrypt (any serializable type)
Returns:
Promise<any>: Encrypted payload object or original data if encryption unavailable
Example:
import { encryptData } from '@/utils/helpers/encryption';
// Encrypt sensitive form data
const sensitiveData = {
ssn: '123-45-6789',
bankAccount: '1234567890',
salary: 75000,
};
const encryptedPayload = await encryptData(sensitiveData);
console.log(encryptedPayload);
// Returns: {
// x: "base64-encrypted-data",
// y: "rsa-encrypted-aes-key",
// z: "base64-initialization-vector"
// }
// Send to API
const response = await fetch('/api/secure-endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(encryptedPayload),
});
Use Cases:
- Sensitive form data transmission
- Payment information encryption
- Personal data protection
- Secure API communication
Utility Functions
base64ToArrayBuffer(base64)
Converts a base64 string to ArrayBuffer for cryptographic operations.
Parameters:
base64(string): Base64 encoded string
Returns:
ArrayBuffer: Binary data representation
Example:
import { base64ToArrayBuffer } from '@/utils/helpers/encryption';
const base64String = 'SGVsbG8gV29ybGQ=';
const buffer = base64ToArrayBuffer(base64String);
console.log(buffer); // ArrayBuffer(11) {}
arrayBufferToBase64(buffer)
Converts an ArrayBuffer to base64 string for transmission.
Parameters:
buffer(ArrayBuffer): Binary data to convert
Returns:
string: Base64 encoded string
Example:
import { arrayBufferToBase64 } from '@/utils/helpers/encryption';
const buffer = new ArrayBuffer(8);
const base64 = arrayBufferToBase64(buffer);
console.log(base64); // "AAAAAAAAAAA="
getRandomBytes(length)
Generates cryptographically secure random bytes.
Parameters:
length(number): Number of bytes to generate
Returns:
Promise<Uint8Array>: Random byte array
Example:
import { getRandomBytes } from '@/utils/helpers/encryption';
// Generate 32 random bytes for AES-256 key
const keyBytes = await getRandomBytes(32);
console.log(keyBytes); // Uint8Array(32) [...]
// Generate 16 random bytes for AES IV
const ivBytes = await getRandomBytes(16);
console.log(ivBytes); // Uint8Array(16) [...]
Encryption Process
How Hybrid Encryption Works
- Generate Random Keys: Creates 256-bit AES key and 128-bit IV
- AES Encryption: Encrypts data using AES-CBC with PKCS#7 padding
- RSA Encryption: Encrypts the AES key using RSA public key
- Package Response: Returns encrypted data, encrypted key, and IV
Data Flow
// Original data
const originalData = { sensitive: 'information' };
// Step 1: Generate random AES key and IV
const aesKey = await getRandomBytes(32); // 256-bit key
const iv = await getRandomBytes(16); // 128-bit IV
// Step 2: Encrypt data with AES
const jsonString = JSON.stringify(originalData);
const encryptedData = await encryptAesManual(aesKey, iv, jsonString);
// Step 3: Encrypt AES key with RSA
const aesKeyBase64 = arrayBufferToBase64(aesKey);
const encryptedKey = jsEncrypt.encrypt(aesKeyBase64);
// Step 4: Package for transmission
const payload = {
x: encryptedData, // AES encrypted data
y: encryptedKey, // RSA encrypted AES key
z: arrayBufferToBase64(iv), // Base64 IV
};
Advanced Usage Examples
Secure Form Submission
import { encryptData } from '@/utils/helpers/encryption';
const SecureForm = () => {
const [formData, setFormData] = useState({
ssn: '',
bankAccount: '',
routingNumber: '',
});
const handleSubmit = async e => {
e.preventDefault();
try {
// Encrypt sensitive data
const encryptedPayload = await encryptData(formData);
// Submit encrypted data
const response = await fetch('/api/employee/banking', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${authToken}`,
},
body: JSON.stringify(encryptedPayload),
});
if (response.ok) {
alert('Banking information saved securely');
}
} catch (error) {
console.error('Encryption failed:', error);
alert('Failed to secure data. Please try again.');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type='text'
placeholder='SSN'
value={formData.ssn}
onChange={e => setFormData({ ...formData, ssn: e.target.value })}
/>
<input
type='text'
placeholder='Bank Account'
value={formData.bankAccount}
onChange={e =>
setFormData({ ...formData, bankAccount: e.target.value })
}
/>
<input
type='text'
placeholder='Routing Number'
value={formData.routingNumber}
onChange={e =>
setFormData({ ...formData, routingNumber: e.target.value })
}
/>
<button type='submit'>Save Securely</button>
</form>
);
};
Payment Processing
import { encryptData } from '@/utils/helpers/encryption';
const PaymentForm = () => {
const processPayment = async paymentData => {
try {
// Encrypt payment information
const encryptedPayment = await encryptData({
cardNumber: paymentData.cardNumber,
cvv: paymentData.cvv,
expiryDate: paymentData.expiryDate,
amount: paymentData.amount,
});
// Process payment with encrypted data
const response = await fetch('/api/payments/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...encryptedPayment,
merchantId: 'your-merchant-id',
timestamp: Date.now(),
}),
});
return response.json();
} catch (error) {
throw new Error('Payment encryption failed');
}
};
return <CreditCardForm onSubmit={processPayment} />;
};
Conditional Encryption Hook
import { encryptData } from '@/utils/helpers/encryption';
const useSecureSubmission = () => {
const [isEncrypting, setIsEncrypting] = useState(false);
const secureSubmit = async (data, endpoint, options = {}) => {
setIsEncrypting(true);
try {
let payload = data;
// Only encrypt if RSA public key is available
if (import.meta.env.VITE_RSA_PUBLIC_KEY) {
payload = await encryptData(data);
} else {
console.warn('No RSA public key found. Sending unencrypted data.');
}
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...options.headers,
},
body: JSON.stringify(payload),
});
return response.json();
} finally {
setIsEncrypting(false);
}
};
return { secureSubmit, isEncrypting };
};
// Usage
const UserProfileForm = () => {
const { secureSubmit, isEncrypting } = useSecureSubmission();
const handleSave = async profileData => {
const result = await secureSubmit(profileData, '/api/profile/update');
if (result.success) {
alert('Profile updated securely');
}
};
return (
<form onSubmit={handleSave}>
{/* form fields */}
<button type='submit' disabled={isEncrypting}>
{isEncrypting ? 'Encrypting...' : 'Save Profile'}
</button>
</form>
);
};
Environment Configuration
Required Environment Variables
# .env file
VITE_RSA_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"
Environment-Specific Behavior
// Development vs Production
const isDevelopment = import.meta.env.DEV;
if (isDevelopment && !import.meta.env.VITE_RSA_PUBLIC_KEY) {
console.warn('RSA public key not configured. Encryption disabled.');
}
// The encryptData function automatically handles missing keys
const result = await encryptData(sensitiveData);
// Returns original data if no public key is configured
Security Considerations
Best Practices
- Key Management: Never store private keys in frontend code
- Environment Variables: Use secure environment variable management
- HTTPS Only: Always use HTTPS in production for encrypted payloads
- Key Rotation: Regularly rotate RSA key pairs
- Validation: Validate encrypted payloads on the backend
Security Features
- AES-256-CBC: Strong symmetric encryption
- RSA-2048+: Asymmetric key encryption
- PKCS#7 Padding: Standard padding for AES blocks
- Random IVs: Each encryption uses a unique initialization vector
- Base64 Encoding: Safe transport encoding
Error Handling
import { encryptData } from '@/utils/helpers/encryption';
const safeEncryption = async data => {
try {
return await encryptData(data);
} catch (error) {
console.error('Encryption failed:', error);
// Decide whether to proceed without encryption
if (isProductionEnvironment()) {
throw new Error('Encryption required but failed');
}
// In development, might proceed without encryption
console.warn('Proceeding without encryption in development mode');
return data;
}
};
Performance Considerations
- Async Operations: All encryption is asynchronous
- Memory Usage: Large data sets consume more memory during encryption
- Processing Time: Encryption adds latency to requests
- Browser Support: Uses modern Web Crypto API
Related Utilities
- General Helpers - For base64 encoding utilities
- String Utilities - For data formatting
TypeScript Definitions
export function base64ToArrayBuffer(base64: string): ArrayBuffer;
export function arrayBufferToBase64(buffer: ArrayBuffer): string;
export function getRandomBytes(length: number): Promise<Uint8Array>;
export function encryptData<T>(data: T): Promise<any>;
Dependencies
- jsencrypt: RSA encryption library
- Web Crypto API: Browser native cryptographic functions
- Environment Variables: For RSA public key configuration