Java AES-kryptering og dekryptering

Java Top

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

1. Oversikt

Den symmetriske nøkkelblokk krypteringen spiller en viktig rolle i datakryptering. Det betyr at samme nøkkel brukes til både kryptering og dekryptering. Advanced Encryption Standard (AES) er en mye brukt symmetrisk nøkkelkrypteringsalgoritme.

I denne opplæringen vil vi se hvordan du implementerer AES-kryptering og dekryptering ved hjelp av Java Cryptography Architecture (JCA) i JDK.

2. AES-algoritme

AES-algoritmen er en iterativ, symmetrisk nøkkelblokk-kryptering som støtter kryptografiske nøkler (hemmelige nøkler) på 128, 192 og 256 bits for å kryptere og dekryptere data i blokker på 128 bits. Figuren nedenfor viser AES-algoritmen på høyt nivå:

Hvis dataene som skal krypteres ikke oppfyller kravet om blokkstørrelse på 128 bits, må de polstres. Padding er en prosess for å fylle opp den siste blokken til 128 bits.

3. AES-variasjoner

AES-algoritmen har seks driftsmåter:

  1. ECB (elektronisk kodebok)
  2. CBC (Chifferblokk kjetting)
  3. CFB (Cipher FeedBack)
  4. OFB (tilbakemelding)
  5. CTR (teller)
  6. GCM (Galois / Counter Mode)

Driftsmåten kan brukes for å styrke effekten av krypteringsalgoritmen. Videre kan driftsmåten konvertere blokkrypteringen til en strømkryptering. Hver modus har sin styrke og svakhet. La oss ta en rask gjennomgang.

3.1. ECB

Denne driftsmåten er den enkleste av alle. Klarteksten er delt inn i blokker med en størrelse på 128 bits. Deretter blir hver blokk kryptert med samme nøkkel og algoritme. Derfor gir det samme resultat for samme blokk. Dette er den viktigste svakheten i denne modusen og det anbefales ikke for kryptering. Det krever polstringsdata.

3.2. CBC

For å overvinne ECB-svakheten bruker CBC-modus en initialiseringsvektor (IV) for å øke krypteringen. For det første bruker CBC klartekstblokken xor med IV. Deretter krypterer det resultatet til krypteringsteksten. I neste blokk bruker den krypteringsresultatet til å xorere med ren tekstblokk til siste blokk.

I denne modusen kan ikke kryptering parallelliseres, men dekryptering kan parallelliseres. Det krever også polstringsdata.

3.3. CFB

Denne modusen kan brukes som en stream cipher. Først krypterer den IV, deretter xorerer den med klartekstblokken for å få krypteringstekst. Deretter krypterer CFB krypteringsresultatet til x eller ren tekst. Det trenger en IV.

I denne modusen kan dekryptering parallelliseres, men kryptering kan ikke parallelliseres.

3.4. OFB

Denne modusen kan brukes som en stream cipher. For det første krypterer den IV. Deretter bruker den krypteringsresultatene til å xeller ren tekst for å få krypteringstekst.

Det krever ikke polstringsdata og vil ikke bli påvirket av den støyende blokken.

3.5. CTR

Denne modusen bruker verdien på en teller som en IV. Det ligner veldig på OFB, men det bruker telleren til å bli kryptert hver gang i stedet for IV.

Denne modusen har to styrker, inkludert kryptering / dekryptering parallellisering, og støy i en blokk påvirker ikke andre blokker.

3.6. GCM

Denne modusen er en utvidelse av CTR-modus. GCM har fått betydelig oppmerksomhet og anbefales av NIST. GCM-modellen sender ut krypteringstekst og en godkjenningsmerke. Den største fordelen med denne modusen, sammenlignet med andre driftsmodi i algoritmen, er dens effektivitet.

I denne opplæringen bruker vi AES / CBC / PKCS5Padding algoritme fordi den er mye brukt i mange prosjekter.

3.7. Størrelse på data etter kryptering

Som nevnt tidligere har AES en blokkstørrelse på 128 bits eller 16 byte. AES endrer ikke størrelsen, og krypteringstekststørrelsen er lik klartekststørrelsen. I ECB- og CBC-modus bør vi også bruke en polstringsalgoritme som PKCS 5. Så størrelsen på data etter kryptering er:

ciphertext_size (bytes) = cleartext_size + (16 - (cleartext_size% 16))

For å lagre IV med ciphertext, må vi legge til 16 byte til.

4. AES-parametere

I AES-algoritmen trenger vi tre parametere: inndata, hemmelig nøkkel og IV. IV brukes ikke i ECB-modus.

4.1. Inndata

Inndataene til AES kan være streng-, fil-, objekt- og passordbasert.

4.2. Hemmelig nøkkel

Det er to måter å generere en hemmelig nøkkel i AES: generere fra et tilfeldig tall eller stamme fra et gitt passord.

I den første tilnærmingen skal den hemmelige nøkkelen genereres fra en kryptografisk sikker (Pseudo-) tilfeldig nummergenerator som SecureRandom klasse.

For å generere en hemmelig nøkkel kan vi bruke KeyGenerator klasse. La oss definere en metode for å generere AES-nøkkelen med størrelsen på n (128, 192 og 256) biter:

offentlig statisk SecretKey generere Key (int n) kaster NoSuchAlgorithmException {KeyGenerator keyGenerator = KeyGenerator.getInstance ("AES"); keyGenerator.init (n); SecretKey key = keyGenerator.generateKey (); retur nøkkel; }

I den andre tilnærmingen kan den hemmelige AES-nøkkelen hentes fra et gitt passord ved hjelp av en passordbasert nøkkelavledningsfunksjon som PBKDF2. Vi trenger også en saltverdi for å gjøre et passord om til en hemmelig nøkkel. Saltet er også en tilfeldig verdi.

Vi kan bruke SecretKeyFactory klasse med PBKDF2WithHmacSHA256 algoritme for å generere en nøkkel fra et gitt passord.

La oss definere en metode for å generere AES-nøkkelen fra et gitt passord med 65 536 iterasjoner og en nøkkelengde på 256 bits:

offentlig statisk SecretKey getKeyFromPassword (strengpassord, strengesalt) kaster NoSuchAlgorithmException, InvalidKeySpecException {SecretKeyFactory factory = SecretKeyFactory.getInstance ("PBKDF2WithHmacSHA256"); KeySpec spec = ny PBEKeySpec (password.toCharArray (), salt.getBytes (), 65536, 256); SecretKey secret = new SecretKeySpec (factory.generateSecret (spec) .getEncoded (), "AES"); tilbake hemmelighet; }

4.3. Initialiseringsvektor (IV)

IV er en pseudo-tilfeldig verdi og har samme størrelse som blokken som er kryptert. Vi kan bruke SecureRandom klasse for å generere en tilfeldig IV.

La oss definere en metode for å generere en IV:

offentlig statisk IvParameterSpec generereIv () {byte [] iv = ny byte [16]; nye SecureRandom (). nextBytes (iv); returner nye IvParameterSpec (iv); }

5. Kryptering og dekryptering

5.1. String

For å implementere kryptering av inngangsstrenger, må vi først generere den hemmelige nøkkelen og IV i henhold til forrige avsnitt. Som neste trinn oppretter vi en forekomst fra Kryptering klasse ved å bruke getInstance () metode.

I tillegg konfigurerer vi en krypteringsforekomst med i det() metode med en hemmelig nøkkel, IV og krypteringsmodus. Til slutt krypterer vi inngangsstrengen ved å påkalle doFinal () metode. Denne metoden får byte av inndata og returnerer krypteringstekst i byte:

offentlig statisk strengkryptering (strengalgoritme, strenginngang, SecretKey-nøkkel, IvParameterSpec iv) kaster NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockScipherSmall cipher.init (Cipher.ENCRYPT_MODE, nøkkel, iv); byte [] cipherText = cipher.doFinal (input.getBytes ()); returner Base64.getEncoder () .encodeToString (cipherText); }

For dekryptering av en inngangsstreng kan vi initialisere krypteringen vår med DECRYPT_MODE å dekryptere innholdet:

offentlig statisk String dekryptere (String algoritme, String cipherText, SecretKey nøkkel, IvParameterSpec iv) kaster NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, Illegal cipher.init (Cipher.DECRYPT_MODE, nøkkel, iv); byte [] plainText = cipher.doFinal (Base64.getDecoder () .decode (cipherText)); returner ny streng (vanlig tekst); }

La oss skrive en testmetode for kryptering og dekryptering av en strenginngang:

@Test ugyldig givenString_whenEncrypt_thenSuccess () kaster NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException {String input = " SecretKey-nøkkel = AESUtil.generateKey (128); IvParameterSpec ivParameterSpec = AESUtil.generateIv (); Strengalgoritme = "AES / CBC / PKCS5Padding"; Streng cipherText = AESUtil.encrypt (algoritme, inngang, nøkkel, ivParameterSpec); Streng vanlig tekst = AESUtil.decrypt (algoritme, cipherText, nøkkel, ivParameterSpec); Assertions.assertEquals (input, plainText); }

5.2. Fil

La oss nå kryptere en fil ved hjelp av AES-algoritmen. Trinnene er de samme, men vi trenger noen IO klasser for å jobbe med filene. La oss kryptere en tekstfil:

public static void encryptFile (Strengalgoritme, SecretKey-nøkkel, IvParameterSpec iv, File inputFile, File outputFile) kaster IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadegangException cipher.init (Cipher.ENCRYPT_MODE, nøkkel, iv); FileInputStream inputStream = ny FileInputStream (inputFile); FileOutputStream outputStream = ny FileOutputStream (outputFile); byte [] buffer = ny byte [64]; int bytesLes; mens ((bytesRead = inputStream.read (buffer))! = -1) {byte [] output = cipher.update (buffer, 0, bytesRead); hvis (output! = null) {outputStream.write (output); }} byte [] outputBytes = cipher.doFinal (); hvis (outputBytes! = null) {outputStream.write (outputBytes); } inputStream.close (); outputStream.close (); }

Vær oppmerksom på at det ikke anbefales å prøve å lese hele filen - spesielt hvis den er stor - i minnet. I stedet krypterer vi en buffer om gangen.

For å dekryptere en fil bruker vi lignende trinn og initialiserer krypteringen vår ved hjelp av DECRYPT_MODE som vi så før.

Igjen, la oss definere en testmetode for kryptering og dekryptering av en tekstfil. I denne metoden leser vi baeldung.txt filen fra testressurskatalogen, krypter den til en fil som heter baeldung.kryptert, og dekrypter deretter filen til en ny fil:

@Test ugyldig givenFile_whenEncrypt_thenSuccess () kaster NoSuchAlgorithmException, IOException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException; InvalidAlgorithmParameterException, NoSuchPaddingException; Strengalgoritme = "AES / CBC / PKCS5Padding"; IvParameterSpec ivParameterSpec = AESUtil.generateIv (); Ressursressurs = ny ClassPathResource ("inputFile / baeldung.txt"); File inputFile = resource.getFile (); File encryptedFile = new File ("classpath: baeldung.encrypted"); File decryptedFile = new File ("document.decrypted"); AESUtil.encryptFile (algoritme, nøkkel, ivParameterSpec, inputFile, encryptedFile); AESUtil.decryptFile (algoritme, nøkkel, ivParameterSpec, encryptedFile, decryptedFile); assertThat (inputFile) .hasSameTextualContentAs (decryptedFile); }

5.3. Passordbasert

Vi kan gjøre AES-kryptering og dekryptering ved hjelp av den hemmelige nøkkelen som er hentet fra et gitt passord.

For å generere en hemmelig nøkkel bruker vi getKeyFromPassword () metode. Krypterings- og dekrypteringstrinnene er de samme som de som vises i seksjonen for strenginndata. Vi kan deretter bruke den instantierte krypteringen og den medfølgende hemmelige nøkkelen til å utføre krypteringen.

La oss skrive en testmetode:

@Test ugyldig givenPassword_whenEncrypt_thenSuccess () kaster InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException; NoStastingExceptioning Strengpassord = "baeldung"; Strengsalt = "12345678"; IvParameterSpec ivParameterSpec = AESUtil.generateIv (); SecretKey-nøkkel = AESUtil.getKeyFromPassword (passord, salt); Streng cipherText = AESUtil.encryptPasswordBased (vanlig tekst, nøkkel, ivParameterSpec); String decryptedCipherText = AESUtil.decryptPasswordBased (cipherText, key, ivParameterSpec); Assertions.assertEquals (plainText, decryptedCipherText); }

5.4. Gjenstand

For å kryptere et Java-objekt, må vi bruke Forseglet objekt klasse. Objektet skal være Serialiserbar. La oss begynne med å definere en Student klasse:

offentlig klasse Studentredskaper Serialiserbar {privat strengnavn; privat alder; // standard setters og getters} 

La oss deretter kryptere Student objekt:

offentlig statisk SealedObject encryptObject (strengalgoritme, serielliserbar gjenstand, SecretKey-nøkkel, IvParameterSpec iv) kaster NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOExceptionSignal cipher.init (Cipher.ENCRYPT_MODE, nøkkel, iv); SealedObject sealedObject = nytt SealedObject (objekt, kryptering); retur forsegletObjekt; }

Det krypterte objektet kan senere dekrypteres ved hjelp av riktig kryptering:

offentlig statisk Serializable decryptObject (strengalgoritme, SealedObject forsegletObjekt, SecretKey-nøkkel, IvParameterSpec iv) kaster NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, Codesception, CipherException cipher.init (Cipher.DECRYPT_MODE, nøkkel, iv); Serializable unsealObject = (Serializable) forsegletObject.getObject (kryptering); returner unsealObject; }

La oss skrive en prøvesak:

@Test ugyldig givenObject_whenEncrypt_thenSuccess () kaster NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, BadPaddingException, NoSuchPaddingException, IOException, BadPaddingException; SecretKey-nøkkel = AESUtil.generateKey (128); IvParameterSpec ivParameterSpec = AESUtil.generateIv (); Strengalgoritme = "AES / CBC / PKCS5Padding"; SealedObject sealedObject = AESUtil.encryptObject (algoritme, student, nøkkel, ivParameterSpec); Studentobjekt = (Student) AESUtil.decryptObject (algoritme, forseglet objekt, nøkkel, ivParameterSpec); assertThat (student) .isEqualToComparingFieldByField (object); }

6. Konklusjon

Oppsummert har vi lært hvordan vi krypterer og dekrypterer inngangsdata som strenger, filer, objekter og passordbaserte data ved hjelp av AES-algoritmen i Java. I tillegg har vi diskutert AES-variasjonene og størrelsen på data etter kryptering.

Som alltid er hele kildekoden til artikkelen tilgjengelig på GitHub.

Java bunn

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

$config[zx-auto] not found$config[zx-overlay] not found