在使用網路傳輸資料的過程中,即便使用了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模組,可以參考這篇文章來啟用:
在NodeJS上使用MagicCrypt
npmjs.com
npm 安裝指令
使用require
函數引入MagicCrypt後即可使用。
const MagicCrypt = require("magiccrypt");
const mc = new MagicCrypt("magickey", 256);
console.log(mc.encrypt("https://magiclen.org"));
console.log(mc.decrypt("jWEPYLTECqGvWJbdlRGeZIupoLX8N9DYZIUKMRp/OQY="));
在Rust上使用MagicCrypt
Crates.io
Cargo.toml
將magic_crypt
這個crate引入至程式範圍內即可使用。
#[macro_use]
extern crate magic_crypt;
use magic_crypt::MagicCryptTrait;
let mut mc = new_magic_crypt!("magickey", 256);
let base64 = mc.encrypt_str_to_base64("https://magiclen.org");
assert_eq!("DS/2U8royDnJDiNY2ps3f6ZoTbpZo8ZtUGYLGEjwLDQ=", base64);
assert_eq!("https://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個位元組的話,程式可以這樣寫:
#[macro_use] extern crate magic_crypt;
extern crate base64;
use std::io::Cursor;
use magic_crypt::MagicCryptTrait;
use magic_crypt::generic_array::typenum::U256;
let mut mc = new_magic_crypt!("magickey", 256);
let mut reader = Cursor::new("https://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!("https://magiclen.org", mc.decrypt_base64_to_string(&base64).unwrap());