2008-12-16 407 views
6

我正試圖學習如何使用基於密碼的Java加密。我在網上找到了幾個例子,但沒有發現Stack Overflow。這些例子對我的解釋略有點亮,特別是關於算法選擇。似乎有很多傳遞字符串來說明要使用哪些算法,但關於字符串來自哪裏以及它們的含義的文檔很少。而且它看起來像不同的算法可能需要不同的KeySpec類的實現,所以我不確定哪些算法可以使用我正在查看的PBEKeySpec類。此外,這些例子看起來都有點過時,許多例子要求你獲得一箇舊的加密包,它以前不是JDK的一部分,甚至不是第三方的實現。Java密碼加密

有人可以直接介紹我需要做些什麼來實現加密(字符串數據,字符串密碼)和解密(字節[]數據,字符串密碼)?

+0

你想在這裏做密碼/短語哈希或數據加密(如PGP或SSL與密碼短語)嗎?標題對前者有點誤導,但我認爲你想要做後者... – JeeBee 2008-12-16 19:01:59

+0

我想要做前者,但我不知道你爲什麼稱它爲哈希。我希望它是可逆的。用密碼思考DES或AES加密。正如你所描述的那樣,對稱加密不是非對稱的。 – skiphoppy 2008-12-16 19:07:11

回答

11

我會謹慎提供或從論壇採取與安全有關的意見...具體細節非常複雜,並且很快就會過時。

話雖如此,我認爲Sun的Java Cryptography Architecture (JCA) Reference Guide是一個很好的起點。查看附帶的說明基於密碼的加密(PBE)的code example

順便說一句,標準JRE僅提供PBE的一些開箱即用的選項(「PBEWithMD5AndDES」就是其中之一)。要獲得更多選擇,您需要使用「強加密包」或某些第三方提供商,如Bouncy Castle。另一種選擇是使用JRE中提供的散列和密碼算法來實現您自己的PBE。您可以通過這種方式實現SHA-256和AES-128的PBE(sample encrypt/decrypt methods)。

簡而言之,對於PBE的加密方法可以包括以下步驟:

  1. 從用戶獲取密碼和明文,並將其轉換爲字節數組。
  2. 生成安全隨機salt
  3. 將salt附加到密碼並計算其加密hash。重複這麼多次。
  4. 使用生成的散列作爲initialization vector和/或祕密key加密明文。
  5. 保存鹽和由此產生的密文。
2

你需要一個加密庫,它會告訴你如何設置它。
我碰巧喜歡來自bouncycastle.org的東西。你可以找到他們的方法,如在5.1例子中提到的DES,它是它們提供的加密之一。實際字符串的含義取決於提供商。 基本上你加載庫。

​​

,然後只使用JCE接口做任何你想要的:

keyGen = KeyGenerator.getInstance("DES", "BC"); 

的Java處理庫,併爲您的接口綁定,你不必這樣做。 如果您有任何問題,我會更樂意解釋更多。不幸的是,目前我患有「我不記得我是如何學習它」的疾病,請隨時提問。

1

您可以使用哈希算法(如果需要多次)從密碼中獲取某些可用作密鑰的原始數據(如果算法需要,則使用初始化向量)。然後,您可以使用該密鑰與任何對稱算法 - 如3DES-CBC或AES-CBC(DES現在認爲已經過時)。

根據JCE的不同,您可能有不同的算法供您選擇,但AES可能是您想要的。然而,算法的選擇以及如何使用它,這在某種程度上是一個宗教問題,並且您不應該嘗試自己推出,甚至嘗試使用標準算法構建自己的加密方案。如果你還沒有研究過,那麼你幾乎肯定會弄錯的,甚至有可能。

如果安全性對您來說很重要,您正在考慮加密,那麼您還應該考慮查看安全工程書籍,例如Br​​uce Schneier的應用密碼學或Ross Anderson的安全工程 - 有很多實施陷阱。例如,使用密碼作爲密鑰首先不是一個好主意,因爲它本質上減少了密鑰的大小。

你也可以看看其他人做設計,有很多在IETF,如: http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha1-00

0

如果您不需要解密密碼,但只需根據密碼/密碼生成加密密鑰,則可以使用JCE密碼和MessageDigest類實現PKCS#5 standard

4

使用RFC2898從密碼生成密鑰。據我所知,這不包含在JRE或JCE中,但它包含在諸如JBoss,Oracle和WebSphere之類的J2EE服務器中。它也包含在.NET Base Class Library(Rfc2898DeriveBytes)中。

Java中有一些LGPL實現,但快速查看this one看起來有點過於複雜。還有一個很好的javascript version。 (我製作了a modified version of that one並將它打包爲Windows腳本組件)

由於缺少一個合適的許可證很好的實現,我打包了一些來自Mattias Gartner的代碼。這是整個代碼。簡短,簡單,易於理解。它的許可證號爲MS Public License

// PBKDF2.java 
// ------------------------------------------------------------------ 
// 
// RFC2898 PBKDF2 in Java. The RFC2898 defines a standard algorithm for 
// deriving key bytes from a text password. This is sometimes 
// abbreviated "PBKDF2", for Password-based key derivation function #2. 
// 
// There's no RFC2898-compliant PBKDF2 function in the JRE, as far as I 
// know, but it is available in many J2EE runtimes, including those from 
// JBoss, IBM, and Oracle. 
// 
// It's fairly simple to implement, so here it is. 
// 
// Created Sun Aug 09 01:06:57 2009 
// 
// last saved: 
// Time-stamp: <2009-August-09 02:19:50> 
// ------------------------------------------------------------------ 
// 
// code thanks to Matthias Gartner 
// 
// ------------------------------------------------------------------ 

package cheeso.examples; 


import java.security.NoSuchAlgorithmException; 
import java.security.InvalidKeyException; 
import javax.crypto.Mac; 
import javax.crypto.spec.SecretKeySpec; 


public class PBKDF2 
{ 
    public static byte[] deriveKey(byte[] password, byte[] salt, int iterationCount, int dkLen) 
     throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException 
    { 
     SecretKeySpec keyspec = new SecretKeySpec(password, "HmacSHA1"); 
     Mac prf = Mac.getInstance("HmacSHA1"); 
     prf.init(keyspec); 

     // Note: hLen, dkLen, l, r, T, F, etc. are horrible names for 
     //  variables and functions in this day and age, but they 
     //  reflect the terse symbols used in RFC 2898 to describe 
     //  the PBKDF2 algorithm, which improves validation of the 
     //  code vs. the RFC. 
     // 
     // dklen is expressed in bytes. (16 for a 128-bit key) 

     int hLen = prf.getMacLength(); // 20 for SHA1 
     int l = Math.max(dkLen, hLen); // 1 for 128bit (16-byte) keys 
     int r = dkLen - (l-1)*hLen;  // 16 for 128bit (16-byte) keys 
     byte T[] = new byte[l * hLen]; 
     int ti_offset = 0; 
     for (int i = 1; i <= l; i++) { 
      F(T, ti_offset, prf, salt, iterationCount, i); 
      ti_offset += hLen; 
     } 

     if (r < hLen) { 
      // Incomplete last block 
      byte DK[] = new byte[dkLen]; 
      System.arraycopy(T, 0, DK, 0, dkLen); 
      return DK; 
     } 
     return T; 
    } 


    private static void F(byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex) { 
     final int hLen = prf.getMacLength(); 
     byte U_r[] = new byte[ hLen ]; 
     // U0 = S || INT (i); 
     byte U_i[] = new byte[S.length + 4]; 
     System.arraycopy(S, 0, U_i, 0, S.length); 
     INT(U_i, S.length, blockIndex); 
     for(int i = 0; i < c; i++) { 
      U_i = prf.doFinal(U_i); 
      xor(U_r, U_i); 
     } 

     System.arraycopy(U_r, 0, dest, offset, hLen); 
    } 

    private static void xor(byte[] dest, byte[] src) { 
     for(int i = 0; i < dest.length; i++) { 
      dest[i] ^= src[i]; 
     } 
    } 

    private static void INT(byte[] dest, int offset, int i) { 
     dest[offset + 0] = (byte) (i/(256 * 256 * 256)); 
     dest[offset + 1] = (byte) (i/(256 * 256)); 
     dest[offset + 2] = (byte) (i/(256)); 
     dest[offset + 3] = (byte) (i); 
    } 

    // ctor 
    private PBKDF2() {} 

} 
3

在上面的Cheeso非常有幫助的答案中,有一個糟糕的性能問題。

int l = Math.max(dkLen, hLen) 

不應caculate最大,但分工的天花板,所以

int l = ((dkLen - 1)/hLen) + 1; // >= ceil(dkLen/hLen), == for dkLen =>1 

這將通過20的16個字節密鑰的因素加速計算。

0

在加密期間將字符串轉換爲字節數組。解密後轉換回字符串。

/** 
* Creates a cipher for encryption or decryption. 
* 
* @param algorithm PBE algorithm like "PBEWithMD5AndDES" or "PBEWithMD5AndTripleDES". 
* @param mode Encyrption or decyrption. 
* @param password Password 
* @param salt Salt usable with algorithm. 
* @param count Iterations. 
* @return Ready initialized cipher. 
* @throws GeneralSecurityException Error creating the cipher. 
*/ 
private static Cipher createCipher(final String algorithm, final int mode, final char[] password, final byte[] salt, final int count) throws GeneralSecurityException { 
    final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); 
    final PBEKeySpec keySpec = new PBEKeySpec(password); 
    final SecretKey key = keyFactory.generateSecret(keySpec); 
    final Cipher cipher = Cipher.getInstance(algorithm); 
    final PBEParameterSpec params = new PBEParameterSpec(salt, count); 
    cipher.init(mode, key, params); 
    return cipher; 
} 

/** 
* Encrypts some data based on a password. 
* @param algorithm PBE algorithm like "PBEWithMD5AndDES" or "PBEWithMD5AndTripleDES" 
* @param data Data to encrypt 
* @param password Password 
* @param salt Salt usable with algorithm 
* @param count Iterations. 
* @return Encrypted data. 
*/ 
public static byte[] encryptPasswordBased(final String algorithm, final byte[] data, final char[] password, final byte[] salt, final int count) { 
    Validate.notNull(algorithm); 
    Validate.notNull(data); 
    Validate.notNull(password); 
    Validate.notNull(salt); 
    try { 
     final Cipher cipher = createCipher(algorithm, Cipher.ENCRYPT_MODE, password, salt, count); 
     return cipher.doFinal(data); 
    } catch (final Exception ex) { 
     throw new RuntimeException("Error encrypting the password!", ex); 
    } 
} 

/** 
* Decrypts some data based on a password. 
* @param algorithm PBE algorithm like "PBEWithMD5AndDES" or "PBEWithMD5AndTripleDES" 
* @param encryptedData Data to decrypt 
* @param password Password 
* @param salt Salt usable with algorithm 
* @param count Iterations. 
* @return Encrypted data. 
*/ 
public static byte[] decryptPasswordBased(final String algorithm, final byte[] encryptedData, final char[] password, final byte[] salt, final int count) { 
    Validate.notNull(algorithm); 
    Validate.notNull(encryptedData); 
    Validate.notNull(password); 
    Validate.notNull(salt); 
    try { 
     final Cipher cipher = createCipher(algorithm, Cipher.DECRYPT_MODE, password, salt, count); 
     return cipher.doFinal(encryptedData); 
    } catch (final Exception ex) { 
     throw new RuntimeException("Error decrypting the password!", ex); 
    } 
}