AES

AES

Advanced Encryption Standard,縮寫:AES,又稱Rijndael加密法,但嚴格來說 AES 和 Rijndael 加密法並不完全一樣,因為 Rijndael 加密法可以支援更大範圍的區塊長度,AES的區塊長度固定為128位元而Rijndael使用的區塊長度可以是128,192或256位元。AES用來替代原先的DES。

AES的區塊長度固定為 128 位元,金鑰長度則可以是128,192或256位元;而Rijndael使用的金鑰和區塊長度均可以是128,192或256位元。

AES加密過程是在一個4×4的位元組矩陣上運作,其初值即為一個明文區塊,矩陣中一個元素大小就是明文區塊中的一個Byte。

其加密方法主要包含四個步驟

1. AddRoundKey: 將每個狀態中的位元組與該回合金鑰做異或(⊕)。
2. SubBytes:    矩陣中各位元組被固定的8位元尋找表中對應的特定位元組所替換。
3. ShiftRows:   矩陣中每一行的各個位元組循環向左方位移。位移量則隨著行數遞增而遞增。
4. MixColumns:  每個直行與 AES 定義的多項式 c(x) 進行多項式乘法。

AES-256 範例

const crypto = require('crypto');

const mode = 'aes256' // 可更換為aes128或aes192等等

// 加密
const cipher = crypto.createCipher(mode, 'a password');
let encrypted = cipher.update('I_am_plaintext', 'utf8', 'hex');
encrypted += cipher.final('hex');
console.log(encrypted);

// 解密
const decipher = crypto.createDecipher(mode, 'a password');
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);

AES之區塊加密模式

在CBC、OFB、CFB、CTR等區塊模式 IV 長度均為 16 bytes,GCM模式的 IV 則沒有一定要 16 bytes

AES-256-CBC範例

const crypto = require('crypto');

// 因為aes-256要求之key 長度為256bits 也就是32 bytes = 32個ASCII英文字母
// aes-128 要求之key 長度為128bits 也就是16 bytes = 16個英文字母
let key = Buffer.from("abcbbbbbbbbbbbbbabcbbbbbbbbbbbbb", 'utf-8') //or crypto.randomBytes(32);

const IV_LENGTH = 16; 

function encrypt(text) {
    let iv = crypto.randomBytes(IV_LENGTH);
    // 可直接替換為ofb、cfb、ctr等模式
    let cipher = crypto.createCipheriv('aes-256-cbc', new Buffer(key), iv);
    let encrypted = cipher.update(text);
    encrypted = cipher.final();

    // 將IV附上 在解密時須告知
    return iv.toString('hex') + ':' + encrypted.toString('hex');
}

function decrypt(text) {
    let textParts = text.split(':');
    let iv = new Buffer(textParts.shift(), 'hex');
    let encryptedText = new Buffer(textParts.join(':'), 'hex');
    let decipher = crypto.createDecipheriv('aes-256-cbc', new Buffer(key), iv);
    let decrypted = decipher.update(encryptedText);

    decrypted = decipher.final();

    return decrypted.toString();
}

console.log(encrypt("test"));
console.log(decrypt(encrypt("test")))

AES-256-GCM範例

需要加上cipher.getAuthTag(); 與 decipher.setAuthTag();

目前 AuthTag 與 Additional Authenticated Data ( AAD ) 在 Node.js 只有 GCM 模式支援,cipher.setAAD需要在 update() 之前使用,而 cipher.getAuthTag() 必須要在cipher.final()執行後才能使用。

const crypto = require('crypto');


const mode = 'aes-256-gcm';
// 因為aes-256要求之key 長度為256bits 也就是32 bytes = 32個ASCII英文字母
// aes-128 要求之key 長度為128bits 也就是16 bytes = 16個英文字母
let key = Buffer.from("abcbbbbbbbbbbbbbabcbbbbbbbbbbbbb", 'utf-8') //or crypto.randomBytes(32);

const IV_LENGTH = 12;
let tag;

function encrypt(text) {
    let iv = crypto.randomBytes(IV_LENGTH);

    let cipher = crypto.createCipheriv(mode, new Buffer(key), iv);
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    // 需要加上AuthTag
    tag = cipher.getAuthTag();
    // 將IV附上 在解密時須告知
    return iv.toString('hex') + ':' + encrypted.toString('hex');
}

function decrypt(text) {
    let textParts = text.split(':');
    let iv = new Buffer(textParts.shift(), 'hex');
    let encryptedText = new Buffer(textParts.join(':'), 'hex');
    let decipher = crypto.createDecipheriv(mode, new Buffer(key), iv);
    // 需要加上AuthTag
    decipher.setAuthTag(tag);
    let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
    decrypted += decipher.final('utf8');

    return decrypted.toString();
}

console.log(encrypt("test"));
console.log(decrypt(encrypt("test")))

IV 與 AuthTag

IV and the auth tag can be sent in plain.

The auth tag is a tag the recipient can use to verify that the message has not been altered. This is important because AES-GCM acts as a stream cipher and anyone could flip bits.

The IV is not a secret, the only thing to be concerned about is that it must not be reused with the same key.

使用openssl加密檔案

產生檔案
echo test > file.txt

加密
openssl enc -aes-256-cbc -salt -in file.txt -out file.txt.enc

解密
openssl enc -aes-256-cbc -d -in file.txt.enc -out result.txt

加上密碼

openssl enc -aes-256-cbc -salt -in file.txt -out file.txt.enc -k PASS
openssl enc -aes-256-cbc -d -in file.txt.enc -out file.txt -k PASS

https://www.shellhacks.com/encrypt-decrypt-file-password-openssl/

Last updated