在使用網路傳輸資料的過程中,即便使用了SSL(Secure Sockets Layer)或是TLS(Transport Layer Security),傳輸內容還是有可能會遭到破解(如中間人攻擊, MITM)。當傳輸的資料遭到惡意的第三方竊聽,並且成功取得明文(也就是原始資料),那下場就很慘了。如果要讓傳輸過程更安全的話,那就自行把要傳遞的資料用自己的方式先加密吧!AES(Advanced Encryption Standard)是一套加密標準,使用對稱式密鑰,許多程式語言內都有支援,實作起來還算容易,速度和強度也不錯。



AES

AES加密元素大致可分為以下幾類:

密鑰

AES使用對稱式密鑰,也就是加密和解密使用的密鑰都是同一個。根據密鑰的長度,可以分為AES-128、AES-192和AES-256。愈長的密鑰計算出來的密文,強度愈高。128位元的密鑰已經足夠應付一般的使用,如果是最高機密的訊息(如國家機密)則需使用192位元以上的密鑰。密鑰的產生可以直接使用雜湊演算法來產生固定位元的資料。例如MD5演算法可以產生出128位元的資料,SHA-256則可以產生出256位元的資料。

初始化向量(IV, Initialization Vector)

為避免在相同密鑰下,使用AES加密相同明文會產生同樣的結果,因此可以使用IV來調整加密結果。IV需工作在CBC、CFB或是OFB分組模式下,長度為128位元。

填充(Padding)

AES在加密明文的時候,需把明文分為好幾個區塊,每個區塊的長度為128位元,不能多也不能少。但是,並不是每個明文都可以剛好被區分出完整的區塊,因此須對明文做「填充」,增加它的長度,使其在分區塊的時候可以剛好被分完。常使用的填充方式為「PKCS5」,就是先算出需要再加多少位元才能被128位元整除,再把這多少的位元值加到明文的最後。舉例來說,假設明文有240位元,那麼它還差16位元才能被128整除,所以需要再加入16位元的資料。而這16位元的值可以切成2個8位元,每個8位元都是「2」這個值(000000102)。另外還有「PKCS7」這種填充方式,只是預設實作的方式和「PKCS5」填充後的結果一樣。PKCS7的Block大小可以是浮動的,但預設的實作方式都和PKCS5一樣使用8位元,一次填入8位元的資料。

MagicCrypt

取得MagicCrypt

GitHub:

在Java上使用MagicCrypt

final MagicCrypt mc = new MagicCrypt("magickey", 256);
System.out.println(mc.encrypt("https://magiclen.org"));
System.out.println(mc.decrypt("jWEPYLTECqGvWJbdlRGeZIupoLX8N9DYZIUKMRp/OQY="));

MagicCrypt類別可以直接用在JRE或是Android環境上,支援64位元(Data Encryption Standard, DES)、128位元、192位元和256位元的密鑰。只是在JRE運行的話,使用192位元和256位元的密鑰可能會出現問題,會拋出InvalidKeyException例外,並告知密鑰長度不合法。這是因為JRE預設只支援至128位元的密鑰,如果要使其支援更長的密鑰的話,需要到Java官方網站下載對應Java版本的JCE(Java Cryptography Extension),並將其放置於JRE目錄下的lib/security目錄中。為了將加密後的結果使用字串表示,因此用到了Base64。在Java上使用Base64的方法可以參考這篇文章:

在PHP上使用MagicCrypt

<?php
    $mc = new MagicCrypt('magickey', 256);
    use org\magiclen\magiccrypt\MagicCrypt;

    echo $mc->encrypt('https://magiclen.org'), "\n";
    echo $mc->decrypt('jWEPYLTECqGvWJbdlRGeZIupoLX8N9DYZIUKMRp/OQY=');
?>

PHP用到mcrypt模組來實作AES,因此需要啟用mcrypt模組,可以參考這篇文章來啟用:

在Node.js上使用MagicCrypt

npmjs.com

npm 安裝指令

npm install magiccrypt
使用方法

引入magiccrypt後,接著使用new關鍵字來實體化出MagicCrypt的物件,可以從參數傳入要用來加解密的密鑰字串,以及密鑰的長度。

import { MagicCrypt } from "magiccrypt";

const mc = new MagicCrypt("magickey", 256);

console.log(mc.encrypt("http://magiclen.org"));
console.log(mc.decrypt("DS/2U8royDnJDiNY2ps3f6ZoTbpZo8ZtUGYLGEjwLDQ="));

在Rust上使用MagicCrypt

Crates.io

Cargo.toml

magic-crypt = "*"
使用方法
use magic_crypt::{new_magic_crypt, MagicCryptTrait};

let mc = new_magic_crypt!("magickey", 256);

let base64 = mc.encrypt_str_to_base64("http://magiclen.org");

assert_eq!("DS/2U8royDnJDiNY2ps3f6ZoTbpZo8ZtUGYLGEjwLDQ=", base64);

assert_eq!("http://magiclen.org", mc.decrypt_base64_to_string(&base64).unwrap());

encrypt_reader_to_writer方法和decrypt_reader_to_writer方法的預設緩衝空間大小為4096個位元組。若您想要改變緩衝空間大小,可以使用encrypt_reader_to_writer2方法和decrypt_reader_to_writer2方法並且明確地定義要使用的長度(透過泛型)。

例如,要將緩衝空間大小改為256個位元組的話,程式可以這樣寫:

use std::io::Cursor;

use magic_crypt::{new_magic_crypt, MagicCryptTrait};
use magic_crypt::generic_array::typenum::U256;

let mc = new_magic_crypt!("magickey", 256);

let mut reader = Cursor::new("http://magiclen.org");
let mut writer = Vec::new();

mc.encrypt_reader_to_writer2::<U256>(&mut reader, &mut writer).unwrap();

let base64 = base64::encode(&writer);

assert_eq!("DS/2U8royDnJDiNY2ps3f6ZoTbpZo8ZtUGYLGEjwLDQ=", base64);

assert_eq!("http://magiclen.org", mc.decrypt_base64_to_string(&base64).unwrap());