在使用網路傳輸資料的過程中,即便使用了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位元,不能多也不能少。但是,並不是每個明文都可以剛好被區分出完整的區塊,因此須對明文做「填充」,增加它的長度,使其在分區塊的時候可以剛好被分完。常使用的填充方式為「PKCS7」,就是先算出需要再加多少位元才能被128位元整除,再把這多少的位元值加到明文的最後。舉例來說,假設明文有240位元,那麼它還差16位元才能被128整除,所以需要再加入16位元的資料。而這16位元的值可以切成2個8位元,每個8位元都是「2」這個值(000000102)。另外還有「PKCS5」這種填充方式,PKCS5的填充方式如同PKCS7,唯一的差異是PKCS5只能用於填充長度為64位元(8個位元組)的區塊,而PKCS7則可填充長度為1 ~ 255個位元組的區塊。早年填充機制的定義還不明確時,AES是可以使用PKCS5來填充的,因為當時的函式庫(如javax.crypto)對PKCS5實作根本就沒有去限制區塊的長度,所以PKCS5和PKCS7可以畫上等號。
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 安裝指令
使用方法
引入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
使用方法
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());