Java AES-kryptering og dekryptering

Java Top

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN

1. Oversigt

Den symmetriske blok-chiffer spiller en vigtig rolle i datakryptering. Det betyder, at den samme nøgle bruges til både kryptering og dekryptering. Advanced Encryption Standard (AES) er en udbredt krypteringsalgoritme med symmetrisk nøgle.

I denne vejledning ser vi, hvordan du implementerer AES-kryptering og dekryptering ved hjælp af Java Cryptography Architecture (JCA) inden for JDK.

2. AES-algoritme

AES-algoritmen er en iterativ, blokeret kryptering af symmetrisk nøgle understøtter kryptografiske nøgler (hemmelige nøgler) på 128, 192 og 256 bit til at kryptere og dekryptere data i blokke på 128 bit. Nedenstående figur viser AES-algoritmen på højt niveau:

Hvis de data, der skal krypteres, ikke opfylder kravet om blokstørrelse på 128 bit, skal de polstres. Polstring er en proces til udfyldning af den sidste blok til 128 bit.

3. AES-variationer

AES-algoritmen har seks driftsformer:

  1. ECB (elektronisk kodebog)
  2. CBC (Cipher Block Chains)
  3. CFB (Cipher FeedBack)
  4. OFB (outputfeedback)
  5. CTR (tæller)
  6. GCM (Galois / Counter Mode)

Funktionsmåden kan anvendes for at styrke effekten af ​​krypteringsalgoritmen. Endvidere kan driftsformen konvertere blokcipher til en streamcipher. Hver tilstand har sin styrke og svaghed. Lad os få en hurtig gennemgang.

3.1. ECB

Denne driftsform er den enkleste af alle. Klarteksten er opdelt i blokke med en størrelse på 128 bit. Derefter krypteres hver blok med den samme nøgle og algoritme. Derfor producerer det samme resultat for den samme blok. Dette er den største svaghed ved denne tilstand og det anbefales ikke til kryptering. Det kræver polstringsdata.

3.2. CBC

For at overvinde ECB-svagheden bruger CBC-tilstand en initialiseringsvektor (IV) til at udvide krypteringen. For det første bruger CBC almindelig tekstblok xor med IV. Derefter krypterer det resultatet til ciphertext-blokken. I den næste blok bruger det krypteringsresultatet til at xorere med almindelig tekstblok indtil den sidste blok.

I denne tilstand kan kryptering ikke paralleliseres, men dekryptering kan paralleliseres. Det kræver også polstringsdata.

3.3. CFB

Denne tilstand kan bruges som streamcipher. Først krypterer den IV, så xoreres den med almindelig tekstblok for at få krypteringstekst. Derefter krypterer CFB krypteringsresultatet til x eller almindelig tekst. Det har brug for en IV.

I denne tilstand kan dekryptering paralleliseres, men kryptering kan ikke paralleliseres.

3.4. OFB

Denne tilstand kan bruges som streamcipher. For det første krypterer det IV. Derefter bruger det krypteringsresultaterne til at xeller almindelig tekst for at få krypteringstekst.

Det kræver ikke polstringsdata og påvirkes ikke af den støjende blok.

3.5. CTR

Denne tilstand bruger værdien af ​​en tæller som en IV. Det ligner meget OFB, men det bruger tælleren til at blive krypteret hver gang i stedet for IV.

Denne tilstand har to styrker, herunder kryptering / dekryptering parallelisering, og støj i en blok påvirker ikke andre blokke.

3.6. GCM

Denne tilstand er en udvidelse af CTR-tilstand. GCM har fået betydelig opmærksomhed og anbefales af NIST. GCM-modellen udsender krypteringstekst og et godkendelseskode. Den største fordel ved denne tilstand sammenlignet med andre driftsformer i algoritmen er dens effektivitet.

I denne vejledning bruger vi AES / CBC / PKCS5Padding algoritme, fordi den er meget brugt i mange projekter.

3.7. Datastørrelse efter kryptering

Som nævnt tidligere har AES en blokstørrelse på 128 bits eller 16 bytes. AES ændrer ikke størrelsen, og krypteringstekststørrelsen er lig med klartekststørrelsen. I ECB- og CBC-tilstande skal vi også bruge en polstringsalgoritme, der kan lide PKCS 5. Så størrelsen på data efter kryptering er:

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

For at gemme IV med ciphertext skal vi tilføje 16 ekstra byte.

4. AES-parametre

I AES-algoritmen har vi brug for tre parametre: inputdata, hemmelig nøgle og IV. IV bruges ikke i ECB-tilstand.

4.1. Indtastningsdata

Inputdataene til AES kan være streng-, fil-, objekt- og adgangskodebaseret.

4.2. Hemmelig nøgle

Der er to måder at generere en hemmelig nøgle i AES: generere fra et tilfældigt tal eller stamme fra en given adgangskode.

I den første tilgang skal den hemmelige nøgle genereres fra en kryptografisk sikker (Pseudo-) tilfældig talgenerator som SecureRandom klasse.

Til at generere en hemmelig nøgle kan vi bruge KeyGenerator klasse. Lad os definere en metode til generering af AES-nøglen med størrelsen på n (128, 192 og 256) bits:

offentlig statisk SecretKey createKey (int n) kaster NoSuchAlgorithmException {KeyGenerator keyGenerator = KeyGenerator.getInstance ("AES"); keyGenerator.init (n); SecretKey-nøgle = keyGenerator.generateKey (); returnøgle; }

I den anden tilgang kan den hemmelige AES-nøgle udledes fra en given adgangskode ved hjælp af en adgangskodebaseret nøglediveringsfunktion som PBKDF2. Vi har også brug for en saltværdi for at omdanne en adgangskode til en hemmelig nøgle. Saltet er også en tilfældig værdi.

Vi kan bruge SecretKeyFactory klasse med PBKDF2WithHmacSHA256 algoritme til generering af en nøgle fra en given adgangskode.

Lad os definere en metode til at generere AES-nøglen fra en given adgangskode med 65.536 iterationer og en nøgellængde på 256 bit:

offentlig statisk SecretKey getKeyFromPassword (strengadgangskode, strengesalt) kaster NoSuchAlgorithmException, InvalidKeySpecException {SecretKeyFactory fabrik = SecretKeyFactory.getInstance ("PBKDF2WithHmacSHA256"); KeySpec spec = ny PBEKeySpec (password.toCharArray (), salt.getBytes (), 65536, 256); SecretKey hemmelighed = ny SecretKeySpec (fabrik.generateSecret (spec) .getEncoded (), "AES"); returnere hemmelighed; }

4.3. Initialiseringsvektor (IV)

IV er en pseudo-tilfældig værdi og har samme størrelse som den blok, der er krypteret. Vi kan bruge SecureRandom klasse for at generere en tilfældig IV.

Lad os definere en metode til generering af en IV:

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

5. Kryptering og dekryptering

5.1. Snor

For at implementere inputstrengkryptering skal vi først generere den hemmelige nøgle og IV i henhold til det foregående afsnit. Som det næste trin opretter vi en instans fra Kryptering klasse ved hjælp af getInstance () metode.

Derudover konfigurerer vi en chifferinstans ved hjælp af i det() metode med en hemmelig nøgle, IV og krypteringstilstand. Endelig krypterer vi inputstrengen ved at påkalde doFinal () metode. Denne metode henter bytes af input og returnerer ciphertext i bytes:

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

Til dekryptering af en inputstreng kan vi initialisere vores chiffer ved hjælp af DECRYPT_MODE at dekryptere indholdet:

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

Lad os skrive en testmetode til kryptering og dekryptering af en strenginput:

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

5.2. Fil

Lad os nu kryptere en fil ved hjælp af AES-algoritmen. Trinene er de samme, men vi har brug for nogle IO klasser til at arbejde med filerne. Lad os kryptere en tekstfil:

offentlig statisk ugyldig encryptFile (strengalgoritme, SecretKey-nøgle, IvParameterSpec iv, File inputFile, File outputFile) kaster IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadegangException, Bad cipher.init (Cipher.ENCRYPT_MODE, nøgle, iv); FileInputStream inputStream = ny FileInputStream (inputFile); FileOutputStream outputStream = ny FileOutputStream (outputFile); byte [] buffer = ny byte [64]; int bytesLæs; 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 (); }

Bemærk, at det ikke anbefales at prøve at læse hele filen - især hvis den er stor - i hukommelsen. I stedet krypterer vi en buffer ad gangen.

Til dekryptering af en fil bruger vi lignende trin og initialiserer vores chiffer ved hjælp af DECRYPT_MODE som vi så før.

Lad os igen definere en testmetode til kryptering og dekryptering af en tekstfil. I denne metode læser vi baeldung.txt fil fra testressourcebiblioteket, krypter den til en fil, der hedder baeldung.krypteret, og dekrypter derefter 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 (); Ressource ressource = ny ClassPathResource ("inputFile / baeldung.txt"); File inputFile = resource.getFile (); Fil encryptedFile = ny fil ("classpath: baeldung.encrypted"); File decryptedFile = new File ("document.decrypted"); AESUtil.encryptFile (algoritme, nøgle, ivParameterSpec, inputFile, encryptedFile); AESUtil.decryptFile (algoritme, nøgle, ivParameterSpec, encryptedFile, decryptedFile); assertThat (inputFile) .hasSameTextualContentAs (decryptedFile); }

5.3. Adgangskodebaseret

Vi kan udføre AES-kryptering og dekryptering ved hjælp af den hemmelige nøgle, der er afledt af en given adgangskode.

Til at generere en hemmelig nøgle bruger vi getKeyFromPassword () metode. Krypterings- og dekrypteringstrin er de samme som dem, der vises i sektionen strenginput. Vi kan derefter bruge den instantierede chiffer og den medfølgende hemmelige nøgle til at udføre krypteringen.

Lad os skrive en testmetode:

@Test ugyldigt givenPassword_whenEncrypt_thenSuccess () kaster InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoStastning Strengadgangskode = "baeldung"; Strengsalt = "12345678"; IvParameterSpec ivParameterSpec = AESUtil.generateIv (); SecretKey-nøgle = AESUtil.getKeyFromPassword (adgangskode, salt); Streng cipherText = AESUtil.encryptPasswordBased (plainText, key, ivParameterSpec); String decryptedCipherText = AESUtil.decryptPasswordBased (cipherText, key, ivParameterSpec); Assertions.assertEquals (plainText, decryptedCipherText); }

5.4. Objekt

For at kryptere et Java-objekt skal vi bruge SealedObject klasse. Objektet skal være Serialiserbar. Lad os begynde med at definere en Studerende klasse:

offentlig klasse Elevimplementer Serialiserbar {privat strengnavn; privat int alder // standard settere og getters} 

Lad os derefter kryptere Studerende objekt:

offentlig statisk SealedObject encryptObject (strengalgoritme, Serialiserbart objekt, SecretKey-nøgle, IvParameterSpec iv) kaster NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOExceptionSignal cipher.init (Cipher.ENCRYPT_MODE, nøgle, iv); SealedObject sealedObject = nyt SealedObject (objekt, kryptering); returneret forsegletObjekt; }

Det krypterede objekt kan senere dekrypteres ved hjælp af den korrekte chiffer:

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

Lad os skrive en test case:

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

6. Konklusion

Sammenfattende har vi lært at kryptere og dekryptere inputdata som strenge, filer, objekter og adgangskodebaserede data ved hjælp af AES-algoritmen i Java. Derudover har vi diskuteret AES-variationerne og datastørrelsen efter kryptering.

Som altid er artiklens fulde kildekode tilgængelig på GitHub.

Java bund

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN