Java AES i przy użyciu własnego klucza

88

Chcę zaszyfrować ciąg przy użyciu AES z własnym kluczem. Ale mam problem z długością klucza. Czy możesz przejrzeć mój kod i zobaczyć, co muszę poprawić / zmienić.

public static void main(String[] args) throws Exception {
    String username = "[email protected]";
    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));
}

W tej chwili pojawia się wyjątek „ Nieprawidłowa długość klucza AES: 86 bajtów ”. Czy muszę wprowadzić swój klucz? Jak mam to zrobić?

Czy muszę też cokolwiek ustawiać dla EBC lub CBC?

Dzięki

Bernie Perez
źródło
6
Uważam, że twój brak przypadkowej soli przeszkadza . A teraz poważnie: w kontekście kryptografii SALT powinna być losowa
João Portela
16
Haha zabawne. Właściwie mam przypadkową sól, ale wyczyściłem kod, aby moje pytanie było bardziej jasne. Dlatego zmienna nazywa się SALT2. Ale dobre odniesienie dla innych, którzy napotykają ten sam problem i lubią kopiować / wklejać kod.
Bernie Perez

Odpowiedzi:

125

Edytować:

Jak napisano w komentarzach, stary kod nie jest „najlepszą praktyką”. Należy użyć algorytmu generowania kluczy, takiego jak PBKDF2, z dużą liczbą iteracji. Powinieneś także użyć przynajmniej częściowo niestatycznej (to znaczy dla każdej „wyłącznie tożsamości”) soli. Jeśli to możliwe, generowane losowo i przechowywane razem z szyfrogramem.

    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);

===========

Stara odpowiedź

Powinieneś użyć SHA-1, aby wygenerować skrót z klucza i przyciąć wynik do 128 bitów (16 bajtów).

Dodatkowo nie generuj tablic bajtowych ze Strings przez getBytes () , używa domyślnego zestawu znaków platformy. Zatem hasło „blaöä” skutkuje inną tablicą bajtów na różnych platformach.

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");

Edycja: jeśli potrzebujesz klucza o rozmiarze 256 bitów, musisz pobrać łącze do pobierania Oracle „Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files” , użyj SHA-256 jako skrótu i ​​usuń wiersz Arrays.copyOf . „ECB” to domyślny tryb szyfrowania, a „PKCS5Padding” to domyślne wypełnienie. Możesz użyć różnych trybów szyfrowania i trybów dopełnienia za pomocą ciągu Cipher.getInstance przy użyciu następującego formatu: „Cipher / Mode / Padding”

W przypadku AES używających CTS i PKCS5Padding ciąg to: „AES / CTS / PKCS5Padding”

mknjc
źródło
To zadziała, ale zaszyfruje moje hasło, a następnie użyje tylko pierwszych kilku bitów. Nie ma lepszego sposobu, aby to zrobić?
Bernie Perez
4
Nie ma lepszego sposobu na wygenerowanie klucza, ponieważ AES potrzebuje klucza 128/192/256 bitów. Jeśli nie zhaszujesz swojego klucza i ograniczysz tylko dane wejściowe, użyje tylko pierwszych 16/24/32 bajtów. Zatem generowanie skrótu jest jedynym rozsądnym sposobem.
mknjc
13
Zauważ, że ta odpowiedź nie wykorzystuje dobrej funkcji wyprowadzania klucza i dlatego nie jest tak bezpieczna, jak powinna . Zobacz drugą odpowiedź na nieco przestarzałą funkcję wyprowadzania klucza - i niestety nadal jest to statyczna sól.
Maarten Bodewes
2
Mogę zasugerować usunięcie tej odpowiedzi, ponieważ jest to wyjątkowo zła praktyka. Należy zastosować odpowiednią funkcję wyprowadzania klucza - przynajmniej PBKDF2.
Boris the Spider
1
Tak, odpowiedź jest bardzo zła, jak powiedział przed laty Maarten. Proszę sprawdzić tę odpowiedź z Cryptography and Key Derivation Function
kelalaka
14

Do wygenerowania klucza należy użyć KeyGeneratora,

Klucze AES mają długość 128, 192 i 256 bitów, w zależności od szyfru, którego chcesz użyć.

Spójrz na samouczek tutaj

Oto kod do szyfrowania opartego na haśle, jest to hasło wprowadzane przez System. Możesz to zmienić, aby użyć zapisanego hasła, jeśli chcesz.

        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);
Keibosh
źródło
3
Jak wygenerować klucz z hasłem za pomocą KeyGenerator? Chcę wygenerować ten sam klucz na podstawie hasła. Więc mogę później odszyfrować ciąg.
Bernie Perez
To, o czym mówisz, to szyfrowanie oparte na haśle, a nie AES. Zaktualizowałem swoją odpowiedź przykładowym programem dla PBE
Keibosh
5
Spróbuj zamiast tego użyć generatora kluczy PBEKDF2, używając ciągu znaków „PBKDF2WithHmacSHA1”, aby SecretKeyFactoryuzyskać bardziej aktualne szyfrowanie.
Maarten Bodewes
12
Właściwie wszystkie użyte prymitywy kryptograficzne w tej odpowiedzi są nieaktualne , na pewno MD5 i DES. Uważajcie.
Maarten Bodewes
MD5 i DES to słabe zestawy szyfrów i należy ich UNIKAĆ
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);
    }
}
Shankar Murthy
źródło
5
Może dodaj więcej tekstu wyjaśniającego.
ChrisG,
Pytanie, jaki jest sens posiadania keyValuetablicy bajtów? Widzę, że jest używany do tworzenia klucza, dlaczego? Czy można coś zrobić, używając SecretKeyzamiast tego? Jeśli tak to jak?
Austin
@Mandrek, zawartość pliku „plaintext.txt” zostanie zaszyfrowana. Powyższa logika szyfruje dane / komunikat w pliku, który jest odczytywany jako argument w konstruktorze FileReader.
Shankar Murthy
2

To zadziała.

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;
      }
 }
Taran
źródło
2

MD5, AES, bez wypełnienia

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);
        }
    }
}
Mikrofon
źródło
Jak stworzyć bezpieczny klucz, taki jak SecretKeySpec w kątowym (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();
sonnykwe
źródło