2013年7月5日 星期五

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

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

Jasypt 嚴格來說算是一個介面,提供更簡易的操作方法可以去使用 JCE 元件
Jasypt 本身並沒有實作任何的加密演算法,僅止於提供簡易操作的介面而已,背後的加密演算法是由其他 Provider 提供的。
例如有些特殊的演算法在原生的 JCE 似乎沒有支援,因此又可以額外引入 Bouncy Castle 這個第三方的 JCE 實作函式庫,作為 Jasypt 的 Provider。
另外 Jasypt 目前似乎僅提供 PBC-based 的加密方法的介面,因此有些狀況可能不太適用。

使用 Jasypt 和 Bouncy Castle 時,環境本身在 JDK 6 之下沒有特別需要事先引用的函式庫
因此單純把 Jasypt 和 Bouncy Castle 的函式庫放進執行環境的函式庫路徑即可。
這裡使用的版本是 Jasypt 1.9.0 和 Bouncy Castle 1.49。

首先先試試產生 WHIRLPOOL 演算法的雜湊碼,程式碼如下:
public String getWhirlpoolHash (String st) {
    StandardStringDigester digester = new StandardStringDigester();
    digester.setProvider(new BouncyCastleProvider());
    digester.setAlgorithm("WHIRLPOOL");
    return digester.digest(st);
  }
}

如果說最後要輸出的是字串,可以如上述的範例一樣,使用 StandardStringDigester 來產生輸出 String 的方法。
輸出的東西印出來長得像這樣:
VPJQG3dBxaWpXrD0Qzvw5KiAMFrb9+u0619YEKqdHw6XeAlTOb/PiH3Mlo5+zJn50I+cdxXeyFh4INTIJyQ6LIovsAGqo/eU

如果想要產生出 byte[],可以把 StandardStringDigester 換成 StandardByteDigester
需要輸出 String 時,則利用 org.bouncycastle.util.encoders.Base64 這個類別來進行,例如:
public String getCredentialBytes (String st) {
  StandardByteDigester digester = new StandardByteDigester();
  digester.setProvider(new BouncyCastleProvider());
  digester.setAlgorithm("WHIRLPOOL");
  byte[] hashedBytes = digester.digest(st.getBytes());
  return Base64.toBase64String(hashedBytes);
}

不過測試時,也許會發現做雜湊時,每次產生的結果都不一樣!這其實在 Jasypt 的 Javadoc 上有說明
Digester 本身有提供設定 SaltGenerator 的函式,如果沒有特別設定的話,預設會自動使用 RandomSaltGenerator 作為 SaltGenerator
也因此,每次執行時的 salt 預設都不一樣,當然產生的雜湊碼也會都不同了。

根據 SaltGenerator 文件 [4] 中的連結,可以看出有一些 Fixed 開頭的介面實作是可以產出固定的 salt
以 FixedStringSaltGenerator 為例的話,使用 StandardStringDigester 時可以改成以下的程式碼,就可以每次都產生一樣的雜湊碼了:
public String getWhirlpoolHash (String st) {
    FixedStringSaltGenerator saltGen = new FixedStringSaltGenerator();
    saltGen.setSalt("qwerty!@#$");

    StandardStringDigester digester = new StandardStringDigester();
    digester.setProvider(new BouncyCastleProvider());
    digester.setSaltGenerator(saltGen);
    digester.setAlgorithm("WHIRLPOOL");
    return digester.digest(st);
  }
}

不過有個特別的地方要注意,使用 FixedStringSaltGenerator 時好像有些特殊的要求,文件中的描述如下:
If the requested salt has a size in bytes smaller than the specified salt, the first n bytes are returned. If it is larger, an exception is thrown.
似乎是說指定的 salt 長度不對時,就會噴出 Exception。而噴出的 Exception 大概是這樣:
org.jasypt.exceptions.EncryptionInitializationException: Requested salt larger than set
    at org.jasypt.salt.FixedStringSaltGenerator.generateSalt(FixedStringSaltGenerator.java:106)
    at org.jasypt.digest.StandardByteDigester.digest(StandardByteDigester.java:929)
    at org.jasypt.digest.StandardStringDigester.digest(StandardStringDigester.java:922)

目前我還沒完全了解它需要的規則是怎麼樣,總之只能大概知道 salt 有某些規範要遵循~。


參考資料:
1、What is best the Open Source Java Encryption Lib?
2、Jasypt: Java simplified encryption
3、The Legion of the Bouncy Castle
4、org.jasypt.salt.SaltGenerator

沒有留言: