Java Enkripsi Berbasis Kata Sandi 256-bit

390

Saya perlu menerapkan enkripsi AES 256 bit, tetapi semua contoh yang saya temukan online menggunakan "KeyGenerator" untuk menghasilkan kunci 256 bit, tetapi saya ingin menggunakan passkey saya sendiri. Bagaimana saya bisa membuat kunci saya sendiri? Saya sudah mencoba padding ke 256 bit, tetapi kemudian saya mendapatkan kesalahan dengan mengatakan bahwa kuncinya terlalu panjang. Saya memiliki patch yurisdiksi tidak terbatas yang diinstal, jadi bukan itu masalahnya :)

Yaitu. KeyGenerator terlihat seperti ini ...

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

Kode diambil dari sini

EDIT

Saya benar-benar mengisi kata sandi hingga 256 byte, bukan bit, yang terlalu panjang. Berikut ini adalah beberapa kode yang saya gunakan sekarang sehingga saya memiliki lebih banyak pengalaman dengan ini.

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)

Bit "TODO" yang perlu Anda lakukan sendiri :-)

Nippysaurus
sumber
Bisakah Anda menjelaskan: apakah panggilan kgen.init (256) berfungsi?
Mitch Wheat
2
Ya, tetapi ini secara otomatis menghasilkan kunci ... tetapi karena saya ingin mengenkripsi data antara dua tempat, saya harus mengetahui kunci sebelumnya, jadi saya perlu menentukan satu daripada "menghasilkan" satu. Saya dapat menentukan 16bit yang berfungsi untuk enkripsi 128bit yang berfungsi. Saya telah mencoba 32bit untuk enkripsi 256bit, tetapi tidak berfungsi seperti yang diharapkan.
Nippysaurus
4
Jika saya mengerti dengan benar, Anda mencoba menggunakan kunci 256-bit yang telah diatur sebelumnya, ditentukan, misalnya, sebagai array byte. Jika demikian, pendekatan DarkSquid menggunakan SecretKeySpec harus bekerja. Dimungkinkan juga untuk memperoleh kunci AES dari kata sandi; jika itu yang Anda cari, beri tahu saya, dan saya akan menunjukkan cara yang benar untuk melakukannya; sekadar membuat kata sandi bukanlah praktik terbaik.
erickson
Berhati-hatilah dalam menambahkan nomor, Anda mungkin membuat AES Anda kurang aman.
Joshua
1
@erickson: itulah yang harus saya lakukan (mendapatkan kunci AES dari kata sandi).
Nippysaurus

Jawaban:

476

Bagikan password(a char[]) dan salt(a byte[]—8 byte yang dipilih oleh a SecureRandommenjadi garam yang baik — yang tidak perlu dirahasiakan) dengan penerima di luar jalur. Kemudian untuk mendapatkan kunci yang baik dari informasi ini:

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

Angka ajaib (yang dapat didefinisikan sebagai konstanta di suatu tempat) 65536 dan 256 adalah jumlah iterasi derivasi kunci dan ukuran kunci, masing-masing.

Fungsi derivasi kunci diulang untuk memerlukan upaya komputasi yang signifikan, dan yang mencegah penyerang dari cepat mencoba berbagai kata sandi yang berbeda. Hitungan iterasi dapat diubah tergantung pada sumber daya komputasi yang tersedia.

Ukuran kunci dapat dikurangi menjadi 128 bit, yang masih dianggap enkripsi "kuat", tetapi tidak memberikan banyak margin keamanan jika serangan ditemukan yang melemahkan AES.

Digunakan dengan mode blok-rantai yang tepat, kunci turunan yang sama dapat digunakan untuk mengenkripsi banyak pesan. Dalam Cipher Block Chaining (CBC) , vektor inisialisasi acak (IV) dihasilkan untuk setiap pesan, menghasilkan teks sandi yang berbeda bahkan jika teks biasa identik. CBC mungkin bukan mode paling aman yang tersedia untuk Anda (lihat AEAD di bawah); ada banyak mode lain dengan sifat keamanan berbeda, tetapi semuanya menggunakan input acak yang serupa. Bagaimanapun, output dari setiap operasi enkripsi adalah teks sandi dan vektor inisialisasi:

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

Simpan ciphertextdan iv. Pada dekripsi, kode SecretKeydibuat ulang dengan cara yang persis sama, menggunakan kata sandi dengan parameter garam dan iterasi yang sama. Inisialisasi sandi dengan kunci ini dan vektor inisialisasi yang tersimpan bersama pesan:

/* 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 menyertakan dukungan API untuk mode cipher AEAD , dan penyedia "SunJCE" yang disertakan dengan OpenJDK dan distribusi Oracle mengimplementasikannya dengan Java 8. Salah satu mode ini sangat disarankan sebagai pengganti CBC; itu akan melindungi integritas data serta privasi mereka.


Sebuah java.security.InvalidKeyExceptiondengan pesan "Ilegal ukuran kunci atau standar parameter" berarti bahwa kekuatan kriptografi yang terbatas; file kebijakan yurisdiksi kekuatan tidak terbatas tidak di lokasi yang benar. Dalam JDK, mereka harus ditempatkan di bawah${jdk}/jre/lib/security

Berdasarkan uraian masalah, sepertinya file kebijakan tidak diinstal dengan benar. Sistem dapat dengan mudah memiliki beberapa runtime Java; periksa kembali untuk memastikan bahwa lokasi yang benar sedang digunakan.

erickson
sumber
29
@Nick: Baca PKCS # 5. Garam diperlukan untuk PBKDF2, itulah sebabnya API untuk enkripsi berbasis kata sandi mengharuskan mereka sebagai input untuk derivasi kunci. Tanpa garam, serangan kamus dapat digunakan, memungkinkan daftar pra-perhitungan kunci enkripsi simetris yang paling mungkin. Cipher IVs dan garam derivasi kunci memiliki tujuan berbeda. IVs memungkinkan seseorang menggunakan kembali kunci yang sama untuk beberapa pesan. Garam mencegah serangan kamus pada tombol.
erickson
2
Pertama, itu akan menjadi enkripsi DES, bukan AES. Sebagian besar penyedia tidak memiliki dukungan yang baik untuk PBEwith<prf>and<encryption>algoritma; misalnya, SunJCE tidak menyediakan dan PBE untuk AES. Kedua, memampukan jasypt bukanlah tujuan. Sebuah paket yang dimaksudkan untuk menawarkan keamanan tanpa memerlukan pemahaman tentang prinsip-prinsip yang mendasarinya tampaknya berbahaya.
erickson
6
Saya telah mengimplementasikan jawaban @ erickson sebagai kelas: github.com/mrclay/jSecureEdit/tree/master/src/org/mrclay/crypto (PBE berfungsi, PBEStorage adalah objek nilai untuk menyimpan IV / ciphertext bersama-sama.)
Steve Clay
3
@AndyNuss Contoh ini untuk enkripsi yang dapat dibalik, yang umumnya tidak boleh digunakan untuk kata sandi. Anda dapat menggunakan penurunan kunci PBKDF2 untuk kata sandi "hash" dengan aman. Itu berarti bahwa dalam contoh di atas, Anda akan menyimpan hasil tmp.getEncoded()sebagai hash. Anda juga harus menyimpan saltdan iterasi (65536 dalam contoh ini) sehingga Anda dapat menghitung ulang hash ketika seseorang mencoba untuk mengotentikasi. Dalam hal ini, buat garam dengan generator nomor acak kriptografis setiap kali kata sandi diubah.
erickson
6
Untuk menjalankan kode ini, pastikan Anda memiliki File Kebijakan Yurisdiksi Kekuatan Tak Terbatas yang tepat di JRE Anda seperti yang dinyatakan dalam ngs.ac.uk/tools/jcepolicyfiles
Amir Moghimi
75

Pertimbangkan untuk menggunakan Modul Crypto Spring Security

Modul Spring Security Crypto menyediakan dukungan untuk enkripsi simetris, pembuatan kunci, dan pengkodean kata sandi. Kode didistribusikan sebagai bagian dari modul inti tetapi tidak memiliki dependensi pada kode Spring Security (atau Spring) lainnya.

Ini memberikan abstraksi sederhana untuk enkripsi dan tampaknya cocok dengan apa yang diperlukan di sini,

Metode enkripsi "standar" adalah AES 256-bit menggunakan PBKDF2 PKCS # 5 (Fungsi Penurunan Kunci Berbasis Kata Sandi # 2). Metode ini membutuhkan Java 6. Kata sandi yang digunakan untuk menghasilkan SecretKey harus disimpan di tempat yang aman dan tidak dibagikan. Garam digunakan untuk mencegah serangan kamus terhadap kunci jika data terenkripsi Anda terganggu. Vektor inisialisasi acak 16-byte juga diterapkan sehingga setiap pesan terenkripsi unik.

Pandangan internal mengungkapkan struktur yang mirip dengan jawaban erickson .

Seperti yang disebutkan dalam pertanyaan, ini juga memerlukan Java Cryptography Extension (JCE) Kebijakan Yurisdiksi Kekuatan Tidak Terbatas (jika tidak Anda akan menemukan InvalidKeyException: Illegal Key Size). Ini dapat diunduh untuk Java 6 , Java 7 dan Java 8 .

Contoh penggunaan

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

Dan sampel output,

Garam: "feacbc02a3a697b0"
Teks asli: "* rahasia kerajaan *"
Teks terenkripsi: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 
Teks yang didekripsi: "* rahasia kerajaan *"
Sukses: teks yang didekripsi cocok
John McCarthy
sumber
Bisakah Anda menggunakan modul itu tanpa memuat semua Spring? Tampaknya file jar tidak tersedia untuk diunduh.
theglauber
5
@ theglauber Ya, Anda dapat menggunakan modul tanpa Spring Security atau kerangka kerja Spring. Dari melihat pom , dependensi runtime hanya adalah apache commons-logging 1.1.1 . Anda dapat menarik toples dengan maven atau mengunduhnya langsung dari repo biner resmi (lihat Unduhan binari Spring 4 untuk info lebih lanjut tentang binari Spring).
John McCarthy
1
Apakah mungkin untuk mengatur panjang kunci ke 128-bit? Memodifikasi folder keamanan di setiap PC bukanlah pilihan bagi saya.
IvanRF
1
@IvanRF maaf, tidak terlihat seperti itu. 256 adalah kode sulit dalam sumber
John McCarthy
2
Penggunaan NULL_IV_GENERATORoleh utilitas Spring tidak aman. Jika aplikasi tidak menyediakan infus, biarkan penyedia memilihnya, dan permintaan setelah inisialisasi.
erickson
32

Setelah membaca saran erickson, dan mendapatkan apa yang saya bisa dari beberapa posting lain dan contoh ini di sini , saya telah mencoba untuk memperbarui kode Doug dengan perubahan yang disarankan. Anda bebas mengedit untuk membuatnya lebih baik.

  • Inisialisasi Vektor tidak lagi diperbaiki
  • kunci enkripsi diturunkan menggunakan kode dari erickson
  • Garam 8 byte dihasilkan di setupEncrypt () menggunakan SecureRandom ()
  • kunci dekripsi dihasilkan dari garam enkripsi dan kata sandi
  • sandi dekripsi dihasilkan dari kunci dekripsi dan vektor inisialisasi
  • menghapus twiddling hex sebagai pengganti org.apache.commons rutinitas hex codec

Beberapa catatan: Ini menggunakan kunci enkripsi 128 bit - java tampaknya tidak akan melakukan enkripsi 256 bit out-of-the-box. Menerapkan 256 membutuhkan menginstal beberapa file tambahan ke direktori install java.

Juga, aku bukan orang yang suka bersembunyi. Menyimak.

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


}
wufoo
sumber
13
Ini pada dasarnya jawaban yang sama dengan yang dimiliki Erickson, dikelilingi oleh - bukan-yang-terprogram-baik-menurut-saya - bungkus. printStackTrace()
Maarten Bodewes
2
@owlstead - Ini adalah jawaban yang bagus. Ini menunjukkan cara mengenkripsi aliran dengan mengenkripsi byte byte, alih-alih memiliki semua yang ada di memori. Jawaban Erickson tidak akan berfungsi untuk file besar, yang tidak sesuai dengan memori. Jadi +1 ke wufoo. :)
dynamokaj
2
@dynamokaj Penggunaan CipherInputStreamdan CipherOutputStreamtidak banyak masalah. Mengocok semua pengecualian di bawah tabel adalah masalah. Fakta bahwa garam tiba-tiba telah menjadi ladang dan bahwa IV diperlukan adalah masalah. Fakta bahwa itu tidak mengikuti konvensi kode Java adalah masalah. Dan fakta bahwa ini hanya berfungsi pada file sementara tidak diminta adalah masalah. Dan bahwa sisa kode pada dasarnya adalah salinan juga tidak membantu. Tetapi mungkin saya akan mengubahnya untuk membuatnya lebih baik, seperti yang disarankan ...
Maarten Bodewes
@owlstead Saya setuju bahwa pengkodean bisa terlihat lebih baik. Saya telah memotongnya menjadi 1/4 atau sesuatu, tapi saya suka dia memperkenalkan saya ke CipherInputStream dan CipherOutputStream, karena hanya itulah yang saya butuhkan kemarin! ;)
dynamokaj
mengapa dua kali? fout.close (); fout.close ();
Marian Paździoch
7

Membuat kunci sendiri dari array byte mudah:

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

Tetapi membuat kunci 256-bit tidak cukup. Jika generator kunci tidak dapat menghasilkan kunci 256-bit untuk Anda, maka Cipherkelas mungkin juga tidak mendukung AES 256-bit. Anda mengatakan Anda memiliki patch yurisdiksi yang tidak terbatas yang diinstal, jadi cipher AES-256 harus didukung (tetapi kemudian kunci 256-bit juga harus, jadi ini mungkin masalah konfigurasi).

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

Solusi untuk kurangnya dukungan AES-256 adalah untuk mengambil beberapa implementasi AES-256 yang tersedia secara bebas, dan menggunakannya sebagai penyedia kustom. Ini melibatkan pembuatan Providersubclass Anda sendiri dan menggunakannya bersama Cipher.getInstance(String, Provider). Tapi ini bisa menjadi proses yang terlibat.

waqas
sumber
5
Anda harus selalu menunjukkan mode dan algoritma padding. Java menggunakan mode ECB yang tidak aman secara default.
Maarten Bodewes
Anda tidak dapat membuat penyedia Anda sendiri, penyedia harus ditandatangani (tidak percaya saya membaca kesalahan ini pada awalnya). Bahkan jika Anda bisa, pembatasan ukuran kunci adalah dalam implementasi Cipher, bukan pada penyedia itu sendiri. Anda dapat menggunakan AES-256 di Java 8 dan lebih rendah, tetapi Anda harus menggunakan API berpemilik. Atau runtime yang tidak menimbulkan batasan pada ukuran kunci tentunya.
Maarten Bodewes
Versi terbaru dari OpenJDK (dan Android) tidak memiliki batasan untuk menambahkan penyedia keamanan / crypto Anda sendiri. Tetapi Anda melakukannya dengan risiko Anda sendiri, tentu saja. Jika Anda lupa untuk selalu memperbarui perpustakaan Anda, Anda mungkin berisiko terhadap keamanan.
Maarten Bodewes
1
@ MaartenBodewes + OpenJDK tidak pernah memiliki masalah 'kebijakan kripto terbatas' di tempat pertama, dan Oracle JDK menghapusnya lebih dari setahun yang lalu untuk 8u161 dan 9 (dan mungkin beberapa versi sekarang-bayar-saja lebih rendah tapi saya belum memeriksanya)
dave_thompson_085
6

Apa yang telah saya lakukan di masa lalu adalah hash kunci melalui sesuatu seperti SHA256, kemudian ekstrak byte dari hash ke byte kunci [].

Setelah Anda memiliki byte [], Anda dapat melakukannya:

SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());
DarkSquid
sumber
12
Bagi yang lain: ini bukan metode yang sangat aman. Anda harus menggunakan PBKDF 2 yang ditentukan dalam PKCS # 5. Erickson berkata bagaimana melakukan ini di atas. Metode DarkSquid rentan terhadap serangan kata sandi dan juga tidak berfungsi kecuali ukuran plaintext Anda adalah kelipatan dari ukuran blok AES (128 bit) karena ia meninggalkan padding. Juga tidak menentukan mode; baca Mode Operasi Blok Cipher Wikipedia untuk masalah.
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)); Saya juga melakukan hal yang sama seperti yang disarankan dalam jawaban Anda, tetapi saya masih berakhir dengan java.security.InvalidKeyException ini: Ukuran kunci ilegal Apakah mengunduh file kebijakan JCE wajib?
Niranjan Subramanian
2
JANGAN GUNAKAN metode ini di semua jenis lingkungan produksi. Saat memulai dengan enkripsi Berbasis Kata Sandi, banyak pengguna kewalahan oleh dinding kode dan tidak mengerti bagaimana serangan kamus dan peretasan sederhana lainnya bekerja. Walaupun bisa membuat frustasi untuk belajar, itu adalah investasi yang berharga untuk meneliti ini. Inilah artikel pemula yang bagus: adambard.com/blog/3-wrong-ways-to-store-a-password
IcedDante
1

Menambahkan ke pengeditan @ Wufoo, versi berikut menggunakan InputStreams daripada file untuk membuat bekerja dengan berbagai file lebih mudah. Ini juga menyimpan IV dan Garam di awal file, sehingga hanya kata sandi yang perlu dilacak. Karena IV dan Garam tidak perlu dirahasiakan, ini membuat hidup sedikit lebih mudah.

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();
        }
    }
}
Doug
sumber
1
Solusi ini tampaknya menggunakan beberapa penanganan buffer yang canggung dan penanganan pengecualian yang benar-benar di bawah standar, pada dasarnya mencatat dan kemudian melupakannya. Berhati-hatilah bahwa menggunakan CBC tidak masalah untuk file tetapi tidak untuk keamanan transportasi. Menggunakan PBKDF2 dan AES tentu saja dapat dipertahankan, dalam arti itu mungkin merupakan dasar yang baik untuk solusi.
Maarten Bodewes
1

(Mungkin bermanfaat untuk orang lain dengan persyaratan serupa)

Saya memiliki persyaratan yang sama untuk menggunakan AES-256-CBCenkripsi dan dekripsi di Jawa.

Untuk mencapai (atau menentukan) enkripsi / dekripsi 256-byte, Java Cryptography Extension (JCE)kebijakan harus diatur ke"Unlimited"

Itu dapat diatur dalam java.securityfile di bawah $JAVA_HOME/jre/lib/security(untuk JDK) atau $JAVA_HOME/lib/security(untuk JRE)

crypto.policy=unlimited

Atau dalam kode as

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

Java 9 dan versi yang lebih baru mengaktifkannya secara default.

Praveen
sumber
0

Pertimbangkan untuk menggunakan Encryptor4j yang saya penulisnya.

Pertama-tama pastikan Anda memiliki file Kebijakan Yurisdiksi Kekuatan Tidak Terbatas yang diinstal sebelum melanjutkan sehingga Anda dapat menggunakan kunci AES 256-bit.

Kemudian lakukan hal berikut:

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

Anda sekarang dapat menggunakan enkripsi untuk mengenkripsi pesan Anda. Anda juga dapat melakukan enkripsi streaming jika mau. Secara otomatis menghasilkan dan menambahkan IV aman untuk kenyamanan Anda.

Jika ini adalah file yang ingin Anda kompres, lihat jawaban ini. Enkripsi file besar dengan AES menggunakan JAVA untuk pendekatan yang lebih sederhana.

whitebrow
sumber
2
Hai Martin, Anda harus selalu menunjukkan bahwa Anda adalah penulis perpustakaan jika Anda ingin menunjukkannya. Ada banyak sekali pembungkus kripto yang mencoba mempermudah. Apakah yang ini memiliki kertas keamanan atau telah menerima ulasan apa pun untuk menjadikannya layak untuk sementara?
Maarten Bodewes
-1

Gunakan kelas ini untuk enkripsi. Berhasil.

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

    }
}

Dan ini adalah ivBytes dan kunci acak;

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");
Insinyur
sumber
10
"Berhasil" .... ya, tetapi tidak memenuhi persyaratan untuk membuat solusi yang aman secara kriptografis (juga tidak memenuhi standar pengkodean Java sehubungan dengan penanganan pengecualian, menurut pendapat saya).
Maarten Bodewes
2
IV diinisialisasi ke nol. Cari serangan BEAST dan ACPA.
Michele Giuseppe Fadda
Pengecualian dari wazoo, metode menghasilkan kunci "acak", dan nol IV adalah masalah dengan implementasi ini, tetapi masalah-masalah itu sepele untuk diperbaiki. +1.
Phil