Java AES und mit meinem eigenen Schlüssel

88

Ich möchte eine Zeichenfolge mit AES mit meinem eigenen Schlüssel verschlüsseln. Aber ich habe Probleme mit der Bitlänge des Schlüssels. Können Sie meinen Code überprüfen und sehen, was ich reparieren / ändern muss?

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

Im Moment erhalte ich die Ausnahme " Ungültige AES-Schlüssellänge: 86 Byte ". Muss ich meinen Schlüssel auffüllen? Wie soll ich das machen

Muss ich auch etwas für EZB oder CBC einstellen?

Vielen Dank

Bernie Perez
quelle
6
Ich finde Ihren Mangel an zufälligem Salz störend . Jetzt im Ernst: Im Kontext der Kryptographie sollte SALZ zufällig sein
João Portela
16
Haha, lustig. Ich habe tatsächlich ein zufälliges Salz, aber ich habe meinen Code bereinigt, um meine Frage klarer zu machen. Deshalb heißt die Variable SALT2. Aber eine gute Referenz für andere, die auf dasselbe Problem stoßen und gerne Code kopieren / einfügen.
Bernie Perez

Antworten:

124

Bearbeiten:

Wie in den Kommentaren geschrieben, ist der alte Code keine "Best Practice". Sie sollten einen Schlüsselgenerierungsalgorithmus wie PBKDF2 mit einer hohen Iterationszahl verwenden. Sie sollten auch zumindest teilweise ein nicht statisches (dh für jedes exklusive "Identitäts" -Salz) Salz verwenden. Wenn möglich zufällig generiert und zusammen mit dem Chiffretext gespeichert.

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

===========

Alte Antwort

Sie sollten SHA-1 verwenden, um einen Hash aus Ihrem Schlüssel zu generieren und das Ergebnis auf 128 Bit (16 Byte) zu kürzen.

Generieren Sie außerdem keine Byte-Arrays aus Strings über getBytes (), es wird der Plattform-Standardzeichensatz verwendet. Das Passwort "blaöä" führt also zu unterschiedlichen Byte-Arrays auf unterschiedlichen Plattformen.

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

Bearbeiten: Wenn Sie 256-Bit-Schlüsselgrößen benötigen, müssen Sie den Oracle-Download-Link "JCE-Richtliniendateien mit unbegrenzter Gerichtsbarkeit für Java Cryptography Extension (JCE)" herunterladen . Verwenden Sie SHA-256 als Hash und entfernen Sie die Zeile Arrays.copyOf . "ECB" ist der Standard-Verschlüsselungsmodus und "PKCS5Padding" das Standard-Padding. Sie können verschiedene Verschlüsselungs- und Auffüllmodi über die Zeichenfolge Cipher.getInstance im folgenden Format verwenden: "Verschlüsselung / Modus / Auffüllen"

Für AES mit CTS und PKCS5Padding lautet die Zeichenfolge: "AES / CTS / PKCS5Padding"

mknjc
quelle
Dies wird funktionieren, aber es hat mein Passwort gehasht und dann nur die ersten paar Bits verwendet. Es gibt keinen besseren Weg, dies zu tun?
Bernie Perez
4
Es gibt keinen besseren Weg, um den Schlüssel zu generieren, da AES einen 128/192/256-Bit-Schlüssel benötigt. Wenn Sie Ihren Schlüssel nicht hashen und nur die Eingabe kürzen, werden nur die ersten 16/24/32 Bytes verwendet. Das Generieren eines Hash ist also der einzig vernünftige Weg.
mknjc
13
Beachten Sie, dass diese Antwort keine gute Schlüsselableitungsfunktion verwendet und daher nicht so sicher ist, wie sie sein sollte . In der anderen Antwort finden Sie eine leicht veraltete Funktion zur Schlüsselableitung - und leider immer noch ein statisches Salz.
Maarten Bodewes
2
Könnte ich vorschlagen, diese Antwort zu löschen, da dies eine äußerst schlechte Praxis ist. Eine geeignete Schlüsselableitungsfunktion sollte verwendet werden - mindestens PBKDF2.
Boris die Spinne
1
Ja, die Antwort ist sehr schlecht, wie Maarten vor Jahren sagte. Bitte überprüfen Sie diese Antwort von Kryptographie und Schlüsselableitungsfunktion
Kelalaka
14

Sie sollten einen KeyGenerator verwenden, um den Schlüssel zu generieren.

Die AES-Schlüssellängen betragen je nach verwendeter Verschlüsselung 128, 192 und 256 Bit.

Schauen Sie sich das Tutorial hier an

Hier ist der Code für die kennwortbasierte Verschlüsselung. Hierbei wird das Kennwort über System eingegeben. Sie können dies ändern, um ein gespeichertes Kennwort zu verwenden, wenn Sie möchten.

        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
quelle
3
Wie generiere ich meinen Schlüssel mit dem Passwort mit dem KeyGenerator? Ich möchte den gleichen Schlüssel basierend auf dem Passwort generieren. So kann ich die Zeichenfolge später entschlüsseln.
Bernie Perez
Sie sprechen von passwortbasierter Verschlüsselung, nicht von AES. Ich habe meine Antwort mit dem Beispielprogramm für PBE
Keibosh
5
Versuchen Sie stattdessen, den PBEKDF2-Schlüsselgenerator zu verwenden, und verwenden Sie die Zeichenfolge "PBKDF2WithHmacSHA1" für SecretKeyFactoryeine aktuellere Verschlüsselung.
Maarten Bodewes
12
Tatsächlich sind alle in dieser Antwort verwendeten kryptografischen Grundelemente veraltet , MD5 und DES sicher. Pass auf dich auf.
Maarten Bodewes
MD5 und DES sind schwache Chiffresuiten und sollten vermieden werden
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
quelle
5
Fügen Sie möglicherweise einen weiteren Erklärungstext hinzu.
DaGardner
Frage, was bringt es keyValuemit dem Byte-Array? Ich sehe, dass es verwendet wird, um den Schlüssel herzustellen. Warum? Kann etwas stattdessen mit like gemacht SecretKeywerden? Wenn das so ist, wie?
Austin
@ Mandrek, der Inhalt der Datei "plaintext.txt" wird verschlüsselt. Die obige Logik verschlüsselt die Daten / Nachrichten in der Datei, die im FileReader-Konstruktor als Argument gelesen werden.
Shankar Murthy
2

Das wird funktionieren.

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
quelle
2

MD5, AES, keine Polsterung

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);
        }
    }
}
Mike
quelle
So erstellen Sie einen sicheren Schlüssel wie SecretKeySpec in eckigen (ionischen 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
quelle