2011-11-08 18 views
76

Tôi cố gắng để thực hiện mật khẩu có trụ sở thuật toán mã hóa, nhưng tôi nhận được ngoại lệ này:Với khối thức không đúng đệm

javax.crypto.BadPaddingException: Với khối thức không đúng đệm

gì có thể là vấn đề? (Tôi mới vào Java.)

Đây là mã của tôi:

public class PasswordCrypter { 

    private Key key; 
    public PasswordCrypter(String password) { 

       try{ 
        KeyGenerator generator; 
        generator = KeyGenerator.getInstance("DES"); 
        SecureRandom sec = new SecureRandom(password.getBytes()); 
        generator.init(sec); 
        key = generator.generateKey(); 
       } 
     catch (Exception e) { 
      e.printStackTrace(); 
     } 

    } 


    public byte[] encrypt(byte[] array) throws CrypterException { 

     try{ 
      Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 
      cipher.init(Cipher.ENCRYPT_MODE, key); 

       return cipher.doFinal(array); 
     }catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return null; 
    } 

    public byte[] decrypt(byte[] array) throws CrypterException{ 

     try{ 
      Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 
      cipher.init(Cipher.DECRYPT_MODE, key); 

      return cipher.doFinal(array); 
     }catch(Exception e){ 
      e.printStackTrace(); 
     } 


     return null; 
    } 
} 

(Các JUnit thử nghiệm)

public class PasswordCrypterTest { 
     private static final byte[] MESSAGE = "Alpacas are awesome!".getBytes(); 

     private PasswordCrypter[] passwordCrypters; 
     private byte[][] encryptedMessages; 

    @Before 
    public void setUp() { 
     passwordCrypters = new PasswordCrypter[] { 
      new PasswordCrypter("passwd"), 
      new PasswordCrypter("passwd"), 
      new PasswordCrypter("otherPasswd") 
     }; 

     encryptedMessages = new byte[passwordCrypters.length][]; 
     for (int i = 0; i < passwordCrypters.length; i++) { 
      encryptedMessages[i] = passwordCrypters[i].encrypt(MESSAGE); 
     } 
    } 

    @Test 
    public void testEncrypt() { 
     for (byte[] encryptedMessage : encryptedMessages) { 
      assertFalse(Arrays.equals(MESSAGE, encryptedMessage)); 
     } 

     assertFalse(Arrays.equals(encryptedMessages[0], encryptedMessages[2])); 
     assertFalse(Arrays.equals(encryptedMessages[1], encryptedMessages[2])); 
    } 

    @Test 
    public void testDecrypt() { 
     for (int i = 0; i < passwordCrypters.length; i++) { 
      assertArrayEquals(MESSAGE, passwordCrypters[i].decrypt(encryptedMessages[i])); 
     } 

     assertArrayEquals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[1])); 
     assertArrayEquals(MESSAGE, passwordCrypters[1].decrypt(encryptedMessages[0])); 

     try { 
      assertFalse(Arrays.equals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[2]))); 
     } catch (CrypterException e) { 
      // Anything goes as long as the above statement is not true. 
     } 

     try { 
      assertFalse(Arrays.equals(MESSAGE, passwordCrypters[2].decrypt(encryptedMessages[1]))); 
     } catch (CrypterException e) { 
      // Anything goes as long as the above statement is not true. 
     } 
    } 
} 

Trả lời

152

Nếu bạn cố gắng giải mã dữ liệu đệm PKCS5 bằng khóa sai, và sau đó bỏ trống nó (được thực hiện bởi lớp Cipher tự động), rất có thể bạn sẽ nhận được BadPaddingException (với ít hơn 255/256) , khoảng 99,61%), bởi vì padding có một cấu trúc đặc biệt được xác nhận trong unpad và rất ít khóa sẽ tạo ra một padding hợp lệ.

Vì vậy, nếu bạn nhận ngoại lệ này, hãy coi nó và coi đó là "khóa sai".

Điều này cũng có thể xảy ra khi bạn cung cấp mật khẩu sai, sau đó được sử dụng để lấy khóa từ kho khóa hoặc được chuyển đổi thành khóa bằng chức năng tạo khóa.

Tất nhiên, đệm xấu cũng có thể xảy ra nếu dữ liệu của bạn bị hỏng khi vận chuyển.

Điều đó nói rằng, có một số nhận xét an ninh về kế hoạch của bạn:

  • Đối với mã hóa mật khẩu dựa trên, bạn nên sử dụng một SecretKeyFactory và PBEKeySpec thay vì sử dụng một SecureRandom với KeyGenerator. Lý do là SecureRandom có ​​thể là một thuật toán khác nhau trên mỗi thực thi Java, cho bạn một khóa khác. SecretKeyFactory thực hiện dẫn xuất khóa theo cách được định nghĩa (và cách thức được coi là an toàn, nếu bạn chọn thuật toán đúng).

  • Không sử dụng chế độ ECB. Nó mã hóa mỗi khối một cách độc lập, có nghĩa là các khối văn bản thuần túy giống hệt nhau cũng cung cấp các khối mã hóa giống hệt nhau.

    Tốt hơn là sử dụng an toàn mode of operation, như CBC (chuỗi khối mã hóa) hoặc CTR (Bộ đếm). Ngoài ra, sử dụng chế độ cũng bao gồm xác thực, như GCM (chế độ Galois-Counter) hoặc CCM (Bộ đếm với CBC-MAC), xem điểm tiếp theo.

  • Bạn thường không chỉ muốn bảo mật, mà còn xác thực, điều này đảm bảo thư không bị giả mạo. (Điều này cũng ngăn chặn các cuộc tấn công mã hóa đã chọn trên mật mã của bạn, tức là giúp bảo mật.) Vì vậy, hãy thêm MAC (mã xác thực thư) vào thư của bạn hoặc sử dụng chế độ mã hóa bao gồm xác thực (xem điểm trước).

  • DES có kích thước khóa hiệu dụng chỉ 56 bit. Khoảng trống chính này khá nhỏ, nó có thể bị cưỡng bức trong một vài giờ bởi một kẻ tấn công chuyên dụng. Nếu bạn tạo khóa bằng mật khẩu, điều này sẽ nhanh hơn. Ngoài ra, DES có kích thước khối chỉ 64 bit, làm tăng thêm một số điểm yếu trong chế độ chuỗi. Sử dụng thuật toán hiện đại như AES thay thế, có kích thước khối là 128 bit và kích thước khóa là 128 bit (đối với biến thể chuẩn).

+6

Câu trả lời hay, đầy đủ – demongolem

+1

+1 Câu trả lời hay. –

+1

Tôi chỉ muốn xác nhận. Tôi mới sử dụng mã hóa và đây là kịch bản của tôi, tôi đang sử dụng mã hóa AES. trong chức năng mã hóa/giải mã của tôi, tôi đang sử dụng khóa mã hóa. Tôi đã sử dụng một khóa mã hóa sai trong giải mã và tôi đã nhận 'javax.crypto.BadPaddingException: Khối cuối cùng này không được đệm đúng cách'. Tôi có nên coi đây là chìa khóa sai không? – kenicky

1

tùy thuộc vào các thuật toán mật mã bạn đang sử dụng, bạn có thể phải thêm một số byte đệm ở cuối trước khi mã hóa mảng byte sao cho chiều dài của mảng byte là bội số của kích thước khối:

Cụ thể trong trường hợp sơ đồ đệm bạn chọn là PKCS5 mô tả ở đây: http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_CJ_SYM__PAD.html

(tôi giả sử bạn có vấn đề khi bạn cố gắng để mã hóa)

Bạn có thể chọn schema đệm của bạn khi bạn khởi tạo đối tượng Cipher. Các giá trị được hỗ trợ phụ thuộc vào nhà cung cấp bảo mật bạn đang sử dụng.

Bằng cách này, bạn có chắc chắn muốn sử dụng cơ chế mã hóa đối xứng để mã hóa mật khẩu không? Sẽ không phải là một cách băm tốt hơn? Nếu bạn thực sự cần để có thể giải mã mật khẩu, DES là một giải pháp khá yếu, bạn có thể quan tâm đến việc sử dụng thứ gì đó mạnh hơn như AES nếu bạn cần ở lại với một thuật toán đối xứng.

+1

Tôi quên đề cập đến vấn đề là về giải mã. – Altrim

+1

vì vậy bạn có thể vui lòng đăng mã cố mã hóa/giải mã không? (và kiểm tra mảng byte mà bạn cố gắng giải mã không lớn hơn kích thước khối) – fpacifici

+1

Tôi rất mới với Java và cũng là Mật mã vì vậy tôi vẫn không biết cách tốt hơn để thực hiện mã hóa. Tôi chỉ muốn làm điều này hơn là có thể tìm cách tốt hơn để thực hiện nó. – Altrim

Các vấn đề liên quan