2015-05-21 26 views
5

Tôi có khóa mã cứng mà tôi muốn mã hóa chuỗi trước khi lưu trữ trong SharedPreferences. Đây là mã tôi có cho đến nay:IllegalBlockSizeException khi cố gắng mã hóa và giải mã chuỗi bằng AES

public class TokenEncryptor { 

    private final static String TOKEN_KEY = "91a29fa7w46d8x41"; 

    public static String encrypt(String plain) { 
     try { 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]); 
      SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES"); 
      cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec); 
      return new String(cipher.doFinal(plain.getBytes())); 
     } catch (Exception e) { 
      Ln.e(e); 
      return null; 
     } 
    } 

    public static String decrypt(String encoded) { 
     try { 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]); 
      SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES"); 
      cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec); 
      return new String(cipher.doFinal(encoded.getBytes())); 
     } catch (Exception e) { 
      Ln.e(e); 
      return null; 
     } 
    } 
} 

Nó dường như được bắt một ngoại lệ ở phần cuối của decrypt phương pháp:

javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

Ai đó có thể chỉ cho tôi đi đúng hướng? Tôi có cảm giác tôi đang làm một điều gì đó sai trái ngay lập tức IvParameterSpec.

+1

Khóa của bạn chỉ dài 16 byte, vì vậy bạn đang nói về AES-128 chứ không phải AES-256. –

+0

@ArtjomB. nên tôi chỉ cần thay đổi nó thành 32 byte để làm cho nó AES-256? Xin lỗi vì đã không được thông báo về điều này, đây là lần đầu tiên tôi phải xử lý mã hóa – Oleksiy

+0

Có, nếu bạn tăng kích thước khóa thành 24 hoặc 32 byte, AES-192 hoặc AES-256 sẽ được sử dụng tự động. –

Trả lời

5

Khi bạn mã hóa chuỗi bằng AES, bạn sẽ nhận được một mảng byte trở lại. Cố gắng chuyển đổi các byte đó trực tiếp thành một chuỗi (new String(cipher.doFinal(plaintextBytes))) sẽ gây ra tất cả các loại vấn đề. Nếu bạn yêu cầu đầu ra từ phương thức mã hóa của mình thành một chuỗi, thì hãy sử dụng Base64 thay vì thử chuyển đổi trực tiếp. Trong phương thức giải mã của bạn, chuyển đổi chuỗi Base64 trở lại thành mảng byte trước khi giải mã mảng byte.

Ngoài ra, không sử dụng getBytes() vì đầu ra phụ thuộc vào mặc định của hệ thống. Sử dụng getBytes("utf-8") hoặc bất kỳ thứ gì. Điều đó giúp loại bỏ sự mơ hồ.

+2

[Q: Bộ giải mã và bộ giải mã Base64] (http: // stackoverflow.com/questions/4322182/base64-encoder-and-decoder) –

8

Chỉ trong trường hợp có ai quan tâm (hoặc cảm thấy quá lười biếng để làm nghiên cứu của họ), đây là mã kết quả cho AES-256 mã hóa và giải mã mà tôi đặt lại với nhau, với sự giúp đỡ từ các câu trả lời được chấp nhận và bình luận:

public class TokenEncryptor { 

    private final static String TOKEN_KEY = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw"; 

    public static String encrypt(String plain) { 
     try { 
      byte[] iv = new byte[16]; 
      new SecureRandom().nextBytes(iv); 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv)); 
      byte[] cipherText = cipher.doFinal(plain.getBytes("utf-8")); 
      byte[] ivAndCipherText = getCombinedArray(iv, cipherText); 
      return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP); 
     } catch (Exception e) { 
      Ln.e(e); 
      return null; 
     } 
    } 

    public static String decrypt(String encoded) { 
     try { 
      byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP); 
      byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16); 
      byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length); 

      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv)); 
      return new String(cipher.doFinal(cipherText), "utf-8"); 
     } catch (Exception e) { 
      Ln.e(e); 
      return null; 
     } 
    } 

    private static byte[] getCombinedArray(byte[] one, byte[] two) { 
     byte[] combined = new byte[one.length + two.length]; 
     for (int i = 0; i < combined.length; ++i) { 
      combined[i] = i < one.length ? one[i] : two[i - one.length]; 
     } 
     return combined; 
    } 

} 
+0

Tôi xin lỗi, nhưng tại sao chúng ta lại có một dòng "SecureRandom mới(). nextBytes (iv);" ? Giá trị trả về không được sử dụng, phải không? – zkvarz

+1

@zkvarz: phương thức thao tác mảng byte đã gán -> không có giá trị trả về –

+0

@NinoHandler Ok, cảm ơn. – zkvarz

3

Đó là phần mở rộng của câu trả lời Artjom B và làm việc cho tôi.

public String encryptMsg(String message, SecretKey secret) 
      throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { 
     Cipher cipher = null; 
     cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, secret); 
     byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8")); 
     return Base64.encodeToString(cipherText, Base64.NO_WRAP); 
    } 

public String decryptMsg(String cipherText, SecretKey secret) 
     throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException { 
    Cipher cipher = null; 
    cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 
    cipher.init(Cipher.DECRYPT_MODE, secret); 
    byte[] decode = Base64.decode(cipherText, Base64.NO_WRAP); 
    String decryptString = new String(cipher.doFinal(decode), "UTF-8"); 
    return decryptString; 
} 
Các vấn đề liên quan