Jak zaszyfrować ciąg w Javie

150

To, czego potrzebuję, to zaszyfrować ciąg, który pojawi się w kodzie kreskowym 2D (PDF-417), więc gdy ktoś wpadnie na pomysł, aby go zeskanować, nic nie da się odczytać.

Inne wymagania:

  • nie powinno być skomplikowane
  • nie powinien składać się z RSA, infrastruktury PKI, par kluczy itp.

Musi być na tyle proste, aby pozbyć się ludzi węszących wokół i łatwe do odszyfrowania dla innych firm zainteresowanych uzyskaniem tych danych. Dzwonią do nas, podajemy im standard lub podajemy prosty klucz, którego można następnie użyć do odszyfrowania.

Prawdopodobnie firmy te mogłyby korzystać z różnych technologii, więc dobrze byłoby trzymać się jakiegoś standardu, który nie jest powiązany z żadną specjalną platformą czy technologią.

Co sugerujesz? Czy jakaś klasa Java osiąga wysokie standardy bezpieczeństwa encrypt()i decrypt()bez większych komplikacji?

ante.sabo
źródło
Ostrzeżenie . Wiele z poniższych odpowiedzi przedstawia jedną lub drugą metodę wykonywania jakiejkolwiek kryptografii w Javie. Odpowiedzi mogą nie odzwierciedlać dobrych praktyk kryptograficznych i mogą nie zostać dobrze sprawdzone; nie ma czegoś takiego jak zabezpieczenie kopiuj / wklej . Odpowiedzi powinny przynajmniej uwzględniać konwersję ciągów. Rzeczywiste pytanie z dołączonym kodem kreskowym 2D jest zbyt szerokie i powinno wymagać rozwiązania specyficznego dla klienta.
Maarten Bodewes

Odpowiedzi:

156

To jest pierwsza strona, która pojawia się za pośrednictwem Google, a luki w zabezpieczeniach we wszystkich implementacjach sprawiają, że wzdrygam się, więc publikuję to, aby dodać informacje dotyczące szyfrowania dla innych, ponieważ minęło 7 lat od oryginalnego postu. Mam tytuł magistra inżynierii komputerowej i spędziłem dużo czasu studiując i ucząc się kryptografii, więc rzucam moje dwa centy, aby uczynić internet bezpieczniejszym miejscem.

Zwróć również uwagę, że wiele implementacji może być bezpiecznych w danej sytuacji, ale po co ich używać i potencjalnie przypadkowo popełniać błąd? Używaj najsilniejszych dostępnych narzędzi, chyba że masz konkretny powód, aby tego nie robić. Ogólnie rzecz biorąc, zdecydowanie radzę korzystać z biblioteki i trzymać się z daleka od drobiazgowych szczegółów, jeśli możesz.

UPDATE 05.04.18: I przepisał kilka części, aby były łatwiejsze do zrozumienia i zmienił Zalecana bibliotekę z Jasypt do nowej biblioteki Google Tink , polecam całkowicie usuwając Jasypt z istniejącej instalacji.

Przedmowa

Poniżej opiszę podstawy bezpiecznej kryptografii symetrycznej i wskażę typowe błędy, które widzę w Internecie, gdy ludzie samodzielnie wdrażają kryptografię w standardowej bibliotece Java. Jeśli chcesz po prostu pominąć wszystkie szczegóły, przejdź do nowej biblioteki Google Tink, zaimportuj ją do swojego projektu i użyj trybu AES-GCM do wszystkich swoich szyfrowań, a będziesz bezpieczny.

Teraz, jeśli chcesz poznać podstawowe szczegóły dotyczące szyfrowania w Javie, czytaj dalej :)

Szyfry blokowe

Najpierw musisz wybrać symetryczny klucz szyfrujący blokowy. Szyfr blokowy to funkcja / program komputerowy używany do tworzenia pseudolosowości. Pseudolosowość to fałszywa losowość, której żaden komputer inny niż komputer kwantowy nie byłby w stanie odróżnić go od prawdziwej losowości. Szyfr blokowy jest jak element konstrukcyjny kryptografii, a gdy jest używany z różnymi trybami lub schematami, możemy tworzyć szyfrowanie.

Jeśli chodzi o dostępne dzisiaj algorytmy szyfrowania blokowego, upewnij się, że NIGDY , powtarzam NIGDY nie używam DES , powiedziałbym nawet, że NIGDY nie używaj 3DES . Jedynym szyfrem blokowym, który nawet wydanie Snowdena z NSA było w stanie zweryfikować, że jest naprawdę tak blisko Pseudo-Random, jak to tylko możliwe, to AES 256 . Istnieje również AES 128; różnica polega na tym, że AES 256 działa w 256-bitowych blokach, podczas gdy AES 128 działa w 128 blokach. Podsumowując, AES 128 jest uważany za bezpieczny, chociaż odkryto pewne słabości, ale 256 jest tak solidny, jak to tylko możliwe.

Zabawny fakt DES został złamany przez NSA, kiedy został założony i przez kilka lat trzymany w tajemnicy. Chociaż niektórzy ludzie nadal twierdzą, że 3DES jest bezpieczny, istnieje sporo artykułów naukowych, które odkryły i przeanalizowały słabości 3DES .

Tryby szyfrowania

Szyfrowanie jest tworzone, gdy bierzesz szyfr blokowy i używasz określonego schematu, tak że losowość jest połączona z kluczem do stworzenia czegoś, co jest odwracalne, o ile znasz klucz. Nazywa się to trybem szyfrowania.

Oto przykład trybu szyfrowania i najprostszego trybu znanego jako EBC, abyś mógł wizualnie zrozumieć, co się dzieje:

Tryb EBC

Tryby szyfrowania, które najczęściej zobaczysz w Internecie, to:

CTR EBC, CBC, GCM

Istnieją inne tryby poza wymienionymi, a naukowcy zawsze pracują nad nowymi, aby poprawić istniejące problemy.

Przejdźmy teraz do implementacji i tego, co jest bezpieczne. NIGDY nie używaj EBC, jest to złe w ukrywaniu powtarzających się danych, jak pokazał słynny pingwin linuksowy .Przykład Linux Penguin

Podczas implementacji w Javie należy pamiętać, że jeśli używasz następującego kodu, tryb EBC jest ustawiony domyślnie:

Cipher cipher = Cipher.getInstance("AES");

... NIEBEZPIECZEŃSTWO TO JEST WRAŻLIWOŚĆ! i niestety jest to widoczne w całym StackOverflow oraz online w samouczkach i przykładach.

Nonces i IV

W odpowiedzi na problem znaleziony w trybie EBC stworzono rzeczowniki znane również jako IV. Chodzi o to, że generujemy nową zmienną losową i dołączamy ją do każdego szyfrowania, aby po zaszyfrowaniu dwóch takich samych wiadomości wychodziły one różne. Piękno za tym kryje się w tym, że kroplówka lub nonce jest powszechnie znana. Oznacza to, że osoba atakująca może mieć do tego dostęp, ale dopóki nie ma Twojego klucza, nie może nic zrobić z tą wiedzą.

Typowe problemy, które zobaczę, to to, że ludzie ustawią IV jako wartość statyczną, jak w tej samej stałej wartości w swoim kodzie. i tutaj jest pułapka dla IV, gdy powtórzysz jeden, faktycznie naruszysz całe bezpieczeństwo swojego szyfrowania.

Generowanie losu IV

SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);

Uwaga: SHA1 jest zepsuty, ale nie mogłem znaleźć sposobu prawidłowego zaimplementowania SHA256 w tym przypadku użycia, więc jeśli ktoś chciałby się tym zająć i zaktualizować, byłoby wspaniale! Również ataki SHA1 są nadal niekonwencjonalne, ponieważ złamanie ogromnego klastra może zająć kilka lat. Sprawdź szczegóły tutaj.

Implementacja CTR

W trybie CTR nie jest wymagane dopełnienie.

 Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

Wdrożenie CBC

Jeśli zdecydujesz się zaimplementować tryb CBC, zrób to z PKCS7Padding w następujący sposób:

 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

Luka w zabezpieczeniach CBC i CTR oraz dlaczego warto używać GCM

Chociaż niektóre inne tryby, takie jak CBC i CTR, są bezpieczne, napotykają na problem polegający na tym, że atakujący może odwrócić zaszyfrowane dane, zmieniając ich wartość po odszyfrowaniu. Powiedzmy, że szyfrujesz wyimaginowaną wiadomość bankową „Sprzedaj 100”, zaszyfrowana wiadomość wygląda tak, jakby to „eu23ng” atakujący zmienił się o jeden bit na „eu53ng”, a po odszyfrowaniu wiadomości nagle brzmiała „Sprzedaj 900”.

Aby tego uniknąć, większość internetu korzysta z GCM i za każdym razem, gdy widzisz HTTPS, prawdopodobnie używa GCM. GCM podpisuje zaszyfrowaną wiadomość hashem i sprawdza, czy wiadomość nie została zmieniona przy użyciu tego podpisu.

Unikałbym wdrażania GCM ze względu na jego złożoność. Lepiej jest korzystać z nowej biblioteki Google Tink, ponieważ tutaj ponownie, jeśli przypadkowo powtórzysz IV, narażasz klucz w przypadku GCM, co jest ostateczną luką w zabezpieczeniach. Nowi badacze pracują nad trybami szyfrowania IV odpornymi na powtórzenia, w których nawet jeśli powtórzysz IV, klucz nie jest zagrożony, ale to jeszcze nie weszło do głównego nurtu.

Jeśli chcesz zaimplementować GCM, oto link do ładnej implementacji GCM . Nie mogę jednak zapewnić bezpieczeństwa lub czy jest on prawidłowo zaimplementowany, ale powoduje to uszkodzenie podstaw. Zwróć też uwagę, że w przypadku GCM nie ma dopełnienia.

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

Klucze a hasła

Kolejną bardzo ważną uwagą jest to, że w przypadku kryptografii klucz i hasło to nie to samo. Klucz w kryptografii musi mieć pewną entropię i losowość, aby można go było uznać za bezpieczny. Dlatego musisz upewnić się, że używasz odpowiednich bibliotek kryptograficznych do wygenerowania klucza.

Więc naprawdę masz dwie implementacje, które możesz tutaj zrobić, pierwszą jest użycie kodu znalezionego w tym wątku StackOverflow do generowania losowego klucza . To rozwiązanie wykorzystuje bezpieczny generator liczb losowych do tworzenia od podstaw klucza, którego możesz użyć.

Inną mniej bezpieczną opcją jest użycie danych wejściowych użytkownika, takich jak hasło. Problem, o którym mówiliśmy, polega na tym, że hasło nie ma wystarczającej entropii, więc musielibyśmy użyć PBKDF2 , algorytmu, który pobiera hasło i je wzmacnia. Oto implementacja StackOverflow, która mi się podobała . Jednak biblioteka Google Tink ma to wszystko wbudowane i powinieneś z tego skorzystać.

Programiści Androida

Jedną z ważnych kwestii, na które należy zwrócić uwagę, jest wiedza, że ​​kod Androida można poddać inżynierii wstecznej, aw większości przypadków jest to również kod Java. Oznacza to, że jeśli przechowujesz hasło w postaci zwykłego tekstu w swoim kodzie. Haker może go łatwo odzyskać. Zwykle w przypadku tego typu szyfrowania chcesz użyć kryptografii asymetrycznej i tak dalej. To jest poza zakresem tego postu, więc nie będę się w to zagłębiał.

Ciekawa lektura z 2013 roku : wskazuje, że 88% implementacji Crypto w systemie Android zostało wykonanych nieprawidłowo.

Końcowe przemyślenia

Po raz kolejny sugerowałbym unikanie bezpośredniego wdrażania biblioteki java dla kryptowalut i używanie Google Tink , zaoszczędzi ci to bólu głowy, ponieważ naprawdę wykonali dobrą robotę, poprawnie implementując wszystkie algorytmy. A nawet wtedy upewnij się, że sprawdziłeś problemy poruszane na githubie Tink, tu i tam pojawiają się luki w zabezpieczeniach.

Jeśli masz jakieś pytania lub uwagi, możesz je skomentować! Bezpieczeństwo zawsze się zmienia i musisz zrobić wszystko, aby za nim nadążyć :)

Konstantino Sparakis
źródło
15
To najczystsza rzecz, jaką kiedykolwiek widziałem.
Seraf
1
@SabirKhan Może to być powód do niepokoju, ale podstawowe algorytmy nadal nie zostały zepsute, więc nie martwiłbym się tym zbytnio. W przypadku, gdy nie ufasz temu, sprawdź również github.com/google/keyczar , Został opracowany przez zespół bezpieczeństwa Google.
Konstantino Sparakis
1
@KonstantinoSparakis: Gdybym nie zinterpretował błędnie dokumentacji Jasypt's BasicTextEncryptor i StrongTextEncryptor, te klasy używają DES i 3DES do szyfrowania, czyli dokładnie tego, czego nie wolno używać czytelnikom. IMO należy zamienić podane przykłady kodu na taki, który korzysta z Jasypt's StandardPBEStringEncryptor i ręcznie definiuje algorytm AES do użycia.
xpages-noob
1
@ xpages-noob Zaktualizowałem post. Właściwie znalazłem Google Tink, która jest najnowszą obsługiwaną biblioteką dla kryptowalut, więc powinieneś to sprawdzić!
Konstantino Sparakis
2
Rozmiar bloku AES to 128 bitów. W AES 256 rozmiar klucza wynosi 256 bitów. Podobnie, AES 192 i AES 128. Ponadto, ponieważ Java 8, getInstanceStrong()metoda of Cipherjest lepsza niż SHA1PRNG
Saptarshi Basu
110

Zalecałbym użycie standardowego szyfru symetrycznego, który jest szeroko dostępny, jak DES , 3DES lub AES . Chociaż nie jest to najbezpieczniejszy algorytm, istnieje mnóstwo implementacji i wystarczy podać klucz każdemu, kto ma odszyfrować informacje w kodzie kreskowym. javax.crypto.Cipher jest tym, z czym chcesz tutaj pracować.

Załóżmy, że znajdują się bajty do zaszyfrowania

byte[] input;

Następnie będziesz potrzebować klucza i bajtów wektora inicjalizacji

byte[] keyBytes;
byte[] ivBytes;

Teraz możesz zainicjować szyfr dla wybranego algorytmu:

// wrap key data in Key/IV specs to pass to cipher
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// create the cipher with the algorithm you choose
// see javadoc for Cipher class for more info, e.g.
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

Szyfrowanie wyglądałoby tak:

cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted= new byte[cipher.getOutputSize(input.length)];
int enc_len = cipher.update(input, 0, input.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);

I odszyfrowanie w ten sposób:

cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(encrypted, 0, enc_len, decrypted, 0);
dec_len += cipher.doFinal(decrypted, dec_len);
VoidPointer
źródło
9
Czy mogę zasugerować zaktualizowanie tego przykładu w celu odniesienia do DESedealgorytmu? Ponieważ jest to popularne pytanie (i odpowiedź), szkoda byłoby zachęcać ludzi do używania DES, ponieważ szyfr jest tak słaby jak na dzisiejsze standardy.
Duncan Jones,
coś nie tak z javax.crypto.BadPaddingException: Podany końcowy blok nie jest odpowiednio wypełniony podczas odszyfrowywania
ciekawość
2
@Duncan Rzeczywiście DES jest słaby, ale przypuszczam, że AES byłby lepszy niż DESede (aka TipleDES): http://security.stackexchange.com/a/26181/69785
Piovezan
2
Powinno to zostać zaktualizowane, aby mieć AES / GCM / NoPadding, DES jest podatny na ataki bruteforce, TripleDes też nie jest zalecany
Konstantino Sparakis
1
Odpowiedź od Konstantino Sparakisa poniżej jest TAKI znacznie lepsza niż ta.
Steve
22

Ostrzeżenie

Nie używaj tego jako pewnego rodzaju pomiaru bezpieczeństwa.

Mechanizm szyfrowania w tym poście to jednorazowa podkładka, co oznacza, że ​​tajny klucz może być łatwo odzyskany przez atakującego za pomocą 2 zaszyfrowanych wiadomości. Zaszyfrowane wiadomości XOR 2 i otrzymujesz klucz. Tak proste!

Wskazane przez Moussa


Używam Base64Encoder / Decoder firmy Sun, który można znaleźć w środowisku JRE firmy Sun, aby uniknąć kolejnego pliku JAR w bibliotece. To niebezpieczne z punktu widzenia używania OpenJDK lub JRE innego. Poza tym, czy jest jeszcze jeden powód, dla którego powinienem rozważyć użycie Apache commons lib z koderem / dekoderem?

public class EncryptUtils {
    public static final String DEFAULT_ENCODING = "UTF-8"; 
    static BASE64Encoder enc = new BASE64Encoder();
    static BASE64Decoder dec = new BASE64Decoder();

    public static String base64encode(String text) {
        try {
            return enc.encode(text.getBytes(DEFAULT_ENCODING));
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }//base64encode

    public static String base64decode(String text) {
        try {
            return new String(dec.decodeBuffer(text), DEFAULT_ENCODING);
        } catch (IOException e) {
            return null;
        }
    }//base64decode

    public static void main(String[] args) {
        String txt = "some text to be encrypted";
        String key = "key phrase used for XOR-ing";
        System.out.println(txt + " XOR-ed to: " + (txt = xorMessage(txt, key)));

        String encoded = base64encode(txt);       
        System.out.println(" is encoded to: " + encoded + " and that is decoding to: " + (txt = base64decode(encoded)));
        System.out.print("XOR-ing back to original: " + xorMessage(txt, key));
    }

    public static String xorMessage(String message, String key) {
        try {
            if (message == null || key == null) return null;

            char[] keys = key.toCharArray();
            char[] mesg = message.toCharArray();

            int ml = mesg.length;
            int kl = keys.length;
            char[] newmsg = new char[ml];

            for (int i = 0; i < ml; i++) {
                newmsg[i] = (char)(mesg[i] ^ keys[i % kl]);
            }//for i

            return new String(newmsg);
        } catch (Exception e) {
            return null;
        }
    }//xorMessage
}//class
ante.sabo
źródło
1
Użyłem również tej propozycji rozwiązania za pośrednictwem sun.misc.BASE64Encoder, ale przy użyciu dość dużych ciągów do kodowania koder zwrócił fragmenty ciągów (po 76 znaków każdy). Następnie przełączyłem się na Apache Commons Codec Base64, który oferuje metody kodowania bez fragmentów!
basZero
78
Opisany mechanizm szyfrowania jest BARDZO NIEBEZPIECZNY, jeśli zostanie użyty więcej niż jeden raz. to jest powód, dla którego nazywa się to jednorazowa podkładka. Atakujący może łatwo odzyskać tajny klucz za pomocą 2 zaszyfrowanych wiadomości. xor 2 zaszyfrowane wiadomości i otrzymujesz klucz. Tak proste!
xtrem
3
Jego zamysłem nie jest bycie ciężkim, a jedynie odbijanie się od ludzi przed próbą odczytania tego, co jest zapisane w kodach kreskowych PDF-417 2D. Zresztą są tylko indeksy nie dla nikogo kluczowe ...
ante.sabo
2
DOBRZE. Martwię się tylko, że ktoś używa tego jako mechanizmu szyfrowania.
xtrem
W przypadku szyfrowania koder (np. BASE64Encoder) można uniknąć, aby mieć ataki siłowe.
Jagrut Dalwadi
13

dzięki, że stworzyłem tę klasę używając twojego kodu, być może ktoś uzna ją za pełną użytkownika

szyfrowanie obiektów

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class ObjectCrypter {

private Cipher deCipher;
private Cipher enCipher;
private SecretKeySpec key;
private IvParameterSpec ivSpec;


public ObjectCrypter(byte[] keyBytes,   byte[] ivBytes) {
    // wrap key data in Key/IV specs to pass to cipher


     ivSpec = new IvParameterSpec(ivBytes);
    // create the cipher with the algorithm you choose
    // see javadoc for Cipher class for more info, e.g.
    try {
         DESKeySpec dkey = new  DESKeySpec(keyBytes);
          key = new SecretKeySpec(dkey.getKey(), "DES");
         deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
         enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
public byte[] encrypt(Object obj) throws InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, ShortBufferException, BadPaddingException {
    byte[] input = convertToByteArray(obj);
    enCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

    return enCipher.doFinal(input);




//  cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
//  byte[] encypted = new byte[cipher.getOutputSize(input.length)];
//  int enc_len = cipher.update(input, 0, input.length, encypted, 0);
//  enc_len += cipher.doFinal(encypted, enc_len);
//  return encypted;


}
public Object decrypt( byte[]  encrypted) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException, ClassNotFoundException {
    deCipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

    return convertFromByteArray(deCipher.doFinal(encrypted));

}



private Object convertFromByteArray(byte[] byteObject) throws IOException,
        ClassNotFoundException {
    ByteArrayInputStream bais;

    ObjectInputStream in;
    bais = new ByteArrayInputStream(byteObject);
    in = new ObjectInputStream(bais);
    Object o = in.readObject();
    in.close();
    return o;

}



private byte[] convertToByteArray(Object complexObject) throws IOException {
    ByteArrayOutputStream baos;

    ObjectOutputStream out;

    baos = new ByteArrayOutputStream();

    out = new ObjectOutputStream(baos);

    out.writeObject(complexObject);

    out.close();

    return baos.toByteArray();

}


}
szeryf
źródło
napisali powiązane pytanie tutaj !
user2023507
Czy nie powinno być tak, że przekazywanie różnych kluczy podczas szyfrowania i odszyfrowywania nie powinno zwracać tekstu z powrotem? Wydaje się, że tutaj się to nie dzieje. PS: Do wykonania tego testu używam różnych obiektów tej klasy.
instanceOfObject
6

Aktualizacja w dniu 12 grudnia 2019 r

W przeciwieństwie do niektórych innych trybów, takich jak CBC, tryb GCM nie wymaga, aby IV był nieprzewidywalny. Jedynym wymaganiem jest to, że IV musi być unikalny dla każdego wywołania z danym kluczem. Jeśli powtórzy się raz dla danego klucza, bezpieczeństwo może zostać naruszone. Łatwym sposobem osiągnięcia tego jest użycie losowego IV z silnego generatora liczb pseudolosowych, jak pokazano poniżej.

Użycie sekwencji lub znacznika czasu jak IV jest również możliwe, ale może nie być tak banalne, jak mogłoby się wydawać. Na przykład, jeśli system nie śledzi poprawnie sekwencji już używanych jako IV w trwałym magazynie, wywołanie może powtórzyć IV po ponownym uruchomieniu systemu. Podobnie nie ma idealnego zegara. Zegar komputera przestawia się itp.

Ponadto klucz należy obracać po każdych 2 ^ 32 wywołaniach. Więcej informacji na temat wymogu IV można znaleźć w tej odpowiedzi i w zaleceniach NIST .


To jest kod szyfrowania i deszyfrowania, który właśnie napisałem w Javie 8, biorąc pod uwagę następujące punkty. Mam nadzieję, że ktoś uzna to za przydatne:

  1. Algorytm szyfrowania : Szyfr blokowy AES z kluczem 256-bitowym jest uważany za wystarczająco bezpieczny. Aby zaszyfrować całą wiadomość, należy wybrać tryb. Zalecane jest uwierzytelnione szyfrowanie (zapewniające zarówno poufność, jak i integralność). GCM, CCM i EAX to najczęściej używane uwierzytelnione tryby szyfrowania. GCM jest zwykle preferowany i działa dobrze w architekturach Intela, które zapewniają dedykowane instrukcje dla GCM. Wszystkie te trzy tryby są trybami opartymi na CTR (opartymi na licznikach) i dlatego nie wymagają dopełniania. W rezultacie nie są podatne na ataki związane z wypełnianiem

  2. W przypadku GCM wymagany jest wektor inicjujący (IV). IV nie jest tajemnicą. Jedyny wymóg musi być losowy lub nieprzewidywalny. W Javie SecuredRandomklasa ma na celu tworzenie silnych kryptograficznie liczb pseudolosowych. W getInstance()metodzie można określić algorytm generowania liczb pseudolosowych . Jednak od wersji Java 8 zalecanym sposobem jest użycie getInstanceStrong()metody, która będzie wykorzystywać najsilniejszy algorytm skonfigurowany i dostarczony przezProvider

  3. NIST zaleca 96 bit IV dla GCM w celu promowania interoperacyjności, wydajności i prostoty projektowania

  4. Aby zapewnić dodatkowe bezpieczeństwo, w poniższej implementacji SecureRandomjest ponownie inicjowany po wygenerowaniu co 2 ^ 16 bajtów pseudolosowego generowania bajtów

  5. Odbiorca musi znać IV, aby móc odszyfrować zaszyfrowany tekst. Dlatego IV należy przesłać wraz z zaszyfrowanym tekstem. Niektóre implementacje wysyłają IV jako AD (Associated Data), co oznacza, że ​​znacznik uwierzytelnienia zostanie obliczony zarówno na podstawie zaszyfrowanego tekstu, jak i IV. Jednak nie jest to wymagane. IV może być po prostu poprzedzony zaszyfrowanym tekstem, ponieważ jeśli IV zostanie zmieniony podczas transmisji z powodu celowego ataku lub błędu sieci / systemu plików, walidacja znacznika uwierzytelniającego i tak zakończy się niepowodzeniem

  6. Ciągów nie należy używać do przechowywania zwykłej wiadomości tekstowej lub klucza, ponieważ ciągi są niezmienne i dlatego nie możemy ich wyczyścić po użyciu. Te nieoczyszczone struny pozostają w pamięci i mogą pojawić się na wysypisku sterty. Z tego samego powodu klient wywołujący te metody szyfrowania lub deszyfrowania powinien wyczyścić wszystkie zmienne lub tablice przechowujące wiadomość lub klucz, gdy nie są już potrzebne.

  7. Żaden dostawca nie jest zakodowany na stałe w kodzie zgodnie z ogólnymi zaleceniami

  8. Wreszcie w przypadku transmisji przez sieć lub pamięć masową klucz lub zaszyfrowany tekst powinien być zakodowany przy użyciu kodowania Base64. Szczegóły dotyczące Base64 można znaleźć tutaj . Należy postępować zgodnie z podejściem Java 8

Tablice bajtów można wyczyścić za pomocą:

Arrays.fill(clearTextMessageByteArray, Byte.MIN_VALUE);

Jednak od wersji Java 8 nie ma łatwego sposobu, aby to wyczyścić, SecretKeyspeca SecretKeyimplementacje tych dwóch interfejsów nie wydają się zaimplementować metody destroy()interfejsu Destroyable. W poniższym kodzie zapisano oddzielną metodę, aby wyczyścić SecretKeySpeci SecretKeyużywać odbicia.

Klucz należy wygenerować przy użyciu jednego z dwóch podejść wymienionych poniżej.

Zauważ, że klucze są sekretami, takimi jak hasła, ale w przeciwieństwie do haseł przeznaczonych do użytku przez ludzi, klucze są przeznaczone do użytku przez algorytmy kryptograficzne i dlatego powinny być generowane tylko w powyższy sposób.

package com.sapbasu.javastudy;

import java.lang.reflect.Field;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Crypto {

  private static final int AUTH_TAG_SIZE = 128; // bits

  // NIST recommendation: "For IVs, it is recommended that implementations
  // restrict support to the length of 96 bits, to
  // promote interoperability, efficiency, and simplicity of design."
  private static final int IV_LEN = 12; // bytes

  // number of random number bytes generated before re-seeding
  private static final double PRNG_RESEED_INTERVAL = Math.pow(2, 16);

  private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding";

  private static final List<Integer> ALLOWED_KEY_SIZES = Arrays
      .asList(new Integer[] {128, 192, 256}); // bits

  private static SecureRandom prng;

  // Used to keep track of random number bytes generated by PRNG
  // (for the purpose of re-seeding)
  private static int bytesGenerated = 0;

  public byte[] encrypt(byte[] input, SecretKeySpec key) throws Exception {

    Objects.requireNonNull(input, "Input message cannot be null");
    Objects.requireNonNull(key, "key cannot be null");

    if (input.length == 0) {
      throw new IllegalArgumentException("Length of message cannot be 0");
    }

    if (!ALLOWED_KEY_SIZES.contains(key.getEncoded().length * 8)) {
      throw new IllegalArgumentException("Size of key must be 128, 192 or 256");
    }

    Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);

    byte[] iv = getIV(IV_LEN);

    GCMParameterSpec gcmParamSpec = new GCMParameterSpec(AUTH_TAG_SIZE, iv);

    cipher.init(Cipher.ENCRYPT_MODE, key, gcmParamSpec);
    byte[] messageCipher = cipher.doFinal(input);

    // Prepend the IV with the message cipher
    byte[] cipherText = new byte[messageCipher.length + IV_LEN];
    System.arraycopy(iv, 0, cipherText, 0, IV_LEN);
    System.arraycopy(messageCipher, 0, cipherText, IV_LEN,
        messageCipher.length);
    return cipherText;
  }

  public byte[] decrypt(byte[] input, SecretKeySpec key) throws Exception {
    Objects.requireNonNull(input, "Input message cannot be null");
    Objects.requireNonNull(key, "key cannot be null");

    if (input.length == 0) {
      throw new IllegalArgumentException("Input array cannot be empty");
    }

    byte[] iv = new byte[IV_LEN];
    System.arraycopy(input, 0, iv, 0, IV_LEN);

    byte[] messageCipher = new byte[input.length - IV_LEN];
    System.arraycopy(input, IV_LEN, messageCipher, 0, input.length - IV_LEN);

    GCMParameterSpec gcmParamSpec = new GCMParameterSpec(AUTH_TAG_SIZE, iv);

    Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
    cipher.init(Cipher.DECRYPT_MODE, key, gcmParamSpec);

    return cipher.doFinal(messageCipher);
  }

  public byte[] getIV(int bytesNum) {

    if (bytesNum < 1) throw new IllegalArgumentException(
        "Number of bytes must be greater than 0");

    byte[] iv = new byte[bytesNum];

    prng = Optional.ofNullable(prng).orElseGet(() -> {
      try {
        prng = SecureRandom.getInstanceStrong();
      } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("Wrong algorithm name", e);
      }
      return prng;
    });

    if (bytesGenerated > PRNG_RESEED_INTERVAL || bytesGenerated == 0) {
      prng.setSeed(prng.generateSeed(bytesNum));
      bytesGenerated = 0;
    }

    prng.nextBytes(iv);
    bytesGenerated = bytesGenerated + bytesNum;

    return iv;
  }

  private static void clearSecret(Destroyable key)
      throws IllegalArgumentException, IllegalAccessException,
      NoSuchFieldException, SecurityException {
    Field keyField = key.getClass().getDeclaredField("key");
    keyField.setAccessible(true);
    byte[] encodedKey = (byte[]) keyField.get(key);
    Arrays.fill(encodedKey, Byte.MIN_VALUE);
  }
}

Klucz szyfrowania można wygenerować przede wszystkim na dwa sposoby:

  • Bez hasła

    KeyGenerator keyGen = KeyGenerator.getInstance("AES");
    keyGen.init(KEY_LEN, SecureRandom.getInstanceStrong());
    SecretKey secretKey = keyGen.generateKey();
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),
        "AES");
    Crypto.clearSecret(secretKey);
    // After encryption or decryption with key
    Crypto.clearSecret(secretKeySpec);
  • Z hasłem

    SecureRandom random = SecureRandom.getInstanceStrong();
    byte[] salt = new byte[32];
    random.nextBytes(salt);
    PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, 
       keyLength);
    SecretKeyFactory keyFactory = 
        SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    SecretKey secretKey = keyFactory.generateSecret(keySpec);
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(),
        "AES");
    Crypto.clearSecret(secretKey);
    // After encryption or decryption with key
    Crypto.clearSecret(secretKeySpec);

Aktualizacja na podstawie komentarzy

Jak wskazał @MaartenBodewes, moja odpowiedź nie obejmowała żadnego, Stringjak wymaga tego pytanie. Dlatego spróbuję wypełnić tę lukę, na wypadek gdyby ktoś natknął się na tę odpowiedź i zastanawiał się, jak sobie poradzić String.

Jak wskazano wcześniej w odpowiedzi, obsługa poufnych informacji w a nie Stringjest generalnie dobrym pomysłem, ponieważ Stringjest niezmienna i dlatego nie możemy ich usunąć po użyciu. I jak wiemy, nawet jeśli a Stringnie ma silnego odniesienia, śmieciarz nie spieszy się natychmiast z usunięciem go ze stosu. W ten sposób Stringplik nadal znajduje się w pamięci przez nieznane okno czasu, mimo że nie jest dostępny dla programu. Problem polega na tym, że zrzut sterty w tym okresie ujawniłby poufne informacje. Dlatego zawsze lepiej jest obsłużyć wszystkie poufne informacje w tablicy bajtów lub tablicy znaków, a następnie wypełnić tablicę zerami, gdy zostanie osiągnięty ich cel.

Jednak przy całej tej wiedzy, jeśli nadal znajdziemy się w sytuacji, w której poufne informacje do zaszyfrowania znajdują się w a String, musimy najpierw przekształcić je w tablicę bajtów i wywołać funkcje encrypti decryptwprowadzone powyżej. (Drugi klucz wejściowy można wygenerować za pomocą fragmentu kodu podanego powyżej).

A Stringmożna przekonwertować na bajty w następujący sposób:

byte[] inputBytes = inputString.getBytes(StandardCharsets.UTF_8);

Począwszy od Java 8, Stringjest wewnętrznie przechowywany w stercie z UTF-16kodowaniem. Jednak użyliśmy UTF-8tutaj, ponieważ zwykle zajmuje mniej miejsca niż UTF-16, szczególnie w przypadku znaków ASCII.

Podobnie zaszyfrowaną tablicę bajtów można również przekonwertować na ciąg, jak poniżej:

String encryptedString = new String(encryptedBytes, StandardCharsets.UTF_8);
Saptarshi Basu
źródło
1
Chociaż chcę pozytywnie ocenić tę odpowiedź, ponieważ wygląda na to, że jest zgodna z obecnymi praktykami kryptograficznymi, w ogóle nie widzę żadnej obsługi ciągów, co sprawia, że ​​jest to bardziej jak opis korzystania z trybu GCM. Jako taka nie odpowiada na pytanie .
Maarten Bodewes
1
@MaartenBodewes Bardzo dziękuję za poświęcenie czasu na przejrzenie i podzielenie się opiniami. Napisałem to ze zrozumieniem, że zaszyfrowanie Stringprzy użyciu funkcji stworzonych powyżej byłoby trywialne. Jednak po drugim spojrzeniu po przeczytaniu Twojego komentarza rozumiem, że może to nie być oczywiste. Na pewno edytuję, aby dodać te szczegóły.
Saptarshi Basu
5

Co powiesz na to:

private static byte[] xor(final byte[] input, final byte[] secret) {
    final byte[] output = new byte[input.length];
    if (secret.length == 0) {
        throw new IllegalArgumentException("empty security key");
    }
    int spos = 0;
    for (int pos = 0; pos < input.length; ++pos) {
        output[pos] = (byte) (input[pos] ^ secret[spos]);
        ++spos;
        if (spos >= secret.length) {
            spos = 0;
        }
    }
    return output;
}

U mnie działa dobrze i jest raczej kompaktowy.

yegor256
źródło
co się stanie, jeśli parametr wejściowy secret == null lub input == null? praca z bajtami zamiast z łańcuchami jest w porządku, ale w moim przypadku nie miała to znaczenia. Jedyne, co się liczy, to to, że musi to być czytelne i dekodowalne na dowolnym urządzeniu, w każdym możliwym kodowaniu znaków ...
ante.sabo
@ ante.sabo najwyraźniej wyrzuci NPE. To jedyna rzecz, którą można zrobić z wartościami NULL.
Miha_x64
Tak długo, jak długo input.length <= secret.lengthblokuje i nie secretjest używane ponownie, jest to bezpieczne i nazywa się one-time-pad. W tym przypadku input.length > secret.lengthjest to wariant szyfru Vigenère'a i uważany za bardzo słaby.
trichner
5

Możesz użyć Jasypt

Dzięki Jasypt szyfrowanie i sprawdzanie hasła może być tak proste, jak ...

StrongTextEncryptor textEncryptor = new StrongTextEncryptor();
textEncryptor.setPassword(myEncryptionPassword);

Szyfrowanie:

String myEncryptedText = textEncryptor.encrypt(myText);

Deszyfrowanie:

String plainText = textEncryptor.decrypt(myEncryptedText);

Gradle:

compile group: 'org.jasypt', name: 'jasypt', version: '1.9.2'

Cechy:

Jasypt zapewnia łatwe jednokierunkowe (podsumowanie) i dwukierunkowe techniki szyfrowania.

Otwarty interfejs API do użytku z dowolnym dostawcą JCE, a nie tylko z domyślnym Java VM. Jasypt może być łatwo używany ze znanymi dostawcami, takimi jak Bouncy Castle. Ucz się więcej.

Wyższe bezpieczeństwo haseł użytkowników. Ucz się więcej.

Obsługa szyfrowania binarnego. Jasypt umożliwia skrót i szyfrowanie plików binarnych (tablice bajtów). Szyfruj swoje obiekty lub pliki w razie potrzeby (na przykład do wysłania przez sieć).

Obsługa szyfrowania liczb. Oprócz tekstów i plików binarnych umożliwia skrót i szyfrowanie wartości liczbowych (BigInteger i BigDecimal, inne typy liczbowe są obsługiwane podczas szyfrowania w celu trwałości hibernacji). Ucz się więcej.

Całkowicie bezpieczne dla wątków.

Wsparcie dla puli programów szyfrujących / fermentujących w celu osiągnięcia wysokiej wydajności w systemach wieloprocesorowych / wielordzeniowych.

Zawiera lekką („lite”) wersję biblioteki, która ułatwia zarządzanie w środowiskach o ograniczonych rozmiarach, takich jak platformy mobilne.

Zapewnia zarówno proste, niewymagające konfiguracji narzędzia do szyfrowania dla nowych użytkowników, jak również wysoce konfigurowalne standardowe narzędzia szyfrowania dla zaawansowanych użytkowników.

Opcjonalna integracja Hibernate 3 i 4 do utrwalania pól mapowanych jednostek w sposób zaszyfrowany. Szyfrowanie pól jest zdefiniowane w plikach mapowania Hibernate i pozostaje przezroczyste dla reszty aplikacji (przydatne w przypadku wrażliwych danych osobowych, baz danych z wieloma użytkownikami z możliwością odczytu ...). Szyfruj teksty, pliki binarne, liczby, wartości logiczne, daty ... Dowiedz się więcej.

Bezproblemowa integracja z aplikacją Spring, z określonymi funkcjami integracji dla Spring 2, Spring 3.0 i Spring 3.1. Wszystkie wariatory i programy szyfrujące w jasypt są zaprojektowane tak, aby były łatwe w użyciu (tworzenie instancji, wstrzykiwanie zależności ...) od Springa. A ponieważ są bezpieczne dla wątków, mogą być używane bez obaw o synchronizację w środowisku zorientowanym na singleton, takim jak Spring. Więcej informacji: wiosna 2, wiosna 3.0, wiosna 3.1.

Opcjonalna integracja Spring Security (dawniej Acegi Security) w celu wykonywania szyfrowania haseł i dopasowywania zadań dla struktury bezpieczeństwa, zwiększania bezpieczeństwa haseł użytkowników poprzez stosowanie bezpieczniejszych mechanizmów szyfrowania haseł i zapewniania wyższego stopnia konfiguracji i kontroli. Ucz się więcej.

Zapewnia zaawansowane funkcje szyfrowania całości lub części plików konfiguracyjnych aplikacji, w tym poufnych informacji, takich jak hasła do baz danych. Bezproblemowo integruj zaszyfrowaną konfigurację ze zwykłymi aplikacjami Spring i / lub Hibernate. Ucz się więcej.

Zapewnia łatwe w użyciu narzędzia CLI (interfejs wiersza poleceń), które umożliwiają programistom inicjowanie zaszyfrowanych danych i włączanie operacji szyfrowania / deszyfrowania / podsumowania do zadań konserwacyjnych lub skryptów. Ucz się więcej.

Integruje się z Apache Wicket, zapewniając bardziej niezawodne szyfrowanie adresów URL w bezpiecznych aplikacjach.

Kompleksowe przewodniki i dokumentacja javadoc, aby umożliwić programistom lepsze zrozumienie tego, co naprawdę robią ze swoimi danymi.

Solidna obsługa zestawu znaków, zaprojektowana w celu odpowiedniego szyfrowania i odczytywania tekstów, niezależnie od tego, jaki jest oryginalny zestaw znaków. Pełne wsparcie dla języków takich jak japoński, koreański, arabski ... bez problemów z kodowaniem i platformą.

Bardzo wysoki poziom możliwości konfiguracyjnych: programista może zastosować sztuczki, takie jak poinstruowanie programu szyfrującego, aby poprosił na przykład zdalny serwer HTTPS o hasło, które ma być używane do szyfrowania. Pozwala spełnić Twoje potrzeby w zakresie bezpieczeństwa.

Kamil Nekanowicz
źródło
1
Ale jakie Jasyptzapewnia bezpieczeństwo ? Nie mogę tego rozgryźć na podstawie ich strony internetowej. Czy jest nie do odróżnienia w atakach z wybranym tekstem jawnym? Integralność? Poufność?
trichner
4

Oto moja implementacja z meta64.com jako Spring Singleton. Jeśli chcesz utworzyć instancję cipera dla każdego wywołania, która również działałaby, a następnie możesz usunąć wywołania „zsynchronizowane”, ale uważaj, że „szyfr” nie jest bezpieczny dla wątków.

import java.security.Key;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("singleton")
public class Encryptor {

    @Value("${aeskey}")
    private String keyStr;

    private Key aesKey = null;
    private Cipher cipher = null;

    synchronized private void init() throws Exception {
        if (keyStr == null || keyStr.length() != 16) {
            throw new Exception("bad aes key configured");
        }
        if (aesKey == null) {
            aesKey = new SecretKeySpec(keyStr.getBytes(), "AES");
            cipher = Cipher.getInstance("AES");
        }
    }

    synchronized public String encrypt(String text) throws Exception {
        init();
        cipher.init(Cipher.ENCRYPT_MODE, aesKey);
        return toHexString(cipher.doFinal(text.getBytes()));
    }

    synchronized public String decrypt(String text) throws Exception {
        init();
        cipher.init(Cipher.DECRYPT_MODE, aesKey);
        return new String(cipher.doFinal(toByteArray(text)));
    }

    public static String toHexString(byte[] array) {
        return DatatypeConverter.printHexBinary(array);
    }

    public static byte[] toByteArray(String s) {
        return DatatypeConverter.parseHexBinary(s);
    }

    /*
     * DO NOT DELETE
     * 
     * Use this commented code if you don't like using DatatypeConverter dependency
     */
    // public static String toHexStringOld(byte[] bytes) {
    // StringBuilder sb = new StringBuilder();
    // for (byte b : bytes) {
    // sb.append(String.format("%02X", b));
    // }
    // return sb.toString();
    // }
    //
    // public static byte[] toByteArrayOld(String s) {
    // int len = s.length();
    // byte[] data = new byte[len / 2];
    // for (int i = 0; i < len; i += 2) {
    // data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i +
    // 1), 16));
    // }
    // return data;
    // }
}
mkobit
źródło
3
To będzie szyfrować w trybie EBC, który jest okropny. Powinieneś ustawić przynajmniej tryb CBC lub tryb GCM
Konstantino Sparakis
Dzięki za sugestię Konstantinto, wyszukałem go w Google i znalazłem kod, który używa „AES / CBC / PKCS5Padding” jako ciągu inicjującego dla Cipher, zamiast tylko „AES”, ale przyjrzę się temu dokładniej. Lub jeśli chcesz, możesz zapewnić rzeczywistą poprawkę, aby inni mogli zobaczyć lepszy sposób. Jednak poza szczegółami dotyczącymi CBC uważam, że moje rozwiązanie jest najprostsze i najbezpieczniejsze, a przede wszystkim zasługuje na uznanie.
Tak, nie martw się, Crypto to skomplikowany temat. Niestety każda implementacja na tej stronie jest zepsuta i niestety jest to pierwsza strona, która pojawia się podczas wyszukiwania w Google hasła „jak zrobić szyfrowanie java”. Kiedy nadarzy się okazja, spróbuję je wszystkie naprawić.
Konstantino Sparakis
Mój przykład jest taki sam jak ten: docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/… Z wyjątkiem tego, że potrzebowałem Cipher.getInstance ("AES / ECB / PKCS5Padding"); Mój kod zakłada, że ​​istnieje plik właściwości z idealnie 16-bajtowym kluczem szyfrowania, ale w celu zaszyfrowania ciągu znaków z hasła „dostarczonego przez użytkownika” strona Oracle (link powyżej) również pokazuje, jak to zrobić.
1
Zatem problem z EBC polega na tym, że jest on wyjątkowo wrażliwy na analizę częstotliwości. Jest słynny przykład pingwina linuksowego, blog.filippo.io/the-ecb-penguin. Zobaczcie, że chociaż obraz jest zaszyfrowany, nadal można stwierdzić, że jest to pingwin. Poszedłem dalej i spisałem swoje przemyślenia na ten temat poniżej :) stackoverflow.com/a/43779197/2607972
Konstantino Sparakis
4

Tutaj proste rozwiązanie z tylko java.*i javax.crypto.*zależnościami do szyfrowania bajtów zapewniające poufność i integralność . Powinien być nie do odróżnienia w przypadku wybranego ataku w postaci zwykłego tekstu dla krótkich wiadomości w kolejności kilobajtów.

Używa AESw GCMtrybie bez dopełnienia, 128-bitowy klucz jest uzyskiwany przez PBKDF2wiele iteracji i statyczną sól z podanego hasła. Dzięki temu brutalne wymuszanie haseł jest trudne i rozkłada entropię na cały klucz.

Generowany jest losowy wektor inicjujący (IV), który zostanie dołączony do zaszyfrowanego tekstu. Ponadto bajt statyczny 0x01jest dodawany jako pierwszy bajt jako „wersja”.

Cała wiadomość trafia do kodu uwierzytelniania wiadomości (MAC) wygenerowanego przez AES/GCM.

Tak więc, klasa szyfrowania zerowych zależności zewnętrznych zapewniająca poufność i integralność :

package ch.n1b.tcrypt.utils;

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * This class implements AES-GCM symmetric key encryption with a PBKDF2 derived password.
 * It provides confidentiality and integrity of the plaintext.
 *
 * @author Thomas Richner
 * @created 2018-12-07
 */
public class AesGcmCryptor {

    // /crypto/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode
    private static final byte VERSION_BYTE = 0x01;
    private static final int VERSION_BYTE_LENGTH = 1;
    private static final int AES_KEY_BITS_LENGTH = 128;


    // fixed AES-GCM constants
    private static final String GCM_CRYPTO_NAME = "AES/GCM/NoPadding";
    private static final int GCM_IV_BYTES_LENGTH = 12;
    private static final int GCM_TAG_BYTES_LENGTH = 16;

    // can be tweaked, more iterations = more compute intensive to brute-force password
    private static final int PBKDF2_ITERATIONS = 1024;

    // protects against rainbow tables
    private static final byte[] PBKDF2_SALT = hexStringToByteArray("4d3fe0d71d2abd2828e7a3196ea450d4");

    public String encryptString(char[] password, String plaintext) throws CryptoException {

        byte[] encrypted = null;
        try {
            encrypted = encrypt(password, plaintext.getBytes(StandardCharsets.UTF_8));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException //
                | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException //
                | InvalidKeySpecException e) {
            throw new CryptoException(e);
        }
        return byteArrayToHexString(encrypted);
    }

    public String decryptString(char[] password, String ciphertext)
            throws CryptoException {

        byte[] ct = hexStringToByteArray(ciphertext);
        byte[] plaintext = null;
        try {
            plaintext = decrypt(password, ct);
        } catch (AEADBadTagException e) {
            throw new CryptoException(e);
        } catch ( //
                NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeySpecException //
                        | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException //
                        | BadPaddingException e) {
            throw new CryptoException(e);
        }
        return new String(plaintext, StandardCharsets.UTF_8);
    }

    /**
     * Decrypts an AES-GCM encrypted ciphertext and is
     * the reverse operation of {@link AesGcmCryptor#encrypt(char[], byte[])}
     *
     * @param password   passphrase for decryption
     * @param ciphertext encrypted bytes
     * @return plaintext bytes
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws IllegalArgumentException           if the length or format of the ciphertext is bad
     * @throws CryptoException
     */
    public byte[] decrypt(char[] password, byte[] ciphertext)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {

        // input validation
        if (ciphertext == null) {
            throw new IllegalArgumentException("ciphertext cannot be null");
        }

        if (ciphertext.length <= VERSION_BYTE_LENGTH + GCM_IV_BYTES_LENGTH + GCM_TAG_BYTES_LENGTH) {
            throw new IllegalArgumentException("ciphertext too short");
        }

        // the version must match, we don't decrypt other versions
        if (ciphertext[0] != VERSION_BYTE) {
            throw new IllegalArgumentException("wrong version: " + ciphertext[0]);
        }

        // input seems legit, lets decrypt and check integrity

        // derive key from password
        SecretKey key = deriveAesKey(password, PBKDF2_SALT, AES_KEY_BITS_LENGTH);

        // init cipher
        Cipher cipher = Cipher.getInstance(GCM_CRYPTO_NAME);
        GCMParameterSpec params = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * 8,
                ciphertext,
                VERSION_BYTE_LENGTH,
                GCM_IV_BYTES_LENGTH
        );
        cipher.init(Cipher.DECRYPT_MODE, key, params);

        final int ciphertextOffset = VERSION_BYTE_LENGTH + GCM_IV_BYTES_LENGTH;

        // add version and IV to MAC
        cipher.updateAAD(ciphertext, 0, ciphertextOffset);

        // decipher and check MAC
        return cipher.doFinal(ciphertext, ciphertextOffset, ciphertext.length - ciphertextOffset);
    }

    /**
     * Encrypts a plaintext with a password.
     * <p>
     * The encryption provides the following security properties:
     * Confidentiality + Integrity
     * <p>
     * This is achieved my using the AES-GCM AEAD blockmode with a randomized IV.
     * <p>
     * The tag is calculated over the version byte, the IV as well as the ciphertext.
     * <p>
     * Finally the encrypted bytes have the following structure:
     * <pre>
     *          +-------------------------------------------------------------------+
     *          |         |               |                             |           |
     *          | version | IV bytes      | ciphertext bytes            |    tag    |
     *          |         |               |                             |           |
     *          +-------------------------------------------------------------------+
     * Length:     1B        12B            len(plaintext) bytes            16B
     * </pre>
     * Note: There is no padding required for AES-GCM, but this also implies that
     * the exact plaintext length is revealed.
     *
     * @param password  password to use for encryption
     * @param plaintext plaintext to encrypt
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws NoSuchPaddingException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidKeySpecException
     */
    public byte[] encrypt(char[] password, byte[] plaintext)
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException,
            InvalidKeySpecException {

        // initialise random and generate IV (initialisation vector)
        SecretKey key = deriveAesKey(password, PBKDF2_SALT, AES_KEY_BITS_LENGTH);
        final byte[] iv = new byte[GCM_IV_BYTES_LENGTH];
        SecureRandom random = SecureRandom.getInstanceStrong();
        random.nextBytes(iv);

        // encrypt
        Cipher cipher = Cipher.getInstance(GCM_CRYPTO_NAME);
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * 8, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);

        // add IV to MAC
        final byte[] versionBytes = new byte[]{VERSION_BYTE};
        cipher.updateAAD(versionBytes);
        cipher.updateAAD(iv);

        // encrypt and MAC plaintext
        byte[] ciphertext = cipher.doFinal(plaintext);

        // prepend VERSION and IV to ciphertext
        byte[] encrypted = new byte[1 + GCM_IV_BYTES_LENGTH + ciphertext.length];
        int pos = 0;
        System.arraycopy(versionBytes, 0, encrypted, 0, VERSION_BYTE_LENGTH);
        pos += VERSION_BYTE_LENGTH;
        System.arraycopy(iv, 0, encrypted, pos, iv.length);
        pos += iv.length;
        System.arraycopy(ciphertext, 0, encrypted, pos, ciphertext.length);

        return encrypted;
    }

    /**
     * We derive a fixed length AES key with uniform entropy from a provided
     * passphrase. This is done with PBKDF2/HMAC256 with a fixed count
     * of iterations and a provided salt.
     *
     * @param password passphrase to derive key from
     * @param salt     salt for PBKDF2 if possible use a per-key salt, alternatively
     *                 a random constant salt is better than no salt.
     * @param keyLen   number of key bits to output
     * @return a SecretKey for AES derived from a passphrase
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private SecretKey deriveAesKey(char[] password, byte[] salt, int keyLen)
            throws NoSuchAlgorithmException, InvalidKeySpecException {

        if (password == null || salt == null || keyLen <= 0) {
            throw new IllegalArgumentException();
        }
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, keyLen);
        SecretKey pbeKey = factory.generateSecret(spec);

        return new SecretKeySpec(pbeKey.getEncoded(), "AES");
    }

    /**
     * Helper to convert hex strings to bytes.
     * <p>
     * May be used to read bytes from constants.
     */
    private static byte[] hexStringToByteArray(String s) {

        if (s == null) {
            throw new IllegalArgumentException("Provided `null` string.");
        }

        int len = s.length();
        if (len % 2 != 0) {
            throw new IllegalArgumentException("Invalid length: " + len);
        }

        byte[] data = new byte[len / 2];
        for (int i = 0; i < len - 1; i += 2) {
            byte b = (byte) toHexDigit(s, i);
            b <<= 4;
            b |= toHexDigit(s, i + 1);
            data[i / 2] = b;
        }
        return data;
    }

    private static int toHexDigit(String s, int pos) {
        int d = Character.digit(s.charAt(pos), 16);
        if (d < 0) {
            throw new IllegalArgumentException("Cannot parse hex digit: " + s + " at " + pos);
        }
        return d;
    }

    private static String byteArrayToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }

    public class CryptoException extends Exception {

        public CryptoException(Throwable cause) {
            super(cause);
        }
    }
}

Tutaj cały projekt z ładnym CLI: https://github.com/trichner/tcrypt

Edycja: teraz z odpowiednimi encryptStringidecryptString

trichner
źródło
To jest niesamowite. Dziękuję Ci! Wiele się nauczyłem z twojego kodu i po utworzeniu klasy BadVersionException Exception, twój kod działał idealnie za pierwszym razem. Doskonały!!
Morkus
Podoba mi się ta próba. To powiedziawszy ... Sól powinna być przypadkowa, a nie statyczna. Iteracje prawdopodobnie również nie powinny być statyczne. GCM już zawiera IV przy obliczaniu tagu. Nie zawiera jednak numeru wersji. Nie należy określać dostawcy przenośności, opcja „SunJCE” będzie domyślna na platformach, które go obsługują. Ten kod nie zawiera żadnej obsługi ciągu komunikatów, która jest wymagana dla tego konkretnego pytania .
Maarten Bodewes
W porządku, trochę bardziej wyczyściłem i dodałem żądane encryptStringi decryptString:)
trichner
To działało bardzo dobrze; ty dla kodu. Należy zauważyć, że ten kod wymaga interfejsu API 19 (Kit Kat) lub nowszego do prawidłowego działania.
PGMacDesign
3

Rozważałbym użycie czegoś takiego jak https://www.bouncycastle.org/ Jest to wstępnie zbudowana biblioteka, która umożliwia szyfrowanie, co chcesz, za pomocą wielu różnych szyfrów.Rozumiem, że chcesz chronić tylko przed szpiegowaniem, ale jeśli naprawdę chcesz chronić informacje, używanie Base64 w rzeczywistości cię nie ochroni.

hdost
źródło
1
Samo zalecenie losowej biblioteki kryptograficznej z szyframi nie jest odpowiedzią na pytanie. Poza tym, dlaczego nie skorzystać z wbudowanych szyfrów?
Maarten Bodewes
2

Oto kilka linków, dzięki którym możesz przeczytać, co obsługuje Java

Szyfrowanie / deszyfrowanie strumienia danych.

Ten przykład pokazuje, jak zaszyfrować (za pomocą algorytmu szyfrowania symetrycznego, takiego jak AES, Blowfish, RC2, 3DES itp.) Dużej ilości danych. Dane są przesyłane fragmentami do jednej z metod szyfrowania: EncryptBytes, EncryptString, EncryptBytesENC lub EncryptStringENC. (Nazwa metody wskazuje typ danych wejściowych (tablica ciągów lub bajtów) i typ zwracanego (zakodowany ciąg lub tablica bajtów). Właściwości FirstChunk i LastChunk służą do wskazania, czy porcja jest pierwszą, środkową czy ostatnią w strumieniu mają być zaszyfrowane. Domyślnie zarówno FirstChunk, jak i LastChunk mają wartość true - co oznacza, że ​​przekazane dane to cała kwota.

JCERefGuide

Przykłady szyfrowania Java

Markus Lausberg
źródło
Tak, kryptografia jest obsługiwana przez Javę. Szyfrowanie strumienia również nie jest tym, o co proszono.
Maarten Bodewes
2

Jak wielu z nich już powiedziało, powinieneś użyć standardowego szyfru, który jest nadmiernie używany, jak DES lub AES.

Prosty przykład tego, jak można zaszyfrować i odszyfrować ciąg znaków w Javie przy użyciu AES .

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class EncryptorDemo {

    public static String encrypt(String key, String randomVector, String value) {
        try {
            IvParameterSpec iv = new IvParameterSpec(randomVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(value.getBytes());
            System.out.println("encrypted text: "  + Base64.encodeBase64String(encrypted));
            return Base64.encodeBase64String(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String key, String randomVector, String encrypted) {
        try {
            IvParameterSpec iv = new IvParameterSpec(randomVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] originalText = cipher.doFinal(Base64.decodeBase64(encrypted));
            System.out.println("decrypted text: "  + new String(originalText));
            return new String(originalText);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        String key = "JavasEncryptDemo"; // 128 bit key
        String randomVector = "RandomJavaVector"; // 16 bytes IV
        decrypt(key, randomVector, encrypt(key, randomVector, "Anything you want to encrypt!"));

    }
}
viveknaskar
źródło
CBC nie jest już bezpiecznym trybem. Padding jest podatny na wypełnianie ataków Oracle. Ponadto obsługa klucza i komunikatów w String nie jest bezpieczna. Zostaną w basenie String i pojawią się na wysypisku
Saptarshi Basu
2
Doceń komentarz. To był prosty przykład metod szyfrowania i deszyfrowania języka Java, o co prosił użytkownik. Pytanie zostało zadane około 9 lat temu i na tej podstawie udzielono odpowiedzi. Dzięki.
viveknaskar
2
Tak, wydaje się, że to prosty sposób na wprowadzenie szyfrowania / odszyfrowywania. Zadziałało dla mnie jak urok… Dzięki.
Codewrapper
0

Oto rozwiązanie kopiuj / wklej. Polecam również przeczytanie i zagłosowanie na odpowiedź @ Konstantino, mimo że nie dostarcza ona żadnego kodu. Wektor inicjalizacyjny (IV) jest jak sól - nie musi być trzymany w tajemnicy. Jestem nowy w GCM i najwyraźniej AAD jest opcjonalny i używany tylko w określonych okolicznościach. Ustaw klucz w zmiennej środowiskowej SECRET_KEY_BASE. Użyj czegoś takiego jak KeePass, aby wygenerować 32-znakowe hasło. To rozwiązanie jest wzorowane na moim rozwiązaniu Ruby.

    public static String encrypt(String s) {
        try {
            byte[] input = s.getBytes("UTF-8");
            String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
            if (keyString == null || keyString.length() == 0) {
                Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
                return null;
            }
            byte[] keyBytes = keyString.getBytes("UTF-8");
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            // generate IV
            SecureRandom secureRandom = SecureRandom.getInstanceStrong();
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            byte[] ivBytes = new byte[cipher.getBlockSize()];
            secureRandom.nextBytes(ivBytes);
            GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes); // 96 bit tag length
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
            // generate AAD
//          byte[] aadBytes = new byte[cipher.getBlockSize()];
//          secureRandom.nextBytes(aadBytes);
//          cipher.updateAAD(aadBytes);
            // encrypt
            byte[] encrypted = cipher.doFinal(input);
            byte[] returnBytes = new byte[ivBytes.length + encrypted.length];
//          byte[] returnBytes = new byte[ivBytes.length + aadBytes.length + encrypted.length];
            System.arraycopy(ivBytes, 0, returnBytes, 0, ivBytes.length);
//          System.arraycopy(aadBytes, 0, returnBytes, ivBytes.length, aadBytes.length);
            System.arraycopy(encrypted, 0, returnBytes, ivBytes.length, encrypted.length);
//          System.arraycopy(encrypted, 0, returnBytes, ivBytes.length+aadBytes.length, encrypted.length);
            String encryptedString = Base64.getEncoder().encodeToString(returnBytes);
            return encryptedString;
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | 
                InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            Logger.error(Utils.class, "encrypt()", "Could not encrypt string: " + e.getMessage());
            return null;
        }
    }

    public static String decrypt(String s) {
        if (s == null || s.length() == 0) return "";
        try {
            byte[] encrypted = Base64.getDecoder().decode(s);
            String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
            if (keyString == null || keyString.length() == 0) {
                Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
                return null;
            }
            byte[] keyBytes = keyString.getBytes("UTF-8");
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            byte[] ivBytes = new byte[cipher.getBlockSize()];
            System.arraycopy(encrypted, 0, ivBytes, 0, ivBytes.length);
            GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
//          cipher.updateAAD(encrypted, ivBytes.length, cipher.getBlockSize());
            byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize(), encrypted.length - cipher.getBlockSize());
//          byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize()*2, encrypted.length - cipher.getBlockSize()*2);
            String decryptedString = new String(decrypted, "UTF-8");
            return decryptedString;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedEncodingException | InvalidKeyException | 
                InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            Logger.error(Utils.class, "decrypt()", "Could not decrypt string: " + e.getMessage());
            return null;
        }
    }

Oto przykład:

    String s = "This is a test.";
    String enc = Utils.encrypt(s);
    System.out.println(enc);
    // fQHfYjbD+xAuN5XzH2ojk/EWNeKXUrKRSfx8LU+5dpuKkM/pueCMBjKCZw==
    String dec = Utils.decrypt(enc);
    System.out.println(dec);
    // This is a test.
Chloe
źródło
-4

Możesz rozważyć użycie zautomatyzowanego narzędzia do generowania kodu szyfrowania / deszyfrowania, np. https://www.stringencrypt.com/java-encryption/

Może generować za każdym razem inny kod szyfrowania i deszyfrowania dla ciągu lub szyfrowania pliku.

Jest to bardzo przydatne, jeśli chodzi o szybkie szyfrowanie ciągów bez używania RSA, AES itp.

Przykładowe wyniki:

// encrypted with https://www.stringencrypt.com (v1.1.0) [Java]
// szTest = "Encryption in Java!"
String szTest = "\u9E3F\uA60F\uAE07\uB61B\uBE1F\uC62B\uCE2D\uD611" +
                "\uDE03\uE5FF\uEEED\uF699\uFE3D\u071C\u0ED2\u1692" +
                "\u1E06\u26AE\u2EDC";

for (int iatwS = 0, qUJQG = 0; iatwS < 19; iatwS++)
{
        qUJQG = szTest.charAt(iatwS);
        qUJQG ++;
        qUJQG = ((qUJQG << 5) | ( (qUJQG & 0xFFFF) >> 11)) & 0xFFFF;
        qUJQG -= iatwS;
        qUJQG = (((qUJQG & 0xFFFF) >> 6) | (qUJQG << 10)) & 0xFFFF;
        qUJQG ^= iatwS;
        qUJQG -= iatwS;
        qUJQG = (((qUJQG & 0xFFFF) >> 3) | (qUJQG << 13)) & 0xFFFF;
        qUJQG ^= 0xFFFF;
        qUJQG ^= 0xB6EC;
        qUJQG = ((qUJQG << 8) | ( (qUJQG & 0xFFFF) >> 8)) & 0xFFFF;
        qUJQG --;
        qUJQG = (((qUJQG & 0xFFFF) >> 5) | (qUJQG << 11)) & 0xFFFF;
        qUJQG ++;
        qUJQG ^= 0xFFFF;
        qUJQG += iatwS;
        szTest = szTest.substring(0, iatwS) + (char)(qUJQG & 0xFFFF) + szTest.substring(iatwS + 1);
}

System.out.println(szTest);

Używamy go cały czas w naszej firmie.

Bartosz Wójcik
źródło
To jest bezpieczeństwo poprzez zaciemnienie i nie jest naprawdę bezpieczne.
Chloe
To pytanie dotyczy rzeczywistego, nowoczesnego szyfrowania kryptograficznego, takiego jak AES, a nie tylko zaciemniania, aby łańcuchy były trudniejsze do statycznego wyodrębnienia. To nawet nie wydaje się utrzymywać żadnego stanu między znakami, więc jest podatny na analizę częstotliwości. ( Szyfr oparty na pojedynczym alfabecie , z wyjątkiem punktów kodowych UTF-16 zamiast alfabetu łacińskiego. Ale jeśli użyjesz go w angielskim tekście ASCII, otrzymasz tylko kilka unikalnych 16-bitowych wartości znaków, chyba że źle to odczytam)
Peter Cordes
-4
String s1="arshad"; 
char[] s2=s1.toCharArray(); 
int s3= s2.length; 

  System.out.println(s3);
 int i=0; 

// for(int j=0;j<s3;j++) 
// System.out.println(s2[j]); 

for(i=0;i<((s3)/2);i++) { 

char z,f=10; 
z=(char) (s2[i] * f); 
s2[i]=s2[(s3-1)-i]; 
s2[(s3-1)-i]=z; 

String b=new String(s2);

 print(b);  }
Arshad shaik
źródło
Formalnie szyfruje dane do nieczytelnego formatu. Aby odszyfrować, użyj tego samego kodu. I zmień s [i] * f na s [I] / f.
Arshad shaik
To jest bezpieczeństwo poprzez zaciemnienie i nie jest naprawdę bezpieczne.
Chloe
-5
public static String encryptParams(String myTextInput) {

        String myKey = "40674244454045cb9a70040a30e1c007";
        String myVector = "@1B2c3D4e5F6g7H8";

        String encData = "";

        try{
            JavaEncryprtionUtil encUtil = new JavaEncryprtionUtil();
            encData = Base64.encodeToString(encUtil.encrypt(myTextInput.getBytes("UTF-8"), myKey.getBytes("UTF-8"), myVector.getBytes("UTF-8")),Base64.DEFAULT);
            System.out.println(encData);
        }catch(NoSuchAlgorithmException ex){
            ex.printStackTrace();
        }catch(NoSuchPaddingException ex){
            ex.printStackTrace();
        }catch(InvalidKeyException ex){
            ex.printStackTrace();
        }catch(InvalidAlgorithmParameterException ex){
            ex.printStackTrace();
        }catch(IllegalBlockSizeException ex){
            ex.printStackTrace();
        }catch(BadPaddingException ex){
            ex.printStackTrace();
        }catch(UnsupportedEncodingException ex){
            ex.printStackTrace();
        }

        return encData;
    }
rishikesh
źródło
1
czy JavaEncryprtionUtil jest częścią JDK API? jeśli nie, przeliteruj nazwę biblioteki.
Mały uczeń Fermata
4
Nie mogę znaleźć tej klasy. Wydaje się, że odpowiedź jest wymyślona.
james.garriss