W tej odpowiedzi zdecydowałem się podejść do głównego tematu „Prosty przykład szyfrowania / odszyfrowania w języku Java AES”, a nie do konkretnego pytania dotyczącego debugowania, ponieważ myślę, że przyniesie to korzyści większości czytelników.
To jest proste podsumowanie mojego wpisu na blogu na temat szyfrowania AES w Javie, więc polecam przeczytać go przed wdrożeniem czegokolwiek. Podam jednak prosty przykład do użycia i kilka wskazówek, na co należy uważać.
W tym przykładzie wybiorę użycie uwierzytelnionego szyfrowania w trybie Galois / Counter lub GCM . Powodem jest to, że w większości przypadków zależy Ci na uczciwości i autentyczności w połączeniu z poufnością (czytaj więcej na blogu ).
Samouczek szyfrowania / deszyfrowania AES-GCM
Oto kroki wymagane do szyfrowania / odszyfrowywania za pomocą AES-GCM z architekturą Java Cryptography Architecture (JCA) . Nie mieszaj z innymi przykładami , ponieważ subtelne różnice mogą spowodować, że Twój kod będzie całkowicie niepewny.
1. Utwórz klucz
Ponieważ zależy to od twojego przypadku użycia, przyjmuję najprostszy przypadek: losowy tajny klucz.
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = SecretKeySpec(key, "AES");
Ważny:
2. Utwórz wektor inicjujący
Wektor inicjujący (IV) jest używany tak, że ten sam klucz tajny będzie tworzyć różne teksty szyfr .
byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);
Ważny:
3. Szyfruj za pomocą IV i klucza
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] cipherText = cipher.doFinal(plainText);
Ważny:
- użyj 16-bajtowego / 128-bitowego znacznika uwierzytelniającego (używanego do weryfikacji integralności / autentyczności)
- tag uwierzytelniający zostanie automatycznie dołączony do zaszyfrowanego tekstu (w implementacji JCA)
- ponieważ GCM zachowuje się jak szyfr strumieniowy, nie jest wymagane dopełnianie
- użyj
CipherInputStream
podczas szyfrowania dużych fragmentów danych
- chcesz sprawdzić dodatkowe (nie tajne) dane, jeśli zostały zmienione? Możesz użyć powiązanych danych z
cipher.updateAAD(associatedData);
Więcej tutaj.
3. Serializuj do pojedynczej wiadomości
Wystarczy dołączyć IV i zaszyfrowany tekst. Jak wspomniano powyżej, IV nie musi być tajne.
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();
Opcjonalnie zakoduj za pomocą Base64, jeśli potrzebujesz reprezentacji ciągu. Użyj wbudowanej implementacji Androida lub Java 8 (nie używaj kodeka Apache Commons - to okropna implementacja). Kodowanie służy do „konwersji” tablic bajtowych na reprezentację łańcuchową, aby zapewnić bezpieczeństwo ASCII, np .:
String base64CipherMessage = Base64.getEncoder().encodeToString(cipherMessage);
4. Przygotuj deszyfrowanie: deserializacja
Jeśli zakodowałeś wiadomość, najpierw zdekoduj ją do tablicy bajtów:
byte[] cipherMessage = Base64.getDecoder().decode(base64CipherMessage)
Ważny:
5. Odszyfruj
Zainicjuj szyfr i ustaw te same parametry, co przy szyfrowaniu:
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
//use first 12 bytes for iv
AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128, cipherMessage, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmIv);
//use everything from 12 bytes on as ciphertext
byte[] plainText = cipher.doFinal(cipherMessage, 12, cipherMessage.length - 12);
Ważny:
- nie zapomnij dodać powiązanych danych z,
cipher.updateAAD(associatedData);
jeśli dodałeś je podczas szyfrowania.
W tym streszczeniu można znaleźć działający fragment kodu.
Zauważ, że najnowsze implementacje Androida (SDK 21+) i Java (7+) powinny mieć AES-GCM. Starsze wersje mogą go nie mieć. Nadal wybieram ten tryb, ponieważ jest łatwiejszy do wdrożenia i jest bardziej wydajny w porównaniu do podobnego trybu Encrypt-then-Mac (z np. AES-CBC + HMAC ). Zobacz ten artykuł o tym, jak wdrożyć AES-CBC z HMAC .