iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 4
0
Security

安全地寫 Java 的「基本功」系列 第 4

安全地寫 Java 的 「基本功」- Day 3

再論機密性

週末專案的第二天,我們繼續深入思考昨天的案例。

當你想簡單地加密一個東西時,你會使用加密壓縮的機制。然而,認真的工程師不會便宜行事。我們如何真正地、有效地加密一個檔案呢?

AES 加解密

若上 Google 稍微查查,會發現 Java AES 加解密的說明與文章不少。例如:Java JCE - AES 的 Encryption & Decryption @2016-05-01 這篇很推薦各位閱讀。

因此,我們就不拿別人的程式來充自己的版面了。我們先寫一個這樣的程式:

First Test

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 要放那呢?檔案系統?資料庫?保管箱?還是吃進去肚子裡比較安全?

欲知詳情,請待明日分曉。


上一篇
安全地寫 Java 的 「基本功」- Day 2
下一篇
安全地寫 Java 的 「基本功」- Day 4
系列文
安全地寫 Java 的「基本功」14

尚未有邦友留言

立即登入留言