Salta el contingut
 

AES – Encriptació simètrica

Autor: Joan Puigcerver Ibáñez

Correu electrònic: j.puigcerveribanez@edu.gva.es

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

AES – Encriptació simètrica

AES (Advanced Encryption Standard) és un algorisme de xifratge de clau simètrica que s'utilitza per xifrar i desxifrar informació sensible. Funciona prenent text pla com a entrada i aplicant una sèrie de transformacions matemàtiques, o "rounds", mitjançant una clau secreta. El resultat és un text xifrat, que és il·legible sense la clau.

El nombre de rondes i les operacions matemàtiques específiques utilitzades depenen de la mida de la clau; AES pot utilitzar claus de 128 bits, 192 bits o 256 bits.

L'objectiu d'AES és protegir la informació sensible, com ara números de targeta de crèdit i credencials d'inici de sessió, de l'accés no autoritzat. S'utilitza àmpliament en una varietat d'aplicacions, com ara compres en línia, banca en línia i comunicacions segures.

Generar claus

Generar claus aleatòriament

En Java podem generar una SecretKey utilitzant la classe KeyGenerator, indicant l'algorisme que volem utilitzar.

public static SecretKey keygenKeyGeneration(int keySize) {
    SecretKey sKey = null;
    if ((keySize == 128)||(keySize == 192)||(keySize == 256)) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(keySize);
            sKey = kgen.generateKey();
        } catch (NoSuchAlgorithmException ex) {
            System.err.println("Generador no disponible.");
        }
    }
    return sKey;
}

També es pot utilitzar l'eina OpenSSL per generar la clau AES:

openssl genpkey -algorithm AES-128-CBC -out key.pem

Generar claus a partir d'una contrasenya

El següent mètode genera una SecretKey a partir del hash SHA-256 d'una contrasenya.

public static SecretKey passwordKeyGeneration(String password, int keySize) {
    SecretKey sKey = null;
    if ((keySize == 128)||(keySize == 192)||(keySize == 256)) {
        try {
            byte[] data = password.getBytes(StandardCharsets.UTF_8);
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] hash = md.digest(data);
            byte[] key = Arrays.copyOf(hash, keySize/8);
            sKey = new SecretKeySpec(key, "AES");
        } catch (Exception ex) {
            System.err.println("Error generant la clau:" + ex);
        }
    }
    return sKey;
}

Operacions amb AES

L'algorisme AES serveix per xifrar i desxifrar dades.

Aquestes operacions es poden dur a terme a Java mitjançant la classe Cipher.

En el següent mètode, s'utilitza la classe Chiper per portar a terme l'acció d'encriptar o desencriptar, indicada mitjançant el paràmetre int opmode. Aquest paràmetre pot rebre els valors Cipher.ENCRYPT_MODE o Cipher.DECRYPT_MODE.

private static byte[] aes(SecretKey key, byte[] data, int opmode) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS4Padding");
    cipher.init(opmode, key);
    return cipher.doFinal(data);
}

Encriptar

Per encriptar dades en format byte[], podem utilitzar el mètode aes amb l'opció Cipher.ENCRYPT_MODE.

Podem crear un mètode "wrapper", que rebrà la clau i xifrarà les dades proporcionades:

public static byte[] encrypt(SecretKey key, byte[] data) throws Exception {
    return aes(key, data, Cipher.ENCRYPT_MODE);
}

Si volem encriptar dades de tipus String, primer caldrà convertir-les a byte[].

Podem crear un altre mètode "wrapper" per fer-ho:

public static String encrypt(SecretKey key, String str){
    try {
        // Decode the UTF-8 String into byte[] and encrypt it
        byte[] data = encrypt(key, str.getBytes(StandardCharsets.UTF_8));
        // Encode the encrypted data into base64
        return Base64.getEncoder().encodeToString(data);
    } catch (Exception ex){
        System.err.println("Error xifrant les dades: " + ex);
    }
    return null;
}

Desencriptar

Per desencriptar dades en format byte[], podem utilitzar el mètode aes amb l'opció Cipher.DECRYPT_MODE.

Podem crear un mètode "wrapper", que rebrà la clau privada del destinatari que desxifrarà les dades proporcionades:

public static byte[] decrypt(SecretKey key, byte[] data) throws Exception {
    return aes(key, data, Cipher.DECRYPT_MODE);
}

Si volem desencriptar dades de tipus String, primer caldrà convertir-les a byte[].

Podem crear un altre mètode "wrapper" per fer-ho:

public static String decrypt(SecretKey key, String str){
    try {
        // Decode the base64 encrypted string to a byte[]
        byte[] data = Base64.getDecoder().decode(str);
        // Decrypyt the byte[] data
        byte[] decrypted = decrypt(key, data);
        // Encode the decrypted data in a String
        return new String(decrypted);
    } catch (Exception ex){
        System.err.println("Error desxifrant les dades: " + ex);
    }
    return null;
}

Claus en fitxers

Guardar una clau

Per guardar una clau privada en un fitxer, sols cal codificar-la en base64 i guarda-la en el fitxer.

public static void saveSecretKeyToFile(SecretKey key, String path) throws IOException {
    // Base64 encode the key
    String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded());

    // Write the key to a file
    Files.write(Paths.get(path), encodedKey.getBytes());
}

Carregar una clau

Per carregar una clau privada des d'un fitxer, sols cal llegir els continguts i crear l'objecte SecretKey.

public static SecretKey loadSecretKeyFromFile(String path) throws IOException {
    // Read the key from file
    String fileContent = new String(Files.readAllBytes(Paths.get(path)));
    // Decode the base64 key into byte[]
    byte[] keyBytes = Base64.getDecoder().decode(fileContent);
    // Create the SecretKey object
    return new SecretKeySpec(keyBytes, "AES");
}

Codi font

Tot el codi font anteriorment proporcionat s'ha compilat en la següent llibreria:

AES.java
package ud4.examples;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;

public class AES {

    public static SecretKey keygenKeyGeneration(int keySize) {
        SecretKey sKey = null;
        if ((keySize == 128)||(keySize == 192)||(keySize == 256)) {
            try {
                KeyGenerator kgen = KeyGenerator.getInstance("AES");
                kgen.init(keySize);
                sKey = kgen.generateKey();

            } catch (NoSuchAlgorithmException ex) {
                System.err.println("Generador no disponible.");
            }
        }
        return sKey;
    }

    public static SecretKey passwordKeyGeneration(String password, int keySize) {
        SecretKey sKey = null;
        if ((keySize == 128)||(keySize == 192)||(keySize == 256)) {
            try {
                byte[] data = password.getBytes(StandardCharsets.UTF_8);
                MessageDigest md = MessageDigest.getInstance("SHA-256");
                byte[] hash = md.digest(data);
                byte[] key = Arrays.copyOf(hash, keySize/8);
                sKey = new SecretKeySpec(key, "AES");
            } catch (Exception ex) {
                System.err.println("Error generant la clau:" + ex);
            }
        }
        return sKey;
    }

    public static String encrypt(SecretKey key, String str) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        // Decode the UTF-8 String into byte[] and encrypt it
        byte[] data = encrypt(key, str.getBytes(StandardCharsets.UTF_8));
        // Encode the encrypted data into base64
        return Base64.getEncoder().encodeToString(data);
    }
    public static String decrypt(SecretKey key, String str) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        // Decode the base64 encrypted string to a byte[]
        byte[] data = Base64.getDecoder().decode(str);
        // Decrypyt the byte[] data
        byte[] decrypted = decrypt(key, data);
        // Encode the decrypted data in a String
        return new String(decrypted);
    }

    public static byte[] encrypt(SecretKey key, byte[] data) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        return aes(key, data, Cipher.ENCRYPT_MODE);
    }
    public static byte[] decrypt(SecretKey key, byte[] data) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        return aes(key, data, Cipher.DECRYPT_MODE);
    }
    private static byte[] aes(SecretKey key, byte[] data, int opmode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(opmode, key);
        return cipher.doFinal(data);
    }

    public static void saveSecretKeyToFile(SecretKey key, String path) throws IOException {
        // Base64 encode the key
        String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded());

        // Write the key to a file
        Files.write(Paths.get(path), encodedKey.getBytes());
    }

    public static SecretKey loadSecretKeyFromFile(String path) throws IOException {
        // Read the key from file
        String fileContent = new String(Files.readAllBytes(Paths.get(path)));
        // Decode the base64 key into byte[]
        byte[] keyBytes = Base64.getDecoder().decode(fileContent);
        // Create the SecretKey object
        return new SecretKeySpec(keyBytes, "AES");
    }



    public static void main(String[] args) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        String message = "Aquest és un missatge super secret";
        ArrayList<SecretKey> keys = new ArrayList<>();

        String password = "veryDifficultPassword";
        int keySize = 256;

        keys.add(passwordKeyGeneration(password, keySize));
        System.out.printf("Key generated with password: %s\n", password);

        keys.add(keygenKeyGeneration(keySize));

        for (SecretKey key : keys) {
            System.out.printf("Key: %s\n", Base64.getEncoder().encodeToString(key.getEncoded()));
            System.out.printf("Message: %s\n", message);
            String encrypted = encrypt(key, message);
            System.out.printf("Encrypted message: %s\n", encrypted);
            String decrypted = decrypt(key, encrypted);
            System.out.printf("Decrypted message: %s\n", decrypted);
            System.out.println();
        }
    }
}

Comentaris