Java 256-Bit-AES-kennwortbasierte Verschlüsselung


390

Ich muss eine 256-Bit-AES-Verschlüsselung implementieren, aber alle Beispiele, die ich online gefunden habe, verwenden einen "KeyGenerator", um einen 256-Bit-Schlüssel zu generieren, aber ich möchte meinen eigenen Passkey verwenden. Wie kann ich meinen eigenen Schlüssel erstellen? Ich habe versucht, es auf 256 Bit aufzufüllen, aber dann erhalte ich eine Fehlermeldung, dass der Schlüssel zu lang ist. Ich habe den Unlimited Jurisdiction Patch installiert, das ist also nicht das Problem :)

Dh. Der KeyGenerator sieht so aus ...

// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available

// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();

Code von hier genommen

BEARBEITEN

Ich habe das Passwort tatsächlich auf 256 Bytes aufgefüllt, nicht auf Bits, was zu lang ist. Das Folgende ist ein Code, den ich jetzt verwende, da ich mehr Erfahrung damit habe.

byte[] key = null; // TODO
byte[] input = null; // TODO
byte[] output = null;
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
output = cipher.doFinal(input)

Die "TODO" -Bits, die du selbst machen musst :-)


Könnten Sie klarstellen: Funktioniert das Aufrufen von kgen.init (256)?
Mitch Wheat

2
Ja, aber dies generiert automatisch einen Schlüssel ... aber da ich Daten zwischen zwei Orten verschlüsseln möchte, muss ich den Schlüssel vorher kennen, also muss ich einen angeben, anstatt einen zu "generieren". Ich kann eine 16-Bit-Version angeben, die für eine 128-Bit-Verschlüsselung funktioniert. Ich habe ein 32-Bit-Modell für 256-Bit-Verschlüsselung ausprobiert, aber es hat nicht wie erwartet funktioniert.
Nippysaurus

4
Wenn ich das richtig verstehe, versuchen Sie, einen vorab angeordneten 256-Bit-Schlüssel zu verwenden, der beispielsweise als Array von Bytes angegeben wird. In diesem Fall sollte der Ansatz von DarkSquid mit SecretKeySpec funktionieren. Es ist auch möglich, einen AES-Schlüssel aus einem Passwort abzuleiten. Wenn Sie danach suchen, lassen Sie es mich bitte wissen, und ich zeige Ihnen den richtigen Weg, dies zu tun. Das einfache Hashing eines Passworts ist nicht die beste Vorgehensweise.
Erickson

Seien Sie vorsichtig beim Auffüllen einer Nummer, da Sie möglicherweise Ihr AES weniger sicher machen.
Joshua

1
@erickson: das ist genau das, was ich tun muss (einen AES-Schlüssel von einem Passwort ableiten).
Nippysaurus

Antworten:


475

Teilen Sie die password(a char[]) und salt(a - byte[]8 Bytes, die von a ausgewählt wurden, um ein SecureRandomgutes Salz zu erhalten - das nicht geheim gehalten werden muss) mit dem Empfänger außerhalb des Bandes. Um dann aus diesen Informationen einen guten Schlüssel abzuleiten:

/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

Die magischen Zahlen (die irgendwo als Konstanten definiert werden könnten) 65536 und 256 sind die Anzahl der Schlüsselableitungsiterationen bzw. die Schlüsselgröße.

Die Schlüsselableitungsfunktion wird iteriert, um einen erheblichen Rechenaufwand zu erfordern. Dies verhindert, dass Angreifer schnell viele verschiedene Kennwörter ausprobieren können. Die Anzahl der Iterationen kann abhängig von den verfügbaren Rechenressourcen geändert werden.

Die Schlüsselgröße kann auf 128 Bit reduziert werden, was immer noch als "starke" Verschlüsselung angesehen wird, aber es gibt keinen großen Sicherheitsspielraum, wenn Angriffe entdeckt werden, die AES schwächen.

Bei Verwendung eines geeigneten Blockverkettungsmodus kann derselbe abgeleitete Schlüssel zum Verschlüsseln vieler Nachrichten verwendet werden. Bei der Verschlüsselungsblockverkettung (CBC) wird für jede Nachricht ein zufälliger Initialisierungsvektor (IV) erzeugt, der einen anderen Verschlüsselungstext ergibt, selbst wenn der Klartext identisch ist. CBC ist möglicherweise nicht der sicherste Modus, der Ihnen zur Verfügung steht (siehe AEAD unten). Es gibt viele andere Modi mit unterschiedlichen Sicherheitseigenschaften, aber alle verwenden eine ähnliche zufällige Eingabe. In jedem Fall sind die Ausgaben jeder Verschlüsselungsoperation der Chiffretext und der Initialisierungsvektor:

/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));

Speichern Sie die ciphertextund die iv. Bei der Entschlüsselung wird das SecretKeyauf genau die gleiche Weise neu generiert, wobei das Kennwort mit denselben Salt- und Iterationsparametern verwendet wird. Initialisieren Sie die Verschlüsselung mit diesem Schlüssel und den mit der Nachricht gespeicherten Initialisierungsvektor:

/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);

Java 7 enthielt API- Unterstützung für AEAD-Verschlüsselungsmodi , und der in OpenJDK- und Oracle-Distributionen enthaltene "SunJCE" -Anbieter implementiert diese ab Java 8. Einer dieser Modi wird dringend anstelle von CBC empfohlen. Es schützt die Integrität der Daten sowie deren Privatsphäre.


A java.security.InvalidKeyExceptionmit der Meldung „Unzulässige Tastengröße oder Standardparameter“ bedeutet , dass die Kryptografie - Stärke ist begrenzt; Die Richtliniendateien mit unbegrenzter Stärke befinden sich nicht am richtigen Speicherort. In einem JDK sollten sie unter platziert werden${jdk}/jre/lib/security

Basierend auf der Problembeschreibung scheint es, dass die Richtliniendateien nicht korrekt installiert sind. Systeme können problemlos mehrere Java-Laufzeiten haben. Stellen Sie sicher, dass der richtige Speicherort verwendet wird.


29
@ Nick: Lesen Sie PKCS # 5. Salze sind für PBKDF2 erforderlich, weshalb die API für die kennwortbasierte Verschlüsselung sie als Eingabe für die Schlüsselableitung benötigt. Ohne Salze könnte ein Wörterbuchangriff verwendet werden, der eine vorberechnete Liste der wahrscheinlichsten symmetrischen Verschlüsselungsschlüssel ermöglicht. Chiffrier-IVs und Schlüsselableitungssalze dienen unterschiedlichen Zwecken. IVs ermöglichen die Wiederverwendung desselben Schlüssels für mehrere Nachrichten. Salze verhindern Wörterbuchangriffe auf den Schlüssel.
Erickson

2
Erstens wäre das DES-Verschlüsselung, nicht AES. Die meisten Anbieter unterstützen die PBEwith<prf>and<encryption>Algorithmen nicht gut . Zum Beispiel bietet SunJCE kein PBE für AES. Zweitens ist das Aktivieren von Jasypt kein Ziel. Ein Paket, das angeblich Sicherheit bietet, ohne die zugrunde liegenden Prinzipien zu verstehen, scheint auf den ersten Blick gefährlich.
Erickson

6
Ich habe die Antwort von @ erickson als Klasse implementiert: github.com/mrclay/jSecureEdit/tree/master/src/org/mrclay/crypto (PBE erledigt die Arbeit, PBEStorage ist ein Wertobjekt zum gemeinsamen Speichern von IV / Chiffretext.)
Steve Clay

3
@AndyNuss Dieses Beispiel dient der reversiblen Verschlüsselung, die im Allgemeinen nicht für Kennwörter verwendet werden sollte. Sie können die PBKDF2-Schlüsselableitung verwenden, um Kennwörter sicher zu "hashen". Das bedeutet, dass Sie im obigen Beispiel das Ergebnis von tmp.getEncoded()als Hash speichern würden . Sie sollten auch die saltund die Iterationen (in diesem Beispiel 65536) speichern , damit Sie den Hash neu berechnen können, wenn jemand versucht, sich zu authentifizieren. In diesem Fall generieren Sie das Salz bei jeder Änderung des Kennworts mit einem kryptografischen Zufallszahlengenerator.
Erickson


75

Erwägen Sie die Verwendung des Spring Security Crypto-Moduls

Das Spring Security Crypto-Modul bietet Unterstützung für symmetrische Verschlüsselung, Schlüsselgenerierung und Kennwortcodierung. Der Code wird als Teil des Kernmoduls verteilt, hat jedoch keine Abhängigkeiten von anderen Spring Security- (oder Spring-) Codes.

Es bietet eine einfache Abstraktion für die Verschlüsselung und scheint mit den hier erforderlichen Anforderungen übereinzustimmen.

Die "Standard" -Verschlüsselungsmethode ist 256-Bit-AES unter Verwendung von PBKDF2 (Password-Based Key Derivation Function # 2) von PKCS # 5. Diese Methode erfordert Java 6. Das zum Generieren des SecretKey verwendete Kennwort sollte an einem sicheren Ort aufbewahrt und nicht freigegeben werden. Das Salt wird verwendet, um Wörterbuchangriffe auf den Schlüssel zu verhindern, falls Ihre verschlüsselten Daten kompromittiert werden. Ein zufälliger 16-Byte-Initialisierungsvektor wird ebenfalls angewendet, sodass jede verschlüsselte Nachricht eindeutig ist.

Ein Blick auf die Einbauten zeigt eine Struktur ähnlich der von Erickson .

Wie in der Frage erwähnt, erfordert dies auch die JCE-Richtlinie ( Unlimited Strength Jurisdiction Policy) für Java Cryptography Extension (andernfalls InvalidKeyException: Illegal Key Size). Es kann für Java 6 , Java 7 und Java 8 heruntergeladen werden .

Anwendungsbeispiel

import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.crypto.keygen.KeyGenerators;

public class CryptoExample {
    public static void main(String[] args) {
        final String password = "I AM SHERLOCKED";  
        final String salt = KeyGenerators.string().generateKey();

        TextEncryptor encryptor = Encryptors.text(password, salt);      
        System.out.println("Salt: \"" + salt + "\"");

        String textToEncrypt = "*royal secrets*";
        System.out.println("Original text: \"" + textToEncrypt + "\"");

        String encryptedText = encryptor.encrypt(textToEncrypt);
        System.out.println("Encrypted text: \"" + encryptedText + "\"");

        // Could reuse encryptor but wanted to show reconstructing TextEncryptor
        TextEncryptor decryptor = Encryptors.text(password, salt);
        String decryptedText = decryptor.decrypt(encryptedText);
        System.out.println("Decrypted text: \"" + decryptedText + "\"");

        if(textToEncrypt.equals(decryptedText)) {
            System.out.println("Success: decrypted text matches");
        } else {
            System.out.println("Failed: decrypted text does not match");
        }       
    }
}

Und Beispielausgabe,

Salz: "feacbc02a3a697b0"
Originaltext: "* königliche Geheimnisse *"
Verschlüsselter Text: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 
Entschlüsselter Text: "* königliche Geheimnisse *"
Erfolg: Entschlüsselte Textübereinstimmungen

Können Sie dieses Modul verwenden, ohne den gesamten Frühling zu laden? Sie scheinen keine JAR-Dateien zum Download bereitgestellt zu haben.
Theglauber

5
@theglauber Ja, Sie können das Modul ohne Spring Security oder das Spring Framework verwenden. Beim Betrachten des POM ist die einzige Laufzeitabhängigkeit die Apache- Commons-Protokollierung 1.1.1 . Sie können das Glas mit maven ziehen oder es direkt vom offiziellen Binär-Repo herunterladen ( weitere Informationen zu Spring-Binärdateien finden Sie unter Spring 4-Binärdateien herunterladen ).
John McCarthy

1
Ist es möglich, die Schlüssellänge auf 128 Bit einzustellen? Das Ändern des Sicherheitsordners in jedem PC ist für mich keine Option.
IvanRF

1
@IvanRF sorry, sieht nicht so aus. 256 ist in der Quelle
John McCarthy

2
Das NULL_IV_GENERATORvom Spring-Dienstprogramm verwendete ist nicht sicher. Wenn die Anwendung keine IV bereitstellt, lassen Sie den Anbieter diese auswählen und fragen Sie sie nach der Initialisierung ab.
Erickson

32

Nachdem ich die Vorschläge von erickson gelesen und aus ein paar anderen Beiträgen und diesem Beispiel hier herausgefunden habe, was ich konnte , habe ich versucht, Dougs Code mit den empfohlenen Änderungen zu aktualisieren. Fühlen Sie sich frei zu bearbeiten, um es besser zu machen.

  • Der Initialisierungsvektor ist nicht mehr festgelegt
  • Der Verschlüsselungsschlüssel wird unter Verwendung von Code von erickson abgeleitet
  • 8-Byte-Salt wird in setupEncrypt () mit SecureRandom () generiert.
  • Der Entschlüsselungsschlüssel wird aus dem Verschlüsselungssalz und dem Kennwort generiert
  • Die Entschlüsselungsverschlüsselung wird aus dem Entschlüsselungsschlüssel und dem Initialisierungsvektor generiert
  • Hex Twiddling anstelle von org.apache.commons Codec Hex Routinen entfernt

Einige Hinweise: Hierbei wird ein 128-Bit-Verschlüsselungsschlüssel verwendet. Java führt anscheinend keine sofort einsatzbereite 256-Bit-Verschlüsselung durch. Für die Implementierung von 256 müssen einige zusätzliche Dateien im Java-Installationsverzeichnis installiert werden.

Ich bin auch keine Krypto-Person. Pass auf dich auf.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

public class Crypto
{
    String mPassword = null;
    public final static int SALT_LEN = 8;
    byte [] mInitVec = null;
    byte [] mSalt = null;
    Cipher mEcipher = null;
    Cipher mDecipher = null;
    private final int KEYLEN_BITS = 128; // see notes below where this is used.
    private final int ITERATIONS = 65536;
    private final int MAX_FILE_BUF = 1024;

    /**
     * create an object with just the passphrase from the user. Don't do anything else yet 
     * @param password
     */
    public Crypto (String password)
    {
        mPassword = password;
    }

    /**
     * return the generated salt for this object
     * @return
     */
    public byte [] getSalt ()
    {
        return (mSalt);
    }

    /**
     * return the initialization vector created from setupEncryption
     * @return
     */
    public byte [] getInitVec ()
    {
        return (mInitVec);
    }

    /**
     * debug/print messages
     * @param msg
     */
    private void Db (String msg)
    {
        System.out.println ("** Crypt ** " + msg);
    }

    /**
     * this must be called after creating the initial Crypto object. It creates a salt of SALT_LEN bytes
     * and generates the salt bytes using secureRandom().  The encryption secret key is created 
     * along with the initialization vectory. The member variable mEcipher is created to be used
     * by the class later on when either creating a CipherOutputStream, or encrypting a buffer
     * to be written to disk.
     *  
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidParameterSpecException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeyException
     */
    public void setupEncrypt () throws NoSuchAlgorithmException, 
                                                           InvalidKeySpecException, 
                                                           NoSuchPaddingException, 
                                                           InvalidParameterSpecException, 
                                                           IllegalBlockSizeException, 
                                                           BadPaddingException, 
                                                           UnsupportedEncodingException, 
                                                           InvalidKeyException
    {
        SecretKeyFactory factory = null;
        SecretKey tmp = null;

        // crate secureRandom salt and store  as member var for later use
         mSalt = new byte [SALT_LEN];
        SecureRandom rnd = new SecureRandom ();
        rnd.nextBytes (mSalt);
        Db ("generated salt :" + Hex.encodeHexString (mSalt));

        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

        /* Derive the key, given password and salt. 
         * 
         * in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
         * The end user must also install them (not compiled in) so beware. 
         * see here:  http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
         */
        KeySpec spec = new PBEKeySpec (mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);
        tmp = factory.generateSecret (spec);
        SecretKey secret = new SecretKeySpec (tmp.getEncoded(), "AES");

        /* Create the Encryption cipher object and store as a member variable
         */
        mEcipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
        mEcipher.init (Cipher.ENCRYPT_MODE, secret);
        AlgorithmParameters params = mEcipher.getParameters ();

        // get the initialization vectory and store as member var 
        mInitVec = params.getParameterSpec (IvParameterSpec.class).getIV();

        Db ("mInitVec is :" + Hex.encodeHexString (mInitVec));
    }



    /**
     * If a file is being decrypted, we need to know the pasword, the salt and the initialization vector (iv). 
     * We have the password from initializing the class. pass the iv and salt here which is
     * obtained when encrypting the file initially.
     *   
     * @param initvec
     * @param salt
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws DecoderException
     */
    public void setupDecrypt (String initvec, String salt) throws NoSuchAlgorithmException, 
                                                                                       InvalidKeySpecException, 
                                                                                       NoSuchPaddingException, 
                                                                                       InvalidKeyException, 
                                                                                       InvalidAlgorithmParameterException, 
                                                                                       DecoderException
    {
        SecretKeyFactory factory = null;
        SecretKey tmp = null;
        SecretKey secret = null;

        // since we pass it as a string of input, convert to a actual byte buffer here
        mSalt = Hex.decodeHex (salt.toCharArray ());
       Db ("got salt " + Hex.encodeHexString (mSalt));

        // get initialization vector from passed string
        mInitVec = Hex.decodeHex (initvec.toCharArray ());
        Db ("got initvector :" + Hex.encodeHexString (mInitVec));


        /* Derive the key, given password and salt. */
        // in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
        // The end user must also install them (not compiled in) so beware. 
        // see here: 
      // http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);

        tmp = factory.generateSecret(spec);
        secret = new SecretKeySpec(tmp.getEncoded(), "AES");

        /* Decrypt the message, given derived key and initialization vector. */
        mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));
    }


    /**
     * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
     * Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
     * 
     * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
     * into uncertain problems with that. 
     *  
     * @param input - the cleartext file to be encrypted
     * @param output - the encrypted data file
     * @throws IOException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    public void WriteEncryptedFile (File input, File output) throws 
                                                                                          IOException, 
                                                                                          IllegalBlockSizeException, 
                                                                                          BadPaddingException
    {
        FileInputStream fin;
        FileOutputStream fout;
        long totalread = 0;
        int nread = 0;
        byte [] inbuf = new byte [MAX_FILE_BUF];

        fout = new FileOutputStream (output);
        fin = new FileInputStream (input);

        while ((nread = fin.read (inbuf)) > 0 )
        {
            Db ("read " + nread + " bytes");
            totalread += nread;

            // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
            // and results in full blocks of MAX_FILE_BUF being written. 
            byte [] trimbuf = new byte [nread];
            for (int i = 0; i < nread; i++)
                trimbuf[i] = inbuf[i];

            // encrypt the buffer using the cipher obtained previosly
            byte [] tmp = mEcipher.update (trimbuf);

            // I don't think this should happen, but just in case..
            if (tmp != null)
                fout.write (tmp);
        }

        // finalize the encryption since we've done it in blocks of MAX_FILE_BUF
        byte [] finalbuf = mEcipher.doFinal ();
        if (finalbuf != null)
            fout.write (finalbuf);

        fout.flush();
        fin.close();
        fout.close();

        Db ("wrote " + totalread + " encrypted bytes");
    }


    /**
     * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
     * to disk as (output) File.
     * 
     * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
     *  and still have a correctly decrypted file in the end. Seems to work so left it in.
     *  
     * @param input - File object representing encrypted data on disk 
     * @param output - File object of cleartext data to write out after decrypting
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    public void ReadEncryptedFile (File input, File output) throws 
                                                                                                                                            IllegalBlockSizeException, 
                                                                                                                                            BadPaddingException, 
                                                                                                                                            IOException
    {
        FileInputStream fin; 
        FileOutputStream fout;
        CipherInputStream cin;
        long totalread = 0;
        int nread = 0;
        byte [] inbuf = new byte [MAX_FILE_BUF];

        fout = new FileOutputStream (output);
        fin = new FileInputStream (input);

        // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
        cin = new CipherInputStream (fin, mDecipher);

        while ((nread = cin.read (inbuf)) > 0 )
        {
            Db ("read " + nread + " bytes");
            totalread += nread;

            // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
            byte [] trimbuf = new byte [nread];
            for (int i = 0; i < nread; i++)
                trimbuf[i] = inbuf[i];

            // write out the size-adjusted buffer
            fout.write (trimbuf);
        }

        fout.flush();
        cin.close();
        fin.close ();       
        fout.close();   

        Db ("wrote " + totalread + " encrypted bytes");
    }


    /**
     * adding main() for usage demonstration. With member vars, some of the locals would not be needed
     */
    public static void main(String [] args)
    {

        // create the input.txt file in the current directory before continuing
        File input = new File ("input.txt");
        File eoutput = new File ("encrypted.aes");
        File doutput = new File ("decrypted.txt");
        String iv = null;
        String salt = null;
        Crypto en = new Crypto ("mypassword");

        /*
         * setup encryption cipher using password. print out iv and salt
         */
        try
      {
          en.setupEncrypt ();
          iv = Hex.encodeHexString (en.getInitVec ()).toUpperCase ();
          salt = Hex.encodeHexString (en.getSalt ()).toUpperCase ();
      }
      catch (InvalidKeyException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchAlgorithmException e)
      {
          e.printStackTrace();
      }
      catch (InvalidKeySpecException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchPaddingException e)
      {
          e.printStackTrace();
      }
      catch (InvalidParameterSpecException e)
      {
          e.printStackTrace();
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (UnsupportedEncodingException e)
      {
          e.printStackTrace();
      }

        /*
         * write out encrypted file
         */
        try
      {
          en.WriteEncryptedFile (input, eoutput);
          System.out.printf ("File encrypted to " + eoutput.getName () + "\niv:" + iv + "\nsalt:" + salt + "\n\n");
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (IOException e)
      {
          e.printStackTrace();
      }


        /*
         * decrypt file
         */
        Crypto dc = new Crypto ("mypassword");
        try
      {
          dc.setupDecrypt (iv, salt);
      }
      catch (InvalidKeyException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchAlgorithmException e)
      {
          e.printStackTrace();
      }
      catch (InvalidKeySpecException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchPaddingException e)
      {
          e.printStackTrace();
      }
      catch (InvalidAlgorithmParameterException e)
      {
          e.printStackTrace();
      }
      catch (DecoderException e)
      {
          e.printStackTrace();
      }

        /*
         * write out decrypted file
         */
        try
      {
          dc.ReadEncryptedFile (eoutput, doutput);
          System.out.println ("decryption finished to " + doutput.getName ());
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (IOException e)
      {
          e.printStackTrace();
      }
   }


}

13
Dies ist im Grunde die gleiche Antwort wie die von Erickson, umgeben von einem - meiner Meinung nach nicht so gut programmierten - Wrapper. printStackTrace()
Maarten Bodewes

2
@owlstead - Dies ist eine großartige Antwort. Es zeigt, wie ein Stream durch Verschlüsseln des Bytepuffers verschlüsselt wird, anstatt alles im Speicher zu haben. Ericksons Antwort funktioniert nicht für große Dateien, die nicht in den Speicher passen. Also +1 zu Wufoo. :)
Dynamokaj

2
@dynamokaj Die Verwendung von CipherInputStreamund CipherOutputStreamist kein großes Problem. Das Mischen aller Ausnahmen unter der Tabelle ist ein Problem. Die Tatsache, dass das Salz plötzlich zu einem Feld geworden ist und dass die Infusion erforderlich ist, ist ein Problem. Die Tatsache, dass es nicht den Java-Codierungskonventionen entspricht, ist ein Problem. Und die Tatsache, dass dies nur bei Dateien funktioniert, wenn nicht danach gefragt wurde, ist ein Problem. Und dass der Rest des Codes im Grunde eine Kopie ist, hilft auch nicht. Aber vielleicht werde ich es optimieren, um es besser zu machen, wie vorgeschlagen ...
Maarten Bodewes

@owlstead Ich stimme zu, dass die Codierung besser hätte aussehen können. Ich habe sie auf 1/4 oder so reduziert, aber ich mag es, dass er mich mit CipherInputStream und CipherOutputStream bekannt gemacht hat, da das genau das war, was ich gestern brauchte! ;)
Dynamokaj

warum zweimal? fout.close (); fout.close ();
Marian Paździoch

7

Das Generieren eines eigenen Schlüssels aus einem Byte-Array ist einfach:

byte[] raw = ...; // 32 bytes in size for a 256 bit key
Key skey = new javax.crypto.spec.SecretKeySpec(raw, "AES");

Das Erstellen eines 256-Bit-Schlüssels reicht jedoch nicht aus. Wenn der Schlüsselgenerator keine 256-Bit-Schlüssel für Sie generieren kann, unterstützt die CipherKlasse wahrscheinlich auch kein AES-256-Bit. Sie sagen, Sie haben den Patch für die unbegrenzte Gerichtsbarkeit installiert, daher sollte die AES-256-Verschlüsselung unterstützt werden (aber auch 256-Bit-Schlüssel, sodass dies möglicherweise ein Konfigurationsproblem darstellt).

Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skey);
byte[] encrypted = cipher.doFinal(plainText.getBytes());

Eine Problemumgehung wegen mangelnder AES-256-Unterstützung besteht darin, eine frei verfügbare Implementierung von AES-256 als benutzerdefinierten Anbieter zu verwenden. Dazu müssen Sie eine eigene ProviderUnterklasse erstellen und mit verwenden Cipher.getInstance(String, Provider). Dies kann jedoch ein komplizierter Prozess sein.


5
Sie sollten immer den Modus und den Füllalgorithmus angeben. Java verwendet standardmäßig den unsicheren EZB-Modus.
Maarten Bodewes

Sie können keinen eigenen Anbieter erstellen, Anbieter müssen signiert sein (ich kann nicht glauben, dass ich diesen Fehler anfangs gelesen habe). Selbst wenn Sie könnten, liegt die Einschränkung der Schlüsselgröße in der Implementierung von Cipher, nicht im Anbieter selbst. Sie können AES-256 in Java 8 und niedriger verwenden, benötigen jedoch eine proprietäre API. Oder eine Laufzeit, die natürlich keine Einschränkungen für die Schlüsselgröße darstellt.
Maarten Bodewes

Neuere Versionen von OpenJDK (und Android) unterliegen keinen Einschränkungen beim Hinzufügen eines eigenen Sicherheits- / Krypto-Anbieters. Aber Sie tun dies natürlich auf eigenes Risiko. Wenn Sie vergessen, Ihre Bibliotheken auf dem neuesten Stand zu halten, können Sie sich Sicherheitsrisiken aussetzen.
Maarten Bodewes

1
@ MaartenBodewes + OpenJDK hatte nie das Problem der "eingeschränkten Kryptorichtlinie", und Oracle JDK hat es vor über einem Jahr für 8u161 und 9 entfernt entfernt (und möglicherweise einige niedrigere Versionen, die jetzt nur kostenpflichtig sind, aber ich habe diese nicht überprüft).
Dave_thompson_085

6

Was ich in der Vergangenheit getan habe, ist, den Schlüssel über etwas wie SHA256 zu hashen und dann die Bytes aus dem Hash in das Schlüsselbyte [] zu extrahieren.

Nachdem Sie Ihr Byte [] haben, können Sie einfach tun:

SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());

12
Für andere: Dies ist keine sehr sichere Methode. Sie sollten PBKDF 2 verwenden, das in PKCS # 5 angegeben ist. erickson sagte, wie man das oben macht. Die Methode von DarkSquid ist anfällig für Kennwortangriffe und funktioniert auch nur, wenn die Größe Ihres Klartextes ein Vielfaches der Blockgröße von AES (128 Bit) beträgt, da er das Auffüllen weggelassen hat. Außerdem wird der Modus nicht angegeben. Lesen Sie die Block Cipher-Betriebsmodi von Wikipedia, um Bedenken zu haben.
Hut8

1
@DarkSquid Cipher aes256 = Cipher.getInstance("AES/OFB/NoPadding"); MessageDigest keyDigest = MessageDigest.getInstance("SHA-256"); byte[] keyHash = keyDigest.digest(secret.getBytes("UTF-8")); SecretKeySpec key = new SecretKeySpec(keyHash, "AES"); aes256.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(initializationVector)); Ich mache auch das Gleiche wie in Ihrer Antwort vorgeschlagen, aber am Ende habe ich immer noch diese java.security.InvalidKeyException: Unzulässige Schlüsselgröße Ist das Herunterladen der JCE-Richtliniendatei obligatorisch?
Niranjan Subramanian

2
Verwenden Sie diese Methode NICHT in Produktionsumgebungen. Wenn Sie mit der passwortbasierten Verschlüsselung beginnen, werden viele Benutzer von Codewänden überwältigt und verstehen nicht, wie Wörterbuchangriffe und andere einfache Hacks funktionieren. Es kann zwar frustrierend sein zu lernen, aber es lohnt sich, dies zu erforschen. Hier ist ein guter Artikel für Anfänger: adambard.com/blog/3-wrong-ways-to-store-a-password
IcedDante

1

Zusätzlich zu den Änderungen von @ Wufoo verwendet die folgende Version InputStreams anstelle von Dateien, um die Arbeit mit einer Vielzahl von Dateien zu vereinfachen. Außerdem werden IV und Salt am Anfang der Datei gespeichert, sodass nur das Kennwort nachverfolgt werden muss. Da IV und Salz nicht geheim sein müssen, erleichtert dies das Leben ein wenig.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {
    public final static int SALT_LEN     = 8;
    static final String     HEXES        = "0123456789ABCDEF";
    String                  mPassword    = null;
    byte[]                  mInitVec     = null;
    byte[]                  mSalt        = new byte[SALT_LEN];
    Cipher                  mEcipher     = null;
    Cipher                  mDecipher    = null;
    private final int       KEYLEN_BITS  = 128;    // see notes below where this is used.
    private final int       ITERATIONS   = 65536;
    private final int       MAX_FILE_BUF = 1024;

    /**
     * create an object with just the passphrase from the user. Don't do anything else yet
     * @param password
     */
    public AES(String password) {
        mPassword = password;
    }

    public static String byteToHex(byte[] raw) {
        if (raw == null) {
            return null;
        }

        final StringBuilder hex = new StringBuilder(2 * raw.length);

        for (final byte b : raw) {
            hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
        }

        return hex.toString();
    }

    public static byte[] hexToByte(String hexString) {
        int    len = hexString.length();
        byte[] ba  = new byte[len / 2];

        for (int i = 0; i < len; i += 2) {
            ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                                + Character.digit(hexString.charAt(i + 1), 16));
        }

        return ba;
    }

    /**
     * debug/print messages
     * @param msg
     */
    private void Db(String msg) {
        System.out.println("** Crypt ** " + msg);
    }

    /**
     * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
     * Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
     *
     * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
     * into uncertain problems with that.
     *
     * @param input - the cleartext file to be encrypted
     * @param output - the encrypted data file
     * @throws IOException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    public void WriteEncryptedFile(InputStream inputStream, OutputStream outputStream)
            throws IOException, IllegalBlockSizeException, BadPaddingException {
        try {
            long             totalread = 0;
            int              nread     = 0;
            byte[]           inbuf     = new byte[MAX_FILE_BUF];
            SecretKeyFactory factory   = null;
            SecretKey        tmp       = null;

            // crate secureRandom salt and store  as member var for later use
            mSalt = new byte[SALT_LEN];

            SecureRandom rnd = new SecureRandom();

            rnd.nextBytes(mSalt);
            Db("generated salt :" + byteToHex(mSalt));
            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

            /*
             *  Derive the key, given password and salt.
             *
             * in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
             * The end user must also install them (not compiled in) so beware.
             * see here:  http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
             */
            KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);

            tmp = factory.generateSecret(spec);

            SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

            /*
             *  Create the Encryption cipher object and store as a member variable
             */
            mEcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            mEcipher.init(Cipher.ENCRYPT_MODE, secret);

            AlgorithmParameters params = mEcipher.getParameters();

            // get the initialization vectory and store as member var
            mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
            Db("mInitVec is :" + byteToHex(mInitVec));
            outputStream.write(mSalt);
            outputStream.write(mInitVec);

            while ((nread = inputStream.read(inbuf)) > 0) {
                Db("read " + nread + " bytes");
                totalread += nread;

                // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
                // and results in full blocks of MAX_FILE_BUF being written.
                byte[] trimbuf = new byte[nread];

                for (int i = 0; i < nread; i++) {
                    trimbuf[i] = inbuf[i];
                }

                // encrypt the buffer using the cipher obtained previosly
                byte[] tmpBuf = mEcipher.update(trimbuf);

                // I don't think this should happen, but just in case..
                if (tmpBuf != null) {
                    outputStream.write(tmpBuf);
                }
            }

            // finalize the encryption since we've done it in blocks of MAX_FILE_BUF
            byte[] finalbuf = mEcipher.doFinal();

            if (finalbuf != null) {
                outputStream.write(finalbuf);
            }

            outputStream.flush();
            inputStream.close();
            outputStream.close();
            outputStream.close();
            Db("wrote " + totalread + " encrypted bytes");
        } catch (InvalidKeyException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidParameterSpecException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeySpecException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
     * to disk as (output) File.
     *
     * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
     *  and still have a correctly decrypted file in the end. Seems to work so left it in.
     *
     * @param input - File object representing encrypted data on disk
     * @param output - File object of cleartext data to write out after decrypting
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    public void ReadEncryptedFile(InputStream inputStream, OutputStream outputStream)
            throws IllegalBlockSizeException, BadPaddingException, IOException {
        try {
            CipherInputStream cin;
            long              totalread = 0;
            int               nread     = 0;
            byte[]            inbuf     = new byte[MAX_FILE_BUF];

            // Read the Salt
            inputStream.read(this.mSalt);
            Db("generated salt :" + byteToHex(mSalt));

            SecretKeyFactory factory = null;
            SecretKey        tmp     = null;
            SecretKey        secret  = null;

            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

            KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);

            tmp    = factory.generateSecret(spec);
            secret = new SecretKeySpec(tmp.getEncoded(), "AES");

            /* Decrypt the message, given derived key and initialization vector. */
            mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            // Set the appropriate size for mInitVec by Generating a New One
            AlgorithmParameters params = mDecipher.getParameters();

            mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();

            // Read the old IV from the file to mInitVec now that size is set.
            inputStream.read(this.mInitVec);
            Db("mInitVec is :" + byteToHex(mInitVec));
            mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));

            // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
            cin = new CipherInputStream(inputStream, mDecipher);

            while ((nread = cin.read(inbuf)) > 0) {
                Db("read " + nread + " bytes");
                totalread += nread;

                // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
                byte[] trimbuf = new byte[nread];

                for (int i = 0; i < nread; i++) {
                    trimbuf[i] = inbuf[i];
                }

                // write out the size-adjusted buffer
                outputStream.write(trimbuf);
            }

            outputStream.flush();
            cin.close();
            inputStream.close();
            outputStream.close();
            Db("wrote " + totalread + " encrypted bytes");
        } catch (Exception ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * adding main() for usage demonstration. With member vars, some of the locals would not be needed
     */
    public static void main(String[] args) {

        // create the input.txt file in the current directory before continuing
        File   input   = new File("input.txt");
        File   eoutput = new File("encrypted.aes");
        File   doutput = new File("decrypted.txt");
        String iv      = null;
        String salt    = null;
        AES    en      = new AES("mypassword");

        /*
         * write out encrypted file
         */
        try {
            en.WriteEncryptedFile(new FileInputStream(input), new FileOutputStream(eoutput));
            System.out.printf("File encrypted to " + eoutput.getName() + "\niv:" + iv + "\nsalt:" + salt + "\n\n");
        } catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
            e.printStackTrace();
        }

        /*
         * decrypt file
         */
        AES dc = new AES("mypassword");

        /*
         * write out decrypted file
         */
        try {
            dc.ReadEncryptedFile(new FileInputStream(eoutput), new FileOutputStream(doutput));
            System.out.println("decryption finished to " + doutput.getName());
        } catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
            e.printStackTrace();
        }
    }
}

1
Diese Lösung scheint eine umständliche Pufferbehandlung und eine absolut unterdurchschnittliche Ausnahmebehandlung zu verwenden, die sie im Grunde protokolliert und dann vergisst. Seien Sie gewarnt, dass die Verwendung von CBC für Dateien in Ordnung ist, nicht jedoch für die Transportsicherheit. Die Verwendung von PBKDF2 und AES kann natürlich verteidigt werden. In diesem Sinne kann dies eine gute Grundlage für eine Lösung sein.
Maarten Bodewes

1

(Vielleicht hilfreich für andere mit einer ähnlichen Anforderung)

Ich hatte eine ähnliche Anforderung, AES-256-CBCum in Java zu verschlüsseln und zu entschlüsseln.

Um die 256-Byte-Verschlüsselung / -Entschlüsselung zu erreichen (oder anzugeben), Java Cryptography Extension (JCE)sollte die Richtlinie auf festgelegt werden"Unlimited"

Es kann in der java.securityDatei unter $JAVA_HOME/jre/lib/security(für JDK) oder $JAVA_HOME/lib/security(für JRE) eingestellt werden.

crypto.policy=unlimited

Oder im Code als

Security.setProperty("crypto.policy", "unlimited");

In Java 9 und späteren Versionen ist dies standardmäßig aktiviert.


0

Erwägen Sie die Verwendung von Encryptor4j, dessen Autor ich bin.

Stellen Sie zunächst sicher, dass Sie Dateien mit Richtlinien für die Zuständigkeit für unbegrenzte Stärke installiert haben, bevor Sie fortfahren, damit Sie 256-Bit-AES-Schlüssel verwenden können.

Dann machen Sie folgendes:

String password = "mysupersecretpassword"; 
Key key = KeyFactory.AES.keyFromPassword(password.toCharArray());
Encryptor encryptor = new Encryptor(key, "AES/CBC/PKCS7Padding", 16);

Sie können jetzt den Verschlüsseler verwenden, um Ihre Nachricht zu verschlüsseln. Sie können auch eine Streaming-Verschlüsselung durchführen, wenn Sie möchten. Es generiert automatisch eine sichere IV und stellt diese vor.

Wenn es sich um eine Datei handelt, die Sie komprimieren möchten, sehen Sie sich diese Antwort an. Verschlüsseln einer großen Datei mit AES mit JAVA für einen noch einfacheren Ansatz.


2
Hallo Martin, du solltest immer angeben, dass du der Autor der Bibliothek bist, wenn du darauf hinweisen willst. Es gibt Unmengen von Krypto-Wrappern, die versuchen, die Dinge einfach zu machen. Hat dieser ein Sicherheitspapier oder hat er irgendwelche Bewertungen erhalten, damit es sich lohnt?
Maarten Bodewes

-1

Verwenden Sie diese Klasse zur Verschlüsselung. Es klappt.

public class ObjectCrypter {


    public static byte[] encrypt(byte[] ivBytes, byte[] keyBytes, byte[] mes) 
            throws NoSuchAlgorithmException,
            NoSuchPaddingException,
            InvalidKeyException,
            InvalidAlgorithmParameterException,
            IllegalBlockSizeException,
            BadPaddingException, IOException {

        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = null;
        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
        return  cipher.doFinal(mes);

    }

    public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] bytes) 
            throws NoSuchAlgorithmException,
            NoSuchPaddingException,
            InvalidKeyException,
            InvalidAlgorithmParameterException,
            IllegalBlockSizeException,
            BadPaddingException, IOException, ClassNotFoundException {

        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
        return  cipher.doFinal(bytes);

    }
}

Und das sind ivBytes und ein zufälliger Schlüssel;

String key = "e8ffc7e56311679f12b6fc91aa77a5eb";

byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
keyBytes = key.getBytes("UTF-8");

10
"es funktioniert" .... ja, aber es erfüllt nicht die Anforderungen für die Erstellung einer kryptografisch sicheren Lösung (meiner Meinung nach erfüllt es auch nicht die Java-Codierungsstandards für die Ausnahmebehandlung).
Maarten Bodewes

2
IV wird auf Null initialisiert. Suchen Sie nach BEAST- und ACPA-Angriffen.
Michele Giuseppe Fadda

Ausnahmen vom Wazoo, der Methode zum Generieren des "zufälligen" Schlüssels und einer Null-IV sind ein Problem bei dieser Implementierung, aber diese Probleme sind trivial zu beheben. +1.
Phil
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.