2015-05-21 33 views
5

Mam ustalony klucz, z którym chcę zaszyfrować ciąg przed zapisaniem go w SharedPreferences. Jest to kod mam tak daleko:IllegalBlockSizeException podczas próby do szyfrowania i deszyfrowania ciąg z 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; 
     } 
    } 
} 

Wydaje się, że łowienie wyjątek na koniec decrypt metody:

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

Czy ktoś może wskazać mi w dobrym kierunku? Mam przeczucie, że robię coś niewłaściwie tworzącego IvParameterSpec.

+1

Twój klucz ma tylko 16 bajtów, więc mówisz o AES-128 nie AES-256. –

+0

@ArtjomB. czy powinienem zmienić go na 32 bajty, aby był to AES-256? Przepraszam za to, że niedoinformowani o tym, że to pierwszy raz mam do czynienia z szyfrowaniem – Oleksiy

+0

Tak, jeśli zwiększy rozmiar klucza 24 lub 32 bajtów, AES-192 lub AES-256 będą wykorzystywane automatycznie. . –

Odpowiedz

5

Podczas szyfrowania ciąg z AES, masz tablicę bajtów z powrotem. Próba przekonwertowania tych bajtów bezpośrednio na ciąg znaków (new String(cipher.doFinal(plaintextBytes))) spowoduje różnego rodzaju problemy. Jeśli chcesz, aby dane wyjściowe z metody szyfrowania były ciągami, użyj raczej Base64 niż próby bezpośredniej konwersji. W swojej metodzie deszyfrowania, przekształć łańcuch Base64 z powrotem na tablicę bajtów przed odszyfrowaniem tablicy bajtów.

Nie należy również używać getBytes() ponieważ wyjście zależy od ustawień domyślnych systemowych. Użyj getBytes("utf-8") lub cokolwiek innego. To eliminuje dwuznaczność.

+2

[P: Koder i dekoder Base64] (http: // stackoverflow.com/questions/4322182/base64-encoder-and-decoder) –

8

wszelki wypadek ktoś jest zainteresowany (lub czuje się zbyt leniwy, aby zrobić swoje badania), tutaj jest kod wynik dla AES-256 szyfrowania i deszyfrowania, które mogę umieścić razem, z pomocą akceptowanych odpowiedzi i komentarze:

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

Przykro mi, ale dlaczego mamy linię "new SecureRandom(). nextBytes (iv);" ? Zwracana wartość nie jest używana, prawda? – zkvarz

+1

@zkvarz: metoda manipuluje przypisaną tablicą bajtów -> nie ma zwracanej wartości –

+0

@NinoHandler Ok, dziękuję. – zkvarz

3

To rozszerzenie Artjom B odpowiedź i pracuje dla mnie.

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; 
}