Java AES e utilizzando la mia chiave


88

Voglio crittografare una stringa usando AES con la mia chiave. Ma ho problemi con la lunghezza in bit della chiave. Puoi rivedere il mio codice e vedere cosa devo correggere / modificare.

public static void main(String[] args) throws Exception {
    String username = "bob@google.org";
    String password = "Password1";
    String secretID = "BlahBlahBlah";
    String SALT2 = "deliciously salty";

    // Get the Key
    byte[] key = (SALT2 + username + password).getBytes();
    System.out.println((SALT2 + username + password).getBytes().length);

    // Need to pad key for AES
    // TODO: Best way?

    // Generate the secret key specs.
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

    // Instantiate the cipher
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

    byte[] encrypted = cipher.doFinal((secrectID).getBytes());
    System.out.println("encrypted string: " + asHex(encrypted));

    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    byte[] original = cipher.doFinal(encrypted);
    String originalString = new String(original);
    System.out.println("Original string: " + originalString + "\nOriginal string (Hex): " + asHex(original));
}

In questo momento ricevo un'eccezione " Lunghezza chiave AES non valida: 86 byte ". Devo riempire la mia chiave? Come dovrei farlo?

Inoltre devo impostare qualcosa per ECB o CBC?

Grazie



16
Haha divertente. In realtà ho un sale casuale, ma ho ripulito il mio codice per rendere più chiara la mia domanda. Ecco perché la variabile si chiama SALT2. Ma un buon riferimento per gli altri che incontrano lo stesso problema e amano copiare / incollare il codice.
Bernie Perez

Risposte:


125

Modificare:

Come scritto nei commenti, il vecchio codice non è "best practice". È necessario utilizzare un algoritmo di generazione di chiavi come PBKDF2 con un numero elevato di iterazioni. Dovresti anche usare almeno in parte un sale non statico (che significa per ogni "identità" esclusiva). Se possibile generato in modo casuale e memorizzato insieme al testo cifrato.

    SecureRandom sr = SecureRandom.getInstanceStrong();
    byte[] salt = new byte[16];
    sr.nextBytes(salt);

    PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 128 * 8);
    SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec);
    Cipher aes = Cipher.getInstance("AES");
    aes.init(Cipher.ENCRYPT_MODE, key);

===========

Vecchia risposta

Dovresti usare SHA-1 per generare un hash dalla tua chiave e tagliare il risultato a 128 bit (16 byte).

Inoltre, non generare array di byte da stringhe tramite getBytes () , utilizza il set di caratteri predefinito della piattaforma. Quindi la password "blaöä" risulta in un array di byte diverso su piattaforme diverse.

byte[] key = (SALT2 + username + password).getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit

SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

Modifica: se hai bisogno di 256 bit come dimensioni della chiave, devi scaricare il link per il download Oracle "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" , utilizzare SHA-256 come hash e rimuovere la riga Arrays.copyOf . "ECB" è la modalità di crittografia predefinita e "PKCS5Padding" è la spaziatura interna predefinita. È possibile utilizzare diverse modalità di cifratura e modalità di riempimento tramite Cipher.getInstance stringa utilizzando il seguente formato: "Cipher / Mode / Padding"

Per AES che utilizza CTS e PKCS5Padding la stringa è: "AES / CTS / PKCS5Padding"


Funzionerà, ma ha hashing la mia password, quindi utilizza solo i primi bit. Non c'è modo migliore per farlo?
Bernie Perez

4
Non c'è modo migliore per generare la chiave perché AES necessita di una chiave da 128/192/256 bit. Se non si esegue l'hashing della chiave e si taglia solo l'input, verranno utilizzati solo i primi 16/24/32 byte. Quindi generare un hash è l'unico modo ragionevole.
mknjc

13
Nota che questa risposta non utilizza una buona funzione di derivazione della chiave e quindi non è sicura come dovrebbe essere . Vedi l' altra risposta per una funzione di derivazione della chiave leggermente obsoleta e purtroppo ancora un sale statico.
Maarten Bodewes

2
Potrei suggerire di eliminare questa risposta in quanto è una pratica estremamente cattiva. Deve essere utilizzata una funzione di derivazione della chiave appropriata, almeno PBKDF2.
Boris the Spider

1
Sì, la risposta è pessima, come disse Maarten anni fa. Si prega di controllare questa risposta da Cryptography and Key Derivation Function
kelalaka

14

Dovresti usare un KeyGenerator per generare la chiave,

Le lunghezze delle chiavi AES sono 128, 192 e 256 bit a seconda del codice che si desidera utilizzare.

Dai un'occhiata al tutorial qui

Ecco il codice per la crittografia basata su password, questa ha la password inserita tramite System.in puoi cambiarla per utilizzare una password memorizzata se lo desideri.

        PBEKeySpec pbeKeySpec;
        PBEParameterSpec pbeParamSpec;
        SecretKeyFactory keyFac;

        // Salt
        byte[] salt = {
            (byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
            (byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
        };

        // Iteration count
        int count = 20;

        // Create PBE parameter set
        pbeParamSpec = new PBEParameterSpec(salt, count);

        // Prompt user for encryption password.
        // Collect user password as char array (using the
        // "readPassword" method from above), and convert
        // it into a SecretKey object, using a PBE key
        // factory.
        System.out.print("Enter encryption password:  ");
        System.out.flush();
        pbeKeySpec = new PBEKeySpec(readPassword(System.in));
        keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

        // Create PBE Cipher
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");

        // Initialize PBE Cipher with key and parameters
        pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

        // Our cleartext
        byte[] cleartext = "This is another example".getBytes();

        // Encrypt the cleartext
        byte[] ciphertext = pbeCipher.doFinal(cleartext);

3
Come posso generare la mia chiave con la password utilizzando KeyGenerator? Voglio generare la stessa chiave in base alla password. Quindi posso decrittografare la stringa più tardi.
Bernie Perez

Ciò di cui parli è la crittografia basata su password, non AES. Ho aggiornato la mia risposta con il programma di esempio per PBE
Keibosh

5
Prova a utilizzare invece il generatore di chiavi PBEKDF2, utilizzando la stringa "PBKDF2WithHmacSHA1" per SecretKeyFactoryuna crittografia più aggiornata.
Maarten Bodewes

12
In realtà tutte le primitive crittografiche utilizzate in questa risposta sono obsolete , sicuramente MD5 e DES. Badate.
Maarten Bodewes

MD5 e DES sono suite di cifratura deboli e dovrebbero essere EVITATI
atom88

6
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;
import java.io.BufferedReader;
import java.io.FileReader;

public class AESFile 
{
private static String algorithm = "AES";
private static byte[] keyValue=new byte[] {'0','2','3','4','5','6','7','8','9','1','2','3','4','5','6','7'};// your key

    // Performs Encryption
    public static String encrypt(String plainText) throws Exception 
    {
            Key key = generateKey();
            Cipher chiper = Cipher.getInstance(algorithm);
            chiper.init(Cipher.ENCRYPT_MODE, key);
            byte[] encVal = chiper.doFinal(plainText.getBytes());
            String encryptedValue = new BASE64Encoder().encode(encVal);
            return encryptedValue;
    }

    // Performs decryption
    public static String decrypt(String encryptedText) throws Exception 
    {
            // generate key 
            Key key = generateKey();
            Cipher chiper = Cipher.getInstance(algorithm);
            chiper.init(Cipher.DECRYPT_MODE, key);
            byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedText);
            byte[] decValue = chiper.doFinal(decordedValue);
            String decryptedValue = new String(decValue);
            return decryptedValue;
    }

//generateKey() is used to generate a secret key for AES algorithm
    private static Key generateKey() throws Exception 
    {
            Key key = new SecretKeySpec(keyValue, algorithm);
            return key;
    }

    // performs encryption & decryption 
    public static void main(String[] args) throws Exception 
    {
        FileReader file = new FileReader("C://myprograms//plaintext.txt");
        BufferedReader reader = new BufferedReader(file);
        String text = "";
        String line = reader.readLine();
    while(line!= null)
        {
            text += line;
    line = reader.readLine();
        }
        reader.close();
    System.out.println(text);

            String plainText = text;
            String encryptedText = AESFile.encrypt(plainText);
            String decryptedText = AESFile.decrypt(encryptedText);

            System.out.println("Plain Text : " + plainText);
            System.out.println("Encrypted Text : " + encryptedText);
            System.out.println("Decrypted Text : " + decryptedText);
    }
}

5
Magari aggiungi un altro testo di spiegazione.
ChrisG

Domanda, qual è il punto di avere keyValue, con l'array di byte? Vedo che viene utilizzato per creare la chiave, perché? Si può fare qualcosa usando SecretKeyinvece come ? Se é cosi, come?
Austin

@Mandrek, il contenuto del file "plaintext.txt" verrà crittografato. La logica precedente crittografa i dati / messaggio nel file che viene letto come argomento nel costruttore di FileReader.
Shankar Murthy

2

Questo funzionerà.

public class CryptoUtils {

    private  final String TRANSFORMATION = "AES";
    private  final String encodekey = "1234543444555666";
    public  String encrypt(String inputFile)
            throws CryptoException {
        return doEncrypt(encodekey, inputFile);
    }


    public  String decrypt(String input)
            throws CryptoException {
    // return  doCrypto(Cipher.DECRYPT_MODE, key, inputFile);
    return doDecrypt(encodekey,input);
    }

    private  String doEncrypt(String encodekey, String inputStr)   throws CryptoException {
        try {

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);

            byte[] key = encodekey.getBytes("UTF-8");
            MessageDigest sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16); // use only first 128 bit

            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

            byte[] inputBytes = inputStr.getBytes();     
            byte[] outputBytes = cipher.doFinal(inputBytes);

            return Base64Utils.encodeToString(outputBytes);

        } catch (NoSuchPaddingException | NoSuchAlgorithmException
                | InvalidKeyException | BadPaddingException
                | IllegalBlockSizeException | IOException ex) {
            throw new CryptoException("Error encrypting/decrypting file", ex);
       }
     }


    public  String doDecrypt(String encodekey,String encrptedStr) { 
          try {     

              Cipher dcipher = Cipher.getInstance(TRANSFORMATION);
              dcipher = Cipher.getInstance("AES");
              byte[] key = encodekey.getBytes("UTF-8");
              MessageDigest sha = MessageDigest.getInstance("SHA-1");
              key = sha.digest(key);
              key = Arrays.copyOf(key, 16); // use only first 128 bit

              SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

              dcipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            // decode with base64 to get bytes

              byte[] dec = Base64Utils.decode(encrptedStr.getBytes());  
              byte[] utf8 = dcipher.doFinal(dec);

              // create new string based on the specified charset
              return new String(utf8, "UTF8");

          } catch (Exception e) {

            e.printStackTrace();

          }
      return null;
      }
 }

2

MD5, AES, senza imbottitura

import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.apache.commons.io.Charsets.UTF_8;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class PasswordUtils {

    private PasswordUtils() {}

    public static String encrypt(String text, String pass) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(ENCRYPT_MODE, key);

            byte[] encrypted = cipher.doFinal(text.getBytes(UTF_8));
            byte[] encoded = Base64.getEncoder().encode(encrypted);
            return new String(encoded, UTF_8);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot encrypt", e);
        }
    }

    public static String decrypt(String text, String pass) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(DECRYPT_MODE, key);

            byte[] decoded = Base64.getDecoder().decode(text.getBytes(UTF_8));
            byte[] decrypted = cipher.doFinal(decoded);
            return new String(decrypted, UTF_8);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot decrypt", e);
        }
    }
}

Come creare chiavi sicure come SecretKeySpec in angular (ionic 4);
Nitin Karale

0
    byte[] seed = (SALT2 + username + password).getBytes();
    SecureRandom random = new SecureRandom(seed);
    KeyGenerator generator;
    generator = KeyGenerator.getInstance("AES");
    generator.init(random);
    generator.init(256);
    Key keyObj = generator.generateKey();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.