2008-12-16 50 views
6

Tôi đang cố gắng tìm hiểu cách thực hiện mã hóa dựa trên cụm mật khẩu bằng Java. Tôi đang tìm một số ví dụ trực tuyến, nhưng không có (chưa) trên Stack Overflow. Các ví dụ là một chút ánh sáng trên lời giải thích cho tôi, đặc biệt là về lựa chọn thuật toán. Dường như có rất nhiều chuỗi đi qua xung quanh để nói những thuật toán nào để sử dụng, nhưng ít tài liệu về vị trí của chuỗi và ý nghĩa của chúng. Và nó cũng có vẻ như các thuật toán khác nhau có thể yêu cầu triển khai khác nhau của lớp KeySpec, vì vậy tôi không chắc chắn những thuật toán nào có thể sử dụng lớp PBEKeySpec mà tôi đang xem xét. Hơn nữa, tất cả các ví dụ dường như hơi lỗi thời, nhiều người yêu cầu bạn lấy một gói mã hóa cũ hơn không phải là một phần của JDK, hoặc thậm chí là việc thực hiện của bên thứ ba.Mã hóa cụm mật khẩu Java

Ai đó có thể giới thiệu đơn giản về những gì tôi cần làm để thực hiện mã hóa (dữ liệu chuỗi, cụm mật khẩu chuỗi) và giải mã (dữ liệu byte [], cụm mật khẩu chuỗi)?

+0

Bạn có muốn thực hiện băm mật khẩu/cụm từ hoặc mã hóa dữ liệu (như PGP hoặc SSL bằng cụm mật khẩu) tại đây không? Tiêu đề là một chút gây hiểu nhầm cho các cựu, nhưng tôi nghĩ rằng bạn muốn làm thứ hai ... – JeeBee

+0

Tôi muốn làm điều cũ, nhưng tôi không biết tại sao bạn đang gọi nó băm. Tôi muốn nó có thể đảo ngược. Hãy suy nghĩ mã hóa DES hoặc AES bằng cụm mật khẩu. Mã hóa đối xứng, không đối xứng như bạn mô tả. – skiphoppy

+0

Tại sao bạn cần mã hóa cụm từ mật khẩu có thể đảo ngược? Đó không phải là điều mong muốn. – JeeBee

Trả lời

11

Sẽ thận trọng khi cho hoặc nhận tư vấn an ninh liên quan đến từ một diễn đàn ... các chi tiết cụ thể khá phức tạp và thường trở nên lỗi thời một cách nhanh chóng.

Có nói rằng, tôi nghĩ rằng Mặt trời là Java Cryptography Architecture (JCA) Reference Guide là điểm khởi đầu tốt. Hãy xem code example minh họa Mã hóa dựa trên mật khẩu (PBE) kèm theo.

Btw, JRE chuẩn chỉ cung cấp một vài tùy chọn ngoài hộp cho PBE ("PBEWithMD5AndDES" là một trong số chúng). Để có thêm lựa chọn, bạn sẽ cần "gói mã hóa mạnh" hoặc một số nhà cung cấp bên thứ ba như Bouncy Castle. Một giải pháp thay thế khác là triển khai PBE của riêng bạn bằng cách sử dụng thuật toán băm và mật mã được cung cấp trong JRE. Bạn có thể thực hiện PBE với SHA-256 và AES-128 theo cách này (sample encrypt/decrypt methods).

Tóm lại, phương pháp mã hóa cho PBE có thể bao gồm các bước sau:

  1. Nhận Mật khẩu và cleartext từ người dùng, và chuyển đổi chúng sang mảng byte.
  2. Tạo ngẫu nhiên an toàn salt.
  3. Nối muối vào mật khẩu và tính toán mật mã của nó hash. Lặp lại điều này nhiều lần.
  4. Mã hóa văn bản rõ ràng bằng cách sử dụng băm kết quả là initialization vector và/hoặc bí mật key.
  5. Lưu muối và bản mã kết quả.
2

Bạn cần thư viện mã hóa, thư viện này sẽ cho bạn biết cách thiết lập.
Tôi tình cờ thích những thứ từ bouncycastle.org. Bạn có thể tìm thấy cách thức của họ để here DES tham chiếu trong ví dụ 5.1, là một trong các mã hóa mà chúng cung cấp. Chuỗi thực tế có nghĩa là gì, sẽ phụ thuộc vào nhà cung cấp. Về cơ bản bạn tải thư viện.

Security.addProvider(new BouncyCastleProvider()); 

Và sau đó chỉ sử dụng các giao diện JCE để làm bất cứ điều gì bạn muốn:

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

Java xử lý các ràng buộc của thư viện và các giao diện cho bạn, bạn không cần phải làm điều đó . Tôi rất vui khi được giải thích thêm, nếu bạn có bất kỳ câu hỏi nào. Thật không may tại thời điểm này tôi đang bị "Tôi không thể nhớ làm thế nào tôi đã học được nó" bệnh, vì vậy xin vui lòng yêu cầu.

1

Bạn có thể sử dụng thuật toán băm (nhiều lần nếu cần) để lấy từ cụm mật khẩu cho một số dữ liệu thô mà bạn có thể sử dụng làm khóa (+ vectơ khởi tạo nếu thuật toán gọi một).

Sau đó, bạn có thể sử dụng khóa đó bằng bất kỳ thuật toán đối xứng nào - chẳng hạn như 3DES-CBC hoặc AES-CBC (DES được coi là lỗi thời trong những ngày này).

Tùy thuộc vào JCE bạn có sẵn, bạn có thể có các thuật toán khác nhau theo ý của mình, nhưng AES có lẽ là những gì bạn muốn. Tuy nhiên, lựa chọn thuật toán và chính xác cách sử dụng nó có thể là vấn đề tôn giáo, và bạn sẽ bị bệnh nên thử và tự mình cuộn, hoặc thậm chí thử và xây dựng một số lược đồ mã hóa của riêng bạn bằng cách sử dụng các thuật toán chuẩn. Bạn gần như chắc chắn sẽ làm cho nó sai nếu bạn không nghiên cứu nó, và thậm chí nếu bạn có.

Nếu bảo mật quan trọng đối với bạn mà bạn đang xem xét mã hóa, thì bạn cũng nên xem xét cuốn sách kỹ thuật bảo mật như Mật mã ứng dụng của Bruce Schneier hoặc Security Engineering của Ross Anderson - có rất nhiều cạm bẫy triển khai. Ví dụ, sử dụng cụm từ mật khẩu là khóa không phải là một ý tưởng tuyệt vời ở vị trí đầu tiên, vì về cơ bản nó làm giảm kích thước khóa của bạn.

Bạn cũng có thể nhìn vào thiết kế mà người khác đã làm, có rất nhiều tại IETF, ví dụ: http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha1-00

0

Nếu bạn không cần giải mã cụm mật khẩu, nhưng chỉ cần tạo khóa mã hóa dựa trên mật khẩu/cụm mật khẩu, bạn có thể triển khai PKCS#5 standard, sử dụng các lớp JCE Cipher và MessageDigest.

4

Sử dụng RFC2898 để tạo khóa từ mật khẩu. Điều này không được bao gồm trong JRE hoặc JCE, theo như tôi biết, nhưng nó được bao gồm trong các Máy chủ J2EE như JBoss, Oracle và WebSphere. Nó cũng được bao gồm trong thư viện lớp cơ sở .NET (Rfc2898DeriveBytes).

Có một số triển khai LGPL trong Java ngoài kia, nhưng trên giao diện nhanh, this one trông hơi phức tạp một chút. Ngoài ra còn có một số javascript version tốt. (Tôi đã sản xuất a modified version of that one và đóng gói nó như là một thành phần Windows Script)

Thiếu một thực hiện tốt với giấy phép thích hợp, tôi đóng gói một số mã từ Mattias Gartner. Đây là mã toàn bộ. Ngắn gọn, đơn giản, dễ hiểu. Nó được cấp phép theo số 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() {} 

} 
+1

Cảm ơn bạn rất nhiều. Tôi đã xác minh rằng trong các trường hợp thử nghiệm của tôi, mã này tạo ra các kết quả tương tự như thuật toán được xây dựng trong JDK 6 có tên PBKDF2WithHmacSHA1. –

+1

Dòng 'int l = Math.max (dkLen, hLen) ' không nên caculate max, nhưng trần của bộ phận, vì vậy ' int l = ((dkLen - 1)/hLen) + 1; //> = ceil (dkLen/hLen), == cho dkLen => 1' Điều này sẽ tăng tốc độ tính toán theo hệ số 20 cho 16 byte. – Joqn

3

Trong câu trả lời rất hữu ích của Cheeso ở trên, có lỗi hiệu suất kém.

Dòng

int l = Math.max(dkLen, hLen) 

không nên caculate max, nhưng trần của bộ phận, vì vậy

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

này sẽ tăng tốc độ tính toán theo hệ số 20 cho 16 phím byte.

0

Chuyển chuỗi của bạn thành mảng byte trong khi mã hóa. Chuyển đổi lại thành chuỗi sau khi giải mã.

/** 
* 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); 
    } 
} 
Các vấn đề liên quan