週末專案的第二天,我們繼續深入思考昨天的案例。
當你想簡單地加密一個東西時,你會使用加密壓縮的機制。然而,認真的工程師不會便宜行事。我們如何真正地、有效地加密一個檔案呢?
若上 Google 稍微查查,會發現 Java AES 加解密的說明與文章不少。例如:Java JCE - AES 的 Encryption & Decryption @2016-05-01 這篇很推薦各位閱讀。
因此,我們就不拿別人的程式來充自己的版面了。我們先寫一個這樣的程式:
package javaxx.cipher;
import java.io.File;
import java.io.FileInputStream;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import static javaxx.cipher.EncryptionParameter.ALGORITHM_AES;
import static javaxx.cipher.EncryptionParameter.STRENGTH_256;
/**
* AdvancedAesTest
* cchuang, 2016/12/4.
*/
public class AdvancedAesTest {
private static final String SOURCE_FILE = "/test.png";
private static final String ENCRYPTED_FILE = "test.png.enc";
private static final String DECRYPTED_FILE = "test.png.dec";
private EncryptionParameter param;
private File sourceFile;
private File encryptedFile;
private File decryptedFile;
@Before
public void setUp() throws Exception {
param = initialEncryptionParameter();
sourceFile = initSourceFile();
encryptedFile = initEmptyFileBySource(sourceFile, ENCRYPTED_FILE);
decryptedFile = initEmptyFileBySource(sourceFile, DECRYPTED_FILE);
}
private EncryptionParameter initialEncryptionParameter() throws NoSuchAlgorithmException {
return EncryptionParameter.initEncryptionParameter(ALGORITHM_AES, STRENGTH_256);
}
private File initSourceFile() throws URISyntaxException {
File fileInClasspath = new File(this.getClass().getResource(SOURCE_FILE).toURI());
return fileInClasspath;
}
private File initEmptyFileBySource(File exists, String fileName) {
File encryptedFile = exists.toPath()
.getParent()
.resolve(fileName)
.toFile();
return encryptedFile;
}
@Test
public void testEncryptFile() throws Exception {
EncryptionUtils.encryptFile(param, sourceFile, encryptedFile);
checkEncryptedFile();
EncryptionUtils.decryptFile(param, encryptedFile, decryptedFile);
checkDecryptedFile();
encryptedFile.delete();
}
private void checkEncryptedFile() throws IOException {
Assert.assertTrue(encryptedFile.exists());
Assert.assertFalse(
Arrays.equals(IOUtils.toByteArray(new FileInputStream(sourceFile)),
IOUtils.toByteArray(new FileInputStream(encryptedFile))));
}
private void checkDecryptedFile() throws IOException {
Assert.assertTrue(decryptedFile.exists());
Assert.assertTrue(
Arrays.equals(IOUtils.toByteArray(new FileInputStream(sourceFile)),
IOUtils.toByteArray(new FileInputStream(decryptedFile))));
}
}
EncryptionParameter 與 EncryptionUtils 是使用適才的說明文件所整理出來的檔案加密版,程式部份可以在 AdvancedAES 這 Branch 看到。
我們在處理、面對這世界上各樣的問題時,問題本身的技術複雜度是一回事,然而問題的處境是另外一回事。我有些朋友,他們開咖啡店,各自有各自的特色,而且不論是沖咖啡、烘豆子,技術都是一流的。但是坦白說,生意呢,則是各有千秋。這是大家都明白的道理,會做吃的,不等於會賣吃的
同樣的,能用好的加密演算法是一回事,但是把這演算法放到什麼處境下使用,卻是另一回事。當我們產生了金鑰之後,就出現了金鑰保管的問題。
例如:
* 用金鑰 1 加密檔案
* 用金鑰 2 加密金鑰 1
* ...
* 用金鑰 n + 1 加密金鑰 n
於是乎,我們必須有一把終極的金鑰,實務上,就叫 master key。實際運作時,會用這把 master key 去對所有的金鑰進行加解密的動作。
但這把 master key 要放那呢?檔案系統?資料庫?保管箱?還是吃進去肚子裡比較安全?
欲知詳情,請待明日分曉。