顯示具有 Security 標籤的文章。 顯示所有文章
顯示具有 Security 標籤的文章。 顯示所有文章

2014年7月7日 星期一

使用 AES 加密後的資料長度

參考 [1-2],AES 演算法在加密時基本上不會變更檔案長度,所以原始檔案有多長,加密後就有多長。
不過因為 AES 是 block cipher,要加密的對象必須是 16 bytes 的倍數才能正常加密
因此演算法必須搭配 padding 的方法,而 padding 的方法就會影響加密後的長度。

從 [1] 的回應來看,如果 padding 的方法是使用 PKCS5 或 PKCS7 的話,加密後的長度如下:
cipherLen = (clearLen/16 + 1) * 16;

參考資料:
1、Size of data after AES encryption
2、How does PKCS#7 padding work with AES-256, CBC mode?

2013年7月31日 星期三

儲存密碼的方法:Java 的 Scrypt KDF 演算法

使用 Scrypt 的原因可以參考 [1-2],其中 [2] 是比較主要的原因~
大概是說一般常見的密碼儲存方法是使用 One-Way Hash Function 來把密碼變成一串亂碼,儲存在系統上
但不管使用哪種雜湊演算法,其實都不夠安全~主要原因來自於雜湊演算法的兩點特徵:

2013年7月11日 星期四

在 Java 使用加密演算法(六):KeyGenerator、Cipher、工作模式與填充方式之間的關係

記錄一下有關 KeyGenerator(在這個主題中,可以替換為 KeyPairGenerator 或 KeyFactory) 與 Cipher 之間的關係。

keyGenerator 是用來產生 Key 的產生器,取得 KeyGenerator 的實體必須透過 getInstance() 方法,同時需要輸入使用的演算法的名稱。
Cipher 則是用來進行加密、解密的工具,取得 Cipher 的實體也必須透過 getInstance() 方法,同時輸入要使用的演算法名稱。

2013年7月8日 星期一

在 Java 使用加密演算法(五):使用 RSA 加解密長資料的另一種實作方法

原本使用前篇「在 Java 使用加密演算法(三):使用 RSA 加解密長資料」中寫的方法在做 RSA 加解密
但不知道為什麼突然跑出 javax.crypto.BadPaddingException: Data must start with zero 的 Exception
找來找去都不知道問題出在哪.....後來查到 [1] 時發現好像有比較簡潔的寫法...。

2013年7月5日 星期五

在 Java 使用加密演算法(四):使用 Jasypt 與 Bouncy Castle 產生雜湊

要在 Java 做加密方法好像有不少,之前也曾 PO 過以內建的方法產生加密的文章。
不過基於希望程式碼簡潔、容易呼叫,並且能夠支援更多的加密演算法,所以還是嘗試找了一下
根據在 StackOverflow 社群上的問答 [1],在這裡嘗試了使用 Jasypt 與 Bouncy Castle 作為產生雜湊碼的方法。

2012年6月26日 星期二

在 Java 使用加密演算法(三):使用 RSA 加解密長資料

在前一篇文章「在 Java 使用加密演算法(二)」中雖然已經有用 RSA 加解密的程式碼
但那段程式碼沒有辦法加解密長度稍長的資料(大概是 100 多個 bytes 以上)
稍微查詢了一些資料,目前個人的了解應該是受限於 RSA 是個 block cipher
block cipher 本身的設計就是一次只用來加解密很小的資料。
網路上雖然討論在 Java 中使用 RSA 的文章很多
但多數似乎都假定看的人應該要知道 RSA 的 "一次加密" 只能加密很短的資料,就很少在文章中提到這件事....。
PS. "一次加密" 指的就是執行一次 RSA 的動作。

2012年6月25日 星期一

在 Java 使用加密演算法(二):使用 RSA 加密與解密(最後修改:2015-10-11)

利用 RSA 加密演算法來加密字串的範例如下~
這裡總共分成兩段程式碼,分別是 Main.java 以及 myEncryption.java
Main.java 是主要程序;myEncryption.java 進行加解密。

2012年6月19日 星期二

在 Java 使用加密演算法(一):產生與儲存 RSA 的金鑰(2017-07-19 調整)

正在嘗試要實作公開金鑰的加密演算法,目前搜尋了一些資料,原本有在疑惑應該選用哪個演算法
不過在搜尋到 [4] 以後,就決定還是用 RSA 了。

參考資料
1、JAVA 上加密演算法的實作範例
2、Generate Public and Private Keys
3、Save/Load Private and Public Key to/from a file
4、RSA Versus DSA and EL GAMAL
5、有關如何安全地儲存 key 的討論,可以看看:How to securely store a PrivateKey in code

這裡分成三個部分來討論,首先是要如何在 Java 裡面產生 RSA 的 Public Key 和 Private Key
以及把產生的兩把 Key 都儲存成檔案,再把它從檔案裡讀出來,最後才是真正把資料加密和解密。

生成金鑰

生成金鑰和讀寫金鑰可參考以下的程式碼,基本的程式碼是參考 [3],只有做小幅度的修改而已。
程式碼分為以下幾個 method:

  • main():程式的主要進入點,除了產生金鑰以外,也會負責呼叫其他 method。
  • printKeyPair():單純為了將產生的金鑰印出。而因為金鑰會是以 byte 形式存在的東西,因此必須透過某些編碼轉成能夠被印出來的字串。這裡是使用 Base64 編碼。
  • saveKeyPair():將金鑰分別輸出到指定路徑底下,並產生 “public.key” 和 “private.key” 兩個檔案。
  • loadKeyPair():從指定路徑中尋找 “public.key” 和 “private.key” 兩個檔案,並將他們載入成 KeyPair 型態到程式中。

以下就分別是各個 method 的原始碼以及相關的說明。

main()
public static void main(String[] args) throws Throwable {
  String path = "D:/encrypt";

  SecureRandom random = new SecureRandom();
  random.setSeed("test".getBytes());

  // Generate the key pair (public key and private key).
  KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
  // Specify that the key should be 1024-bit.
  keygen.initialize(1024, random);
  KeyPair generatedKeyPair = keygen.generateKeyPair();

  // Print the key. The key will be encoded by Base64.
  log.trace("Generated key pair:");
  printKeyPair(generatedKeyPair);

  // Store the key as files.
  log.info("Output the key pair to {}", path);
  saveKeyPair(path, generatedKeyPair);

  // Load the keys
  log.info("Loaded Key Pair from {}", path);
  KeyPair loadedKeyPair = loadKeyPair(path, "RSA"); // Load the keys from files

  // Print the loaded key pair to ensure that they are exactly the same.
  log.trace("Loaded key pair:");
  printKeyPair(loadedKeyPair);
}

4~5 行是用指定的字串去產生金鑰,透過這個方法的話,只要指定的字串是同一個,產生的金鑰就會是同一個。
這裡指定的字串是 "test"。
7~11 行是產生金鑰的程式碼,其中第 10 行的 1024 是指要產生的金鑰長度為 1024-bit
長度設得越長,產生金鑰所需要的時間也越長~
我的電腦產生 512 bits 的金鑰一瞬間就出來了,1024-bit 要大概一兩秒
2048-bit 就要稍微等個三秒之類的吧...(秒數是純感覺,沒有做過任何精準的測試)

11 行將 KeyPair 產生出來,也就是同時產生 Private Key 和 Public Key,會被存在同一個物件裡。
然後後面就依序是先把產生的金鑰印出來,接著把金鑰輸出成檔案
(預期輸出位置是 D:\encrypt\public.key 和 D:\encrypt\private.key)
然後再從檔案中把金鑰讀進來,再印出來,可以用眼睛確認跟輸出前是否長得一樣。

printKeyPair()

public static void printKeyPair(KeyPair keyPair) {
  log.trace("  public key: {}",
      Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));
  log.trace("  private key: {}",
      Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()));
}

這裡很單純,就只是利用 Java 原生支援的 Base64 編碼器,把金鑰的內容輸出成文字印出來而已。

saveKeyPair()

public static void saveKeyPair(String path, KeyPair keyPair) throws IOException {
  PrivateKey privateKey = keyPair.getPrivate();
  PublicKey publicKey = keyPair.getPublic();

  // Store Public Key.
  File fileForPublicKey = Paths.get(path, "public.key").toFile();
  log.trace("Public key will be output to '{}'.", fileForPublicKey);
  
  X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
  try (FileOutputStream fos = new FileOutputStream(fileForPublicKey)) {
    fos.write(x509EncodedKeySpec.getEncoded());
  }

  // Store Private Key.
  File fileForPrivateKey = Paths.get(path, "private.key").toFile();
  log.trace("Private key will be output to '{}'.", fileForPrivateKey);
  
  PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
  try (FileOutputStream fos = new FileOutputStream(fileForPrivateKey)) {
    fos.write(pkcs8EncodedKeySpec.getEncoded());
  }
}

5 ~ 7 行是產生路徑,實際執行起來,這裡產生的路徑會是 D:\encrypt\public.key。
9 ~ 12 行是把 Public Key 的內容輸出到檔案 D:\encrypt\public.key。

同樣地,接著 15 ~ 16 行也是產生路徑,實際執行時這個路徑會是 D:\encrypt\private.key。
18 ~ 21 行則是把 Private Key 的內容輸出到檔案 D:\encrypt\private.key。

loadKeyPair()

// Load the keys from files.
public static KeyPair loadKeyPair(String path, String algorithm)
    throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {

  // Initiate the factory with specified algorithm.
  KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
  
  // Read public key from file.
  File fileForPublicKey = Paths.get(path, "public.key").toFile();
  log.trace("Public key will be loaded from '{}'.", fileForPublicKey);
  
  PublicKey publicKey = null;
  try (FileInputStream fis = new FileInputStream(fileForPublicKey)) {
    byte[] loadedBytes = new byte[(int) fileForPublicKey.length()];
    fis.read(loadedBytes);
    
    X509EncodedKeySpec spec = new X509EncodedKeySpec(loadedBytes);
    publicKey = keyFactory.generatePublic(spec);
  }
  
  // Read private key from file.
  File fileForPrivateKey = Paths.get(path, "private.key").toFile();
  log.trace("Private key will be loaded from '{}'.", fileForPrivateKey);
  
  PrivateKey privateKey = null;
  try (FileInputStream fis = new FileInputStream(fileForPrivateKey)) {
    byte[] loadedBytes = new byte[(int) fileForPrivateKey.length()];
    fis.read(loadedBytes);
    
    PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(loadedBytes);
    privateKey = keyFactory.generatePrivate(privateKeySpec);
  }

  return new KeyPair(publicKey, privateKey);
}

這裡程式碼看起來好像比較長,但其實邏輯跟前一段 saveKeyPair() 沒什麼差別
就是組成路徑,然後讀檔案,再把讀入的 bytes 轉成金鑰的物件。因為流程上大同小異,這裡就先忽略細節了 XD

以上述的 method 全部放在同一個 Class 合併起來執行的話,會跑出類似下面這樣的 log 輸出:

2017-07-19 22:58:44,252 | Generated key pair:
2017-07-19 22:58:44,254 |   public key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvIaeWOTOx0aBWaz+fZNwsbork4e5RW94CbWR9rOtNE2jtKmgC6hPpWz7ANxzGL7jie9D/BpziyuSmhd3aquf4rmftiNdoTp55earfArlBO2D8Z0vaGnEUetDcahn0UD1bCekRC93iLzRNN73dm1T0CynFFrvFVA/CLgS/BvWEZwIDAQAB
2017-07-19 22:58:44,260 |   private key: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK8hp5Y5M7HRoFZrP59k3CxuiuTh7lFb3gJtZH2s600TaO0qaALqE+lbPsA3HMYvuOJ70P8GnOLK5KaF3dqq5/iuZ+2I12hOnnl5qt8CuUE7YPxnS9oacRR60NxqGfRQPVsJ6REL3eIvNE03vd2bVPQLKcUWu8VUD8IuBL8G9YRnAgMBAAECgYAMiyaLtfEj7VXEms3lxr2WWRyNpDkDjsbp+ZfXAImh7Z/4TK9Cdi2S6zwlXE0tTMG7Rw8DFSArhki2PKRVQyR2Jvp1m7v3ybwLSbP1GH9APKvfDZvjlNobzTPyut5W7jHFDVcAzSQujFcROKWG4gZk+CF9ywatmm3QaAIpiMgz8QJBAN3RlDj8OdzEcyoUNeYmszMWNEPJjoYMiiwrBYN7GCM5ZhK5fRF67PO38Y//PN1Ve5ViwGlKjyo9cRvIlVyRnnsCQQDKHlV71jCkeMherp0WqdtfAExd0BwzVR//CVHktTtCrutOcXOvEiZlnB92ExPzRCJDCD/gkqnU/h6UOM5tUoQFAkB95RFHNoBwuF7UpxvgQF68xAFt59uoYT2ay+AZO6f7dfxk7Dn7zdTmjqPfonGc/YNiyeWC3PpccvrbVgDPxSY5AkBF7wD8/DuQbQpHWHuaH+N7l4rU2vEnAck0YXEohVyf0g4w8iho5wrKFZ79J9S7U1PXhb80YQrKW7MQ7ibexLJRAkA5TcwKY9IEnYZcQlHI2ZZ0GZEL6TANxMXcFzr3OIk+3CHxBw0FKiG56zussDuRGdopey4KUEsCW19HupLsZamE
2017-07-19 22:58:44,260 | Output the key pair to D:/encrypt
2017-07-19 22:58:44,261 | Public key will be output to 'D:\encrypt\public.key'.
2017-07-19 22:58:44,262 | Private key will be output to 'D:\encrypt\private.key'.
2017-07-19 22:58:44,263 | Loaded Key Pair from D:/encrypt
2017-07-19 22:58:44,263 | Public key will be loaded from 'D:\encrypt\public.key'.
2017-07-19 22:58:44,264 | Private key will be loaded from 'D:\encrypt\private.key'.
2017-07-19 22:58:44,264 | Loaded key pair:
2017-07-19 22:58:44,264 |   public key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvIaeWOTOx0aBWaz+fZNwsbork4e5RW94CbWR9rOtNE2jtKmgC6hPpWz7ANxzGL7jie9D/BpziyuSmhd3aquf4rmftiNdoTp55earfArlBO2D8Z0vaGnEUetDcahn0UD1bCekRC93iLzRNN73dm1T0CynFFrvFVA/CLgS/BvWEZwIDAQAB
2017-07-19 22:58:44,264 |   private key: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK8hp5Y5M7HRoFZrP59k3CxuiuTh7lFb3gJtZH2s600TaO0qaALqE+lbPsA3HMYvuOJ70P8GnOLK5KaF3dqq5/iuZ+2I12hOnnl5qt8CuUE7YPxnS9oacRR60NxqGfRQPVsJ6REL3eIvNE03vd2bVPQLKcUWu8VUD8IuBL8G9YRnAgMBAAECgYAMiyaLtfEj7VXEms3lxr2WWRyNpDkDjsbp+ZfXAImh7Z/4TK9Cdi2S6zwlXE0tTMG7Rw8DFSArhki2PKRVQyR2Jvp1m7v3ybwLSbP1GH9APKvfDZvjlNobzTPyut5W7jHFDVcAzSQujFcROKWG4gZk+CF9ywatmm3QaAIpiMgz8QJBAN3RlDj8OdzEcyoUNeYmszMWNEPJjoYMiiwrBYN7GCM5ZhK5fRF67PO38Y//PN1Ve5ViwGlKjyo9cRvIlVyRnnsCQQDKHlV71jCkeMherp0WqdtfAExd0BwzVR//CVHktTtCrutOcXOvEiZlnB92ExPzRCJDCD/gkqnU/h6UOM5tUoQFAkB95RFHNoBwuF7UpxvgQF68xAFt59uoYT2ay+AZO6f7dfxk7Dn7zdTmjqPfonGc/YNiyeWC3PpccvrbVgDPxSY5AkBF7wD8/DuQbQpHWHuaH+N7l4rU2vEnAck0YXEohVyf0g4w8iho5wrKFZ79J9S7U1PXhb80YQrKW7MQ7ibexLJRAkA5TcwKY9IEnYZcQlHI2ZZ0GZEL6TANxMXcFzr3OIk+3CHxBw0FKiG56zussDuRGdopey4KUEsCW19HupLsZamE
透過 RSA 加密與解密

請參考下篇文章:在 Java 使用加密演算法(二):使用 RSA 加密與解密

附錄:對稱金鑰加密(symmetric-key algorithm)

原本弄錯了以為 AES 也是屬於公開金鑰加密,不過後來查到的程式碼覺得怪怪的
於是仔細查了一下 AES,發現它應該是對稱金鑰加密的演算法才對~(參閱下面參考資料中的連結 6)
也就是發送端與接收端共用同一個 secret key。

AES 破解的資訊可參考:New Attack on AES
微軟在 2011 年 8 月發現可以加快破解 AES 的方法,例如以 AES-128 來說,可以將複雜度降低到 2126.1
不過其實 2126.1 還是很多 XD,也就是目前來說 AES 還是安全性可接受的加密方法~。

參考資料:
6、AES encryption, what are public and private keys?
7、Using AES with Java Technology
8、【java】AES加密解密|及Base64的使用
9、Java AES Encrypt & Decrypt Example(加解密)
10、JAVA AES 加密步骤解释