Brak kontroli integralności, dla tych szczególnych powodów
- Potrzeba nie wynika z przypadku użycia. Tryb
"AES/GCM/NoPadding"
jest dostępny tylko z Javy 7
- Zależy od użytkownika, jeśli chce wdrożyć, np. HMAC i/lub AESCMAC (zalecane).
- Wymagałoby to co najmniej dodatkowego klucza i dwóch pełnych przejść.
Jeśli masz implementację trybu GCM po obu stronach - np. używając Bouncy Castle na Java 6 - proszę, idźcie na to, bo jest o wiele bezpieczniej (o ile "IV" jest naprawdę wyjątkowy). Zmiana implementacji powinna być naprawdę łatwa.
notatki wdrożeniowe dotyczące szyfrowania
- Ta implementacja nie jest bezpieczny, gdy stosuje się w nieograniczonym roli klient/serwer z powodu ataków oracle przekładki (wymagają one 128 prób na bajt lub niższym średnio niezależnych od algorytm lub wielkość klucza). Będziesz musiał użyć MAC, HMAC lub Signature na zaszyfrowanych danych i zweryfikować je przed odszyfrowaniem, aby wdrożyć go w trybie klient/serwer.
- Decrypt zwróci wartość null, jeśli odszyfrowanie się nie powiedzie. Może to tylko oznaczać wyjątek wypełnienia, który powinien być odpowiednio obsługiwany (czy ostrzegałem przed wyskakującymi atakami oracle?)
- Nieprawidłowe klucze zostaną zwrócone jako
InvalidArgumentException
.
- Wszystkie inne wyjątki związane z bezpieczeństwem są "usuwane pod tabelą", ponieważ oznaczają, że środowisko wykonawcze Java jest niepoprawne. Na przykład obsługa
"UTF-8"
i "AES/CBC/PKCS5Padding"
wymaga wymaganej dla każdej implementacji Java SE.
Niektóre inne notatki
- Proszę nie próbować przeciwieństwem i wstawić bajtów bezpośrednio do ciągu wejściowego metody szyfrowanie (używając
new String(byte[])
na przykład). Metoda może nie działać w trybie cichym!
- Zoptymalizowany pod kątem czytelności. Przejdź do implementacji Base64 i implementacji
CipherStream
, jeśli wolisz prędkość i lepsze wykorzystanie pamięci.
- Potrzebujesz co najmniej Java 6 SE lub zgodnej do uruchomienia tego kodu.
- szyfrowanie/deszyfrowanie może nie dla AES kluczowych rozmiarach ponad 128 bit jak może potrzebne pliki strategii dla nieograniczonego szyfrowania (dostępne z Oracle)
- Beware of regulacjami rządowymi podczas eksportowania szyfrowania.
- Ta implementacja używa kluczy hex zamiast kluczy base64, ponieważ są one wystarczająco małe, a hex jest po prostu łatwiejszy do edycji/weryfikacji ręcznie.
- Używane kodowanie/dekodowanie hex i base64 pobrane z JDK, nie są potrzebne żadne biblioteki zewnętrzne.
- Uber jest prosty w użyciu, ale oczywiście niezbyt zorientowany obiektowo, bez buforowania instancji obiektów używanych w szyfrowaniu/odszyfrowywaniu. Refaktor do woli.
OK, tutaj jest trochę kodu ...
public static String encrypt(final String plainMessage,
final String symKeyHex) {
final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);
final byte[] encodedMessage = plainMessage.getBytes(Charset
.forName("UTF-8"));
try {
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// create the key
final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");
// generate random IV using block size (possibly create a method for
// this)
final byte[] ivData = new byte[blockSize];
final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
rnd.nextBytes(ivData);
final IvParameterSpec iv = new IvParameterSpec(ivData);
cipher.init(Cipher.ENCRYPT_MODE, symKey, iv);
final byte[] encryptedMessage = cipher.doFinal(encodedMessage);
// concatenate IV and encrypted message
final byte[] ivAndEncryptedMessage = new byte[ivData.length
+ encryptedMessage.length];
System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize);
System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage,
blockSize, encryptedMessage.length);
final String ivAndEncryptedMessageBase64 = DatatypeConverter
.printBase64Binary(ivAndEncryptedMessage);
return ivAndEncryptedMessageBase64;
} catch (InvalidKeyException e) {
throw new IllegalArgumentException(
"key argument does not contain a valid AES key");
} catch (GeneralSecurityException e) {
throw new IllegalStateException(
"Unexpected exception during encryption", e);
}
}
public static String decrypt(final String ivAndEncryptedMessageBase64,
final String symKeyHex) {
final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);
final byte[] ivAndEncryptedMessage = DatatypeConverter
.parseBase64Binary(ivAndEncryptedMessageBase64);
try {
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// create the key
final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");
// retrieve random IV from start of the received message
final byte[] ivData = new byte[blockSize];
System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize);
final IvParameterSpec iv = new IvParameterSpec(ivData);
// retrieve the encrypted message itself
final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length
- blockSize];
System.arraycopy(ivAndEncryptedMessage, blockSize,
encryptedMessage, 0, encryptedMessage.length);
cipher.init(Cipher.DECRYPT_MODE, symKey, iv);
final byte[] encodedMessage = cipher.doFinal(encryptedMessage);
// concatenate IV and encrypted message
final String message = new String(encodedMessage,
Charset.forName("UTF-8"));
return message;
} catch (InvalidKeyException e) {
throw new IllegalArgumentException(
"key argument does not contain a valid AES key");
} catch (BadPaddingException e) {
// you'd better know about padding oracle attacks
return null;
} catch (GeneralSecurityException e) {
throw new IllegalStateException(
"Unexpected exception during decryption", e);
}
}
Zastosowanie:
String plain = "Zaphod's just zis guy, ya knöw?";
String encrypted = encrypt(plain, "000102030405060708090A0B0C0D0E0F");
System.out.println(encrypted);
String decrypted = decrypt(encrypted, "000102030405060708090A0B0C0D0E0F");
if (decrypted != null && decrypted.equals(plain)) {
System.out.println("Hey! " + decrypted);
} else {
System.out.println("Bummer!");
}
"KeyGenerator kgen" nigdy nie jest używany. Co tam się dzieje? 'KeyGenerator' służy do wyboru nowego losowego klucza. Ale wygląda na to, że chcesz ponownie użyć istniejącego klucza, który jest w jakiś sposób przechowywany w 'String' (kodowanie Base-64, być może?) – erickson