AES (Advanced Encryption Standard) is one of the most widely used symmetric encryption algorithms. It was officially adopted by the National Institute of Standards and Technology (NIST) in 2001, replacing the old DES algorithm. AES is a block cipher algorithm that supports three key lengths: 128-bit, 192-bit, and 256-bit.
I. Overview of AES Algorithm
| Feature | Description |
|---|---|
| Algorithm Type | Symmetric Block Cipher |
| Block Size | 128 bits (fixed) |
| Key Size | 128/192/256 bits |
| Number of Rounds | 10/12/14 rounds (corresponding to different key lengths) |
| Security | Extremely high, no effective attack methods currently |
II. AES Encryption Process
The AES encryption process mainly includes the following steps:
2.1 Initial Round (AddRoundKey)
Perform bitwise XOR operation between the plaintext block and the first round key:
state = state XOR roundKey[0]
2.2 Main Round Transformations (repeated N-1 times)
Each round contains four transformations:
- SubBytes: Byte substitution using S-box
- ShiftRows: Row shifting operation
- MixColumns: Column mixing operation
- AddRoundKey: XOR with round key
2.3 Final Round (without MixColumns)
The last round contains only three transformations:
- SubBytes
- ShiftRows
- AddRoundKey
III. Detailed Core Transformations
3.1 SubBytes: Byte Substitution
SubBytes uses a fixed lookup table (S-box) to perform non-linear substitution on each byte:
// AES S-Box example (partial)
const sBox = [
[0x63, 0x7c, 0x77, 0x7b, ...],
[0xf2, 0x6b, 0x6f, 0xc5, ...],
// ... total 16x16 bytes
];
function subBytes(state) {
for (let i = 0; i < 16; i++) {
const row = (state[i] >> 4) & 0x0f;
const col = state[i] & 0x0f;
state[i] = sBox[row][col];
}
return state;
}
3.2 ShiftRows: Row Shifting
ShiftRows performs cyclic shifting on the rows of the matrix, with row i shifted left by i positions:
// Input state matrix (4x4)
// [ s0 s4 s8 s12 ]
// [ s1 s5 s9 s13 ]
// [ s2 s6 s10 s14 ]
// [ s3 s7 s11 s15 ]
function shiftRows(state) {
// Row 0: no shift
// Row 1: shift left by 1
[state[1], state[5], state[9], state[13]] =
[state[5], state[9], state[13], state[1]];
// Row 2: shift left by 2
[state[2], state[6], state[10], state[14]] =
[state[10], state[14], state[2], state[6]];
// Row 3: shift left by 3
[state[3], state[7], state[11], state[15]] =
[state[15], state[3], state[7], state[11]];
return state;
}
3.3 MixColumns: Column Mixing
MixColumns performs linear transformation on each column of the matrix using a fixed polynomial:
function mixColumns(state) {
const matrix = [
[2, 3, 1, 1],
[1, 2, 3, 1],
[1, 1, 2, 3],
[3, 1, 1, 2]
];
const newState = new Uint8Array(16);
for (let col = 0; col < 4; col++) {
for (let row = 0; row < 4; row++) {
let sum = 0;
for (let i = 0; i < 4; i++) {
sum ^= gfMultiply(matrix[row][i], state[col * 4 + i]);
}
newState[col * 4 + row] = sum;
}
}
return newState;
}
// Galois field multiplication
function gfMultiply(a, b) {
let result = 0;
for (let i = 0; i < 8; i++) {
if (b & 1) result ^= a;
const carry = a & 0x80;
a = (a << 1) & 0xff;
if (carry) a ^= 0x1b; // AES polynomial
b >>= 1;
}
return result;
}
3.4 AddRoundKey: Round Key Addition
Perform bitwise XOR between the state and the round key:
function addRoundKey(state, roundKey) {
for (let i = 0; i < 16; i++) {
state[i] ^= roundKey[i];
}
return state;
}
IV. Key Expansion
AES uses a key expansion algorithm to expand the original key into multiple round keys:
function expandKey(key) {
const keyLen = key.length;
const rounds = keyLen === 16 ? 10 : (keyLen === 24 ? 12 : 14);
const expanded = new Uint8Array(16 * (rounds + 1));
// Copy original key
expanded.set(key);
for (let i = keyLen; i < expanded.length; i += 4) {
let temp = expanded.slice(i - 4, i);
// Transform every 4 bytes
if (i % keyLen === 0) {
temp = subWord(rotWord(temp));
temp[0] ^= rcon[i / keyLen];
} else if (keyLen > 24 && i % keyLen === 16) {
temp = subWord(temp);
}
for (let j = 0; j < 4; j++) {
expanded[i + j] = expanded[i + j - keyLen] ^ temp[j];
}
}
return expanded;
}
function rotWord(word) {
return [word[1], word[2], word[3], word[0]];
}
function subWord(word) {
return word.map(byte => sBox[(byte >> 4) & 0x0f][byte & 0x0f]);
}
V. Complete AES Encryption Implementation
class AES {
constructor(key) {
this.key = key;
this.rounds = key.length === 16 ? 10 : (key.length === 24 ? 12 : 14);
this.expandedKey = this.expandKey(key);
}
encrypt(plaintext) {
// Initial round
let state = this.addRoundKey(plaintext, this.expandedKey.slice(0, 16));
// Main rounds
for (let round = 1; round < this.rounds; round++) {
state = this.subBytes(state);
state = this.shiftRows(state);
state = this.mixColumns(state);
state = this.addRoundKey(state, this.expandedKey.slice(round * 16, (round + 1) * 16));
}
// Final round
state = this.subBytes(state);
state = this.shiftRows(state);
state = this.addRoundKey(state, this.expandedKey.slice(this.rounds * 16));
return state;
}
// ... other methods (subBytes, shiftRows, mixColumns, addRoundKey, expandKey)
}
// Usage example
const key = new Uint8Array(16); // 128-bit key
const aes = new AES(key);
const plaintext = new Uint8Array(16); // 128-bit plaintext
const ciphertext = aes.encrypt(plaintext);
VI. AES Operating Modes
AES itself is a block cipher and needs to work with operating modes to handle data of arbitrary length:
| Mode | Features | Security | Applicable Scenario |
|---|---|---|---|
| ECB | Same plaintext produces same ciphertext | Low | Not recommended |
| CBC | Requires initialization vector (IV) | Medium | General scenarios |
| CFB | Stream cipher mode | Medium | Streaming data |
| OFB | Stream cipher mode, no error propagation | Medium | Network transmission |
| CTR | Stream cipher mode, parallel encryption | High | High-performance scenarios |
| GCM | Authenticated encryption mode | High | Scenarios requiring authentication |
VII. Frontend Implementation Example
Modern browsers natively support AES using the Web Crypto API:
async function aesEncrypt(key, data, iv) {
const cryptoKey = await window.crypto.subtle.importKey(
'raw',
key,
{ name: 'AES-GCM' },
false,
['encrypt']
);
const ciphertext = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
cryptoKey,
data
);
return ciphertext;
}
async function aesDecrypt(key, ciphertext, iv) {
const cryptoKey = await window.crypto.subtle.importKey(
'raw',
key,
{ name: 'AES-GCM' },
false,
['decrypt']
);
const plaintext = await window.crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
cryptoKey,
ciphertext
);
return plaintext;
}
// Usage example
const key = window.crypto.getRandomValues(new Uint8Array(16)); // 128-bit key
const iv = window.crypto.getRandomValues(new Uint8Array(12)); // GCM recommended 12-byte IV
const data = new TextEncoder().encode('Hello, AES!');
const ciphertext = await aesEncrypt(key, data, iv);
const decrypted = await aesDecrypt(key, ciphertext, iv);
console.log(new TextDecoder().decode(decrypted)); // "Hello, AES!"
π‘ Tip:It is recommended to use AES-GCM mode, which provides both encryption and authentication functions and can detect whether data has been tampered with.
VIII. Security Best Practices
- Key Management: Keys must be kept secret, avoid hardcoding
- IV Usage: Use a random IV for each encryption, IV can be public
- Mode Selection: Prefer GCM or CTR mode, avoid ECB
- Key Derivation: Use PBKDF2 or scrypt to derive keys from passwords
- Authentication: Ensure data integrity, use authenticated encryption modes
IX. Summary
AES is the cornerstone of modern cryptography. Understanding its principles is crucial for secure development. The security of AES is based on its complex round function design and finite field operations, and there are currently no effective attack methods.
In actual development, the browser's native Web Crypto API or verified third-party libraries should be used first, and self-implemented encryption algorithms should be avoided. Proper key management and mode selection are equally important.