Symmetric Key Algorithms
A Symmetric-Key Algorithm uses a string of data to encrypt and decrypt information. This string of data acts like a real-world key which can lock and unlock a door. In fact, it is often called a “key” or a “password”. With symmetric-key algorithms, the same key is used for encrypting and for decrypting (that is what makes it “symmetric”).
Anyone who knows or possesses the key can decrypt the information. Anyone who does not know or possess the key cannot decrypt it easily. It cannot be simply unscrambled like a substitution cipher. Someone would need to use advanced techniques to “break” the encryption.
If the algorithm is hard to break, this is a better way to encrypt messages than a substitution cipher. Messages can be sent publicly, but only the recipient who knows the password can decrypt it. Much of the history of cryptography (and military communication/intelligence) has been dedicated towards either developing stronger algorithms or trying to break current algorithms.
The rapid increase of computing power beginning in the 1970s transformed the cryptography landscape. Hundreds of algorithms have been developed and hundreds have been broken. There are three algorithms which are notable for their resistance to decryption and their wide-spread usage.
-
The Data Encryption Standard (DES) algorithm was developed at IBM and first published in 1977. It was one of the first N.S.A. approved standards for encryption. It was widely used from 1977 until 2000. It was also widely studied and informs the design of later algorithms. However, it is now considered insecure. Modern computers can decrypt a DES encrypted message in less than a day. Its successor Triple DES (DES encryption applied three times) is also no longer recommended: NIST disallowed three-key Triple DES (TDEA) for applying cryptographic protection after December 31, 2023 and withdrew SP 800-67 Rev. 2 in January 2024. Use AES instead.
-
The Advanced Encryption Standard (AES) algorithm was selected by N.I.S.T. as the winner of an algorithm competition that concluded in October 2000, and was published as the federal standard FIPS PUB 197 on November 26, 2001. It is also known as “Rijndael” (pronounced rain-dahl) because that was its name during the competition. AES has not yet been broken and is still considered strong enough to encrypt U.S. classified data.
-
The Blowfish algorithm was designed in 1993. It has not yet been broken, even though a few technical and theoretical weaknesses have been identified. It is also widely used in many encryption software products. Blowfish encryption itself is fast — its designer benchmarked it at roughly 18 cycles per byte on a Pentium (Schneier, “The Blowfish Encryption Algorithm”) — but it has a deliberately expensive key-setup phase. The bcrypt password-hashing algorithm exploits that property: it runs a modified Blowfish key schedule (Eksblowfish) for a configurable number of rounds to slow down password cracking (Wikipedia: “bcrypt”). It is bcrypt’s iterated key setup, not Blowfish encryption, that makes bcrypt-hashed passwords expensive to brute-force.
Symmetric Key Algorithms Pros and Cons
Symmetric key algorithms are very fast and can encrypt large amounts of data.
The primary drawback of symmetric key algorithms is the “key distribution problem”. Once data is encrypted it can be sent publicly, however, the key or password must also be given to the recipient. If the key is sent with the data or sent in plain text through another visible channel, then it is easily intercepted and the data will be easily decrypted.
One common solution is to use symmetric key algorithms to encrypt the data and then to use asymmetric key algorithms (a.k.a. public key cryptography) to encrypt only the key which unlocks the encrypted data.
Encryption/Decryption in PHP using AES
Use PHP’s OpenSSL functions to encrypt data with a key. The example below uses AES-256-GCM, an authenticated (AEAD) cipher mode: in addition to the ciphertext, GCM produces a short authentication tag that lets the recipient detect any tampering with the ciphertext, IV, or associated data. OWASP’s Cryptographic Storage Cheat Sheet recommends GCM (or CCM) as the first choice precisely because unauthenticated modes such as CBC or CTR provide no integrity guarantee and require a separate MAC to be safe against active attackers. openssl_encrypt() and openssl_decrypt() have supported GCM since PHP 7.1 via the $tag by-reference parameter (see the PHP manual).
<?php
const CIPHER_METHOD = 'AES-256-GCM';
$plaintext = 'This is a secret.';
$key = 'a1b2c3d4e5';
// Needs a key of length 32 (256-bit)
$key = str_pad($key, 32, '*');
// Create an initialization vector (called a "nonce" in GCM).
// openssl_cipher_iv_length() returns 12 bytes for AES-256-GCM,
// the recommended nonce size. A fresh random nonce MUST be used
// for every encryption performed with the same key.
// random_bytes() is the modern, cryptographically-secure source for
// key material and nonces (PHP 7.0+); it throws on failure rather
// than silently returning false the way openssl_random_pseudo_bytes()
// can on older PHP versions.
$iv_length = openssl_cipher_iv_length(CIPHER_METHOD);
$iv = random_bytes($iv_length);
// $tag is populated by reference: openssl_encrypt() writes the GCM
// authentication tag here (16 bytes by default).
$tag = '';
// Encrypt
$ciphertext = openssl_encrypt(
$plaintext, CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv, $tag
);
// openssl_encrypt() returns false on failure (unknown cipher, bad
// key/IV length, OpenSSL error). Concatenating a false return below
// would coerce it to an empty string and silently produce a malformed
// message -- always check before building the bundle.
if ($ciphertext === false) {
throw new RuntimeException('Encryption failed.');
}
// The IV, ciphertext, and tag must all be sent to the recipient.
$message = $iv . $ciphertext . $tag;
// Encode just ensures encrypted characters are viewable/savable
echo base64_encode($message);
// l129gKj7byZm1WRk31olnmuxdTok5i34ahig+FXD2ds0aknQoApkppC3YTR8
?>
Use PHP’s OpenSSL functions to decrypt data with a key. With GCM, decryption only succeeds if the supplied authentication tag matches the ciphertext, IV, and key; otherwise openssl_decrypt() returns false and the plaintext is never produced. Always check that return value and refuse to act on a false result — treating it as an empty string would let an attacker bypass the integrity check.
<?php
const CIPHER_METHOD = 'AES-256-GCM';
const TAG_LENGTH = 16;
$message = 'l129gKj7byZm1WRk31olnmuxdTok5i34ahig+FXD2ds0aknQoApkppC3YTR8';
$key = 'a1b2c3d4e5';
// Needs a key of length 32 (256-bit)
$key = str_pad($key, 32, '*');
// Base64 decode before decrypting. Pass `true` for strict mode so
// invalid characters cause a `false` return instead of being silently
// discarded -- otherwise a tampered or malformed message could quietly
// mis-split into nonce/ciphertext/tag.
$bundle = base64_decode($message, true);
$iv_length = openssl_cipher_iv_length(CIPHER_METHOD);
if ($bundle === false || strlen($bundle) < $iv_length + TAG_LENGTH) {
throw new RuntimeException('Decryption failed: malformed message.');
}
// Split the stored bundle into nonce, ciphertext, and auth tag.
$iv = substr($bundle, 0, $iv_length);
$tag = substr($bundle, -TAG_LENGTH);
$ciphertext = substr($bundle, $iv_length, -TAG_LENGTH);
// Decrypt AND verify the tag in a single call. Returns false if the
// ciphertext, IV, or tag has been tampered with.
$plaintext = openssl_decrypt(
$ciphertext, CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv, $tag
);
if ($plaintext === false) {
// Authentication failed: forged, corrupted, or wrong key. Do not
// fall through to using a false/empty plaintext.
throw new RuntimeException('Decryption failed: invalid authentication tag.');
}
echo $plaintext;
// This is a secret.
?>