Mã hóa dựa trên mật khẩu AES 256-bit AES


390

Tôi cần triển khai mã hóa AES 256 bit, nhưng tất cả các ví dụ tôi tìm thấy trực tuyến đều sử dụng "KeyGenerator" để tạo khóa 256 bit, nhưng tôi muốn sử dụng mật mã của riêng mình. Làm thế nào tôi có thể tạo khóa riêng của mình? Tôi đã thử đệm nó lên 256 bit, nhưng sau đó tôi gặp lỗi khi nói rằng khóa quá dài. Tôi đã cài đặt bản vá quyền hạn không giới hạn, vì vậy đó không phải là vấn đề :)

I E. KeyGenerator trông như thế này ...

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

Mã lấy từ đây

BIÊN TẬP

Tôi thực sự đã đệm mật khẩu lên 256 byte, không phải bit, quá dài. Sau đây là một số mã tôi đang sử dụng mà tôi có thêm một số kinh nghiệm với điều này.

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)

Các bit "TODO" bạn cần tự làm :-)


Bạn có thể làm rõ: việc gọi kgen.init (256) có hoạt động không?
Mitch Wheat

2
Có, nhưng điều này sẽ tự động tạo khóa ... nhưng vì tôi muốn mã hóa dữ liệu giữa hai nơi, tôi cần biết trước khóa, vì vậy tôi cần chỉ định một khóa thay vì "tạo" một. Tôi có thể chỉ định một 16bit hoạt động cho mã hóa 128 bit hoạt động. Tôi đã thử một bản 32 bit để mã hóa 256 bit, nhưng nó không hoạt động như mong đợi.
Nippysaurus

4
Nếu tôi hiểu chính xác, bạn đang cố gắng sử dụng khóa 256 bit được sắp xếp trước, được chỉ định, ví dụ, như một mảng byte. Nếu vậy, cách tiếp cận của DarkSquid bằng SecretKeySpec sẽ hoạt động. Cũng có thể lấy khóa AES từ mật khẩu; nếu đó là những gì bạn đang theo đuổi, xin vui lòng cho tôi biết, và tôi sẽ chỉ cho bạn cách chính xác để làm điều đó; chỉ đơn giản là băm mật khẩu không phải là cách tốt nhất.
erickson

Hãy cẩn thận về việc đệm một số, bạn có thể làm cho AES của bạn kém an toàn hơn.
Joshua

1
@erickson: đó là những gì tôi cần làm (lấy khóa AES từ mật khẩu).
Nippysaurus

Câu trả lời:


476

Chia sẻ password(a char[]) và salt(một byte[]byte8 byte được chọn bởi một SecureRandomloại muối tốt mà không cần phải giữ bí mật) với người nhận ngoài băng tần. Sau đó, để có được một chìa khóa tốt từ thông tin này:

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

Các số ma thuật (có thể được định nghĩa là hằng số ở đâu đó) 65536 và 256 lần lượt là số lần lặp đạo hàm chính và kích thước khóa.

Hàm phái sinh chính được lặp đi lặp lại để yêu cầu nỗ lực tính toán đáng kể và điều đó ngăn kẻ tấn công nhanh chóng thử nhiều mật khẩu khác nhau. Số lần lặp có thể được thay đổi tùy thuộc vào tài nguyên tính toán có sẵn.

Kích thước khóa có thể giảm xuống còn 128 bit, vẫn được coi là mã hóa "mạnh", nhưng nó không mang lại nhiều giới hạn an toàn nếu các cuộc tấn công được phát hiện làm suy yếu AES.

Được sử dụng với chế độ chuỗi khối thích hợp, cùng một khóa dẫn xuất có thể được sử dụng để mã hóa nhiều tin nhắn. Trong Chuỗi khối mã hóa (CBC) , một vectơ khởi tạo ngẫu nhiên (IV) được tạo cho mỗi thông báo, mang lại văn bản mã hóa khác nhau ngay cả khi văn bản thuần túy giống hệt nhau. CBC có thể không phải là chế độ an toàn nhất có sẵn cho bạn (xem AEAD bên dưới); có nhiều chế độ khác với các thuộc tính bảo mật khác nhau, nhưng tất cả chúng đều sử dụng một đầu vào ngẫu nhiên tương tự. Trong mọi trường hợp, đầu ra của mỗi hoạt động mã hóa là văn bản mã hóa vectơ khởi tạo:

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

Lưu trữ ciphertextiv. Khi giải mã, dữ liệu SecretKeyđược tạo lại theo cách chính xác, sử dụng mật khẩu có cùng tham số muối và lặp. Khởi tạo mật mã bằng khóa này vectơ khởi tạo được lưu trữ với thông báo:

/* 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 bao gồm hỗ trợ API cho các chế độ mã hóa AEAD và nhà cung cấp "SunJCE" đi kèm với các bản phân phối OpenJDK và Oracle thực hiện các khởi đầu này với Java 8. Một trong những chế độ này được khuyến nghị mạnh mẽ thay cho CBC; nó sẽ bảo vệ tính toàn vẹn của dữ liệu cũng như quyền riêng tư của họ.


Một java.security.InvalidKeyExceptionvới thông điệp "bất hợp pháp kích thước chìa khóa hoặc mặc định thông số" có nghĩa rằng sức mạnh mật mã được giới hạn; các tệp chính sách quyền lực pháp lý không giới hạn không ở đúng vị trí. Trong một JDK, chúng nên được đặt dưới${jdk}/jre/lib/security

Dựa trên mô tả sự cố, có vẻ như các tệp chính sách không được cài đặt chính xác. Các hệ thống có thể dễ dàng có nhiều thời gian chạy Java; kiểm tra kỹ để đảm bảo rằng vị trí chính xác đang được sử dụng.


29
@Nick: Đọc PKCS # 5. Các muối là cần thiết cho PBKDF2, đó là lý do tại sao API cho mã hóa dựa trên mật khẩu yêu cầu chúng làm đầu vào cho dẫn xuất chính. Không có muối, một cuộc tấn công từ điển có thể được sử dụng, cho phép một danh sách được tính toán trước của các khóa mã hóa đối xứng có khả năng nhất. IV mật mã và muối dẫn xuất khóa phục vụ các mục đích khác nhau. IV cho phép một người sử dụng lại cùng một khóa cho nhiều tin nhắn. Muối ngăn chặn các cuộc tấn công từ điển vào chìa khóa.
erickson

2
Đầu tiên, đó sẽ là mã hóa DES, không phải AES. Hầu hết các nhà cung cấp không có hỗ trợ tốt cho các PBEwith<prf>and<encryption>thuật toán; ví dụ, SunJCE không cung cấp và PBE cho AES. Thứ hai, cho phép jasypt là một mục tiêu không. Một gói có ý định cung cấp bảo mật mà không đòi hỏi sự hiểu biết về các nguyên tắc cơ bản có vẻ nguy hiểm prima facie.
erickson

6
Tôi đã triển khai câu trả lời của @ erickson dưới dạng một lớp: github.com/mrclay/jSecureEdit/tree/master/src/org/mrclay/crypto (PBE thực hiện công việc, PBEStorage là một đối tượng giá trị để lưu trữ IV / mật mã cùng nhau.)
Steve Clay

3
@AndyNuss Ví dụ này dành cho mã hóa đảo ngược, thường không được sử dụng cho mật khẩu. Bạn có thể sử dụng dẫn xuất khóa PBKDF2 để "băm" mật khẩu một cách an toàn. Điều đó có nghĩa là trong ví dụ trên, bạn sẽ lưu trữ kết quả tmp.getEncoded()là hàm băm. Bạn cũng nên lưu trữ saltvà các lần lặp (65536 trong ví dụ này) để bạn có thể tính toán lại hàm băm khi ai đó cố gắng xác thực. Trong trường hợp này, tạo muối bằng trình tạo số ngẫu nhiên mã hóa mỗi lần thay đổi mật khẩu.
erickson

6
Để chạy mã này, hãy đảm bảo bạn có đúng Tệp chính sách quyền lực không giới hạn trong JRE của bạn như được nêu trong ngs.ac.uk/tools/jIGHolicyfiles
Amir Moghimi

75

Cân nhắc sử dụng Mô-đun mã hóa bảo mật mùa xuân

Mô-đun bảo mật Spring Security cung cấp hỗ trợ cho mã hóa đối xứng, tạo khóa và mã hóa mật khẩu. Mã được phân phối như một phần của mô-đun lõi nhưng không phụ thuộc vào bất kỳ mã Spring Security (hoặc Spring) nào khác.

Nó cung cấp một sự trừu tượng hóa đơn giản để mã hóa và dường như khớp với những gì được yêu cầu ở đây,

Phương thức mã hóa "tiêu chuẩn" là AES 256 bit sử dụng PBKDF2 của PKCS # 5 (Hàm dẫn xuất khóa dựa trên mật khẩu # 2). Phương pháp này yêu cầu Java 6. Mật khẩu được sử dụng để tạo SecretKey phải được giữ ở nơi an toàn và không được chia sẻ. Muối được sử dụng để ngăn chặn các cuộc tấn công từ điển chống lại khóa trong trường hợp dữ liệu được mã hóa của bạn bị xâm phạm. Một vectơ khởi tạo ngẫu nhiên 16 byte cũng được áp dụng để mỗi thông điệp được mã hóa là duy nhất.

Nhìn vào phần bên trong cho thấy một cấu trúc tương tự như câu trả lời của erickson .

Như đã lưu ý trong câu hỏi, điều này cũng đòi hỏi Chính sách tài phán về sức mạnh không giới hạn của mã hóa Java (JCE) (nếu không bạn sẽ gặp phải InvalidKeyException: Illegal Key Size). Có thể tải xuống cho Java 6 , Java 7Java 8 .

Ví dụ sử dụng

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

Và đầu ra mẫu,

Muối: "feacbc02a3a697b0"
Văn bản gốc: "* bí mật hoàng gia *"
Văn bản được mã hóa: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 
Văn bản được giải mã: "* bí mật hoàng gia *"
Thành công: phù hợp với văn bản được giải mã

Bạn có thể sử dụng mô-đun đó mà không tải tất cả Spring? Họ dường như không có sẵn các tệp jar để tải xuống.
theglauber

5
@theglauber Có, bạn có thể sử dụng mô-đun mà không cần Spring Security hoặc khung công tác Spring. Từ việc nhìn vào pom , sự phụ thuộc thời gian chạy duy nhất là apache commons-log 1.1.1 . Bạn có thể kéo vào jar bằng maven hoặc tải xuống trực tiếp từ repo nhị phân chính thức (xem phần tải xuống nhị phân của Spring 4 để biết thêm thông tin về nhị phân Spring).
John McCarthy

1
Có thể đặt độ dài khóa thành 128 bit không? Sửa đổi thư mục bảo mật trong mọi PC không phải là một lựa chọn cho tôi.
IvanRF

1
@IvanRF xin lỗi, không giống như vậy. 256 được mã hóa cứng trong nguồn
John McCarthy

2
Việc NULL_IV_GENERATORsử dụng bởi tiện ích Spring không an toàn. Nếu ứng dụng không cung cấp IV, hãy để nhà cung cấp chọn nó và truy vấn nó sau khi khởi tạo.
erickson

32

Sau khi đọc qua các đề xuất của erickson và lượm lặt những gì tôi có thể từ một vài bài đăng khác và ví dụ này ở đây , tôi đã cố gắng cập nhật mã của Doug với các thay đổi được đề xuất. Hãy chỉnh sửa để làm cho nó tốt hơn.

  • Vector khởi tạo không còn cố định
  • Khóa mã hóa được lấy bằng mã từ erickson
  • Muối 8 byte được tạo trong setupEncrypt () bằng SecureRandom ()
  • Khóa giải mã được tạo từ muối và mật khẩu
  • mật mã giải mã được tạo ra từ khóa giải mã và vectơ khởi tạo
  • đã xóa hex twiddling thay cho org.apache.commons codec Hex

Một số lưu ý: Điều này sử dụng khóa mã hóa 128 bit - java dường như sẽ không thực hiện mã hóa 256 bit ngoài hộp. Việc triển khai 256 yêu cầu cài đặt một số tệp bổ sung vào thư mục cài đặt java.

Ngoài ra, tôi không phải là người mật mã. Hãy chú ý.

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
Về cơ bản, đây là câu trả lời tương tự như của Erickson, được bao quanh bởi một trình bao bọc - không được lập trình tốt theo ý kiến ​​của tôi -. printStackTrace()
Maarten Bodewes

2
@owlstead - Đây là một câu trả lời tuyệt vời. Nó chỉ ra cách mã hóa một luồng bằng cách mã hóa bộ đệm byte, thay vì có mọi thứ trong bộ nhớ. Câu trả lời của Erickson sẽ không hoạt động đối với các tệp lớn, không phù hợp với bộ nhớ. Vì vậy, +1 để wufoo. :)
điện

2
@dynamokaj Việc sử dụng CipherInputStreamCipherOutputStreamkhông có nhiều vấn đề. Xáo trộn tất cả các ngoại lệ dưới bàn là một vấn đề. Việc muối đột nhiên trở thành một lĩnh vực và IV được yêu cầu là một vấn đề. Thực tế là nó không tuân theo các quy ước mã hóa Java là một vấn đề. Và thực tế là điều này chỉ hoạt động trên các tập tin trong khi nó không được yêu cầu là một vấn đề. Và phần còn lại của mã về cơ bản là một bản sao cũng không giúp được gì. Nhưng có lẽ tôi sẽ điều chỉnh nó để làm cho nó tốt hơn, như được đề xuất ...
Maarten Bodewes

Tài khoản ;)
điện

Tại sao hai lần? fout.c Đóng (); fout.c Đóng ();
Mary Paździoch

7

Tạo khóa riêng của bạn từ một mảng byte rất dễ dàng:

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

Nhưng việc tạo khóa 256 bit là không đủ. Nếu trình tạo khóa không thể tạo khóa 256 bit cho bạn, thì Cipherlớp đó có thể không hỗ trợ AES 256-bit. Bạn nói rằng bạn đã cài đặt bản vá quyền hạn không giới hạn, do đó, mật mã AES-256 phải được hỗ trợ (nhưng các khóa 256 bit cũng vậy, vì vậy đây có thể là vấn đề về cấu hình).

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

Một cách giải quyết cho việc thiếu hỗ trợ AES-256 là thực hiện một số triển khai AES-256 miễn phí và sử dụng nó như một nhà cung cấp tùy chỉnh. Điều này liên quan đến việc tạo Providerlớp con của riêng bạn và sử dụng nó với Cipher.getInstance(String, Provider). Nhưng đây có thể là một quá trình liên quan.


5
Bạn phải luôn luôn chỉ ra chế độ và thuật toán đệm. Java sử dụng chế độ ECB không an toàn theo mặc định.
Maarten Bodewes

Bạn không thể tạo nhà cung cấp của riêng mình, nhà cung cấp phải được ký (ban đầu tôi không thể đọc được lỗi này). Ngay cả khi bạn có thể, việc hạn chế kích thước khóa nằm ở việc triển khai Cipherchứ không phải ở chính nhà cung cấp. Bạn có thể sử dụng AES-256 trong Java 8 trở xuống, nhưng bạn cần sử dụng API độc quyền. Hoặc một thời gian chạy không đặt ra các hạn chế về kích thước khóa của khóa học.
Maarten Bodewes

Các phiên bản gần đây của OpenJDK (và Android) không có hạn chế về việc thêm nhà cung cấp bảo mật / tiền điện tử của riêng bạn. Nhưng bạn làm như vậy có nguy cơ của riêng bạn, tất nhiên. Nếu bạn quên cập nhật thư viện của mình, bạn có thể gặp rủi ro về bảo mật.
Maarten Bodewes 24/03/19

1
@ MaartenBodewes + OpenJDK chưa bao giờ gặp phải vấn đề 'chính sách tiền điện tử hạn chế' và Oracle JDK đã gỡ bỏ nó hơn một năm trước cho 8u161 và 9 trở lên (và có thể một số phiên bản chỉ trả tiền thấp hơn nhưng tôi chưa kiểm tra các phiên bản đó)
dave_thndry_085

6

Những gì tôi đã làm trong quá khứ là băm khóa thông qua một cái gì đó như SHA256, sau đó trích xuất các byte từ hàm băm vào byte khóa [].

Sau khi bạn có byte [], bạn có thể thực hiện:

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

12
Đối với những người khác: đây không phải là một phương pháp rất an toàn. Bạn nên sử dụng PBKDF 2 được chỉ định trong PKCS # 5. erickson cho biết làm thế nào để làm điều này ở trên. Phương pháp của DarkSquid dễ bị tấn công bằng mật khẩu và cũng không hoạt động trừ khi kích thước của bản rõ của bạn là bội số của kích thước khối AES (128 bit) do anh ta bỏ đệm. Ngoài ra, nó không chỉ định chế độ; đọc các chế độ hoạt động mã hóa khối của Wikipedia vì lo ngại.
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)); Tôi cũng đang làm như đề xuất trong câu trả lời của bạn nhưng tôi vẫn kết thúc với java.security.InvalidKeyException: Kích thước khóa bất hợp pháp Việc tải xuống tệp chính sách JCE có bắt buộc không?
Niranjan Subramanian

2
KHÔNG SỬ DỤNG phương pháp này trong bất kỳ loại môi trường sản xuất nào. Khi bắt đầu với mã hóa dựa trên mật khẩu, rất nhiều người dùng bị choáng ngợp bởi các bức tường mã và không hiểu cách thức tấn công từ điển và các cách hack đơn giản khác. Mặc dù có thể nản lòng khi học, nhưng đây là một khoản đầu tư đáng giá để nghiên cứu vấn đề này. Đây là một bài viết tốt cho người mới bắt đầu: adambard.com/blog/3-wrong-ways-to-store-a-password
IcedDante

1

Thêm vào các chỉnh sửa của @ Wufoo, phiên bản sau sử dụng InputStreams thay vì các tệp để làm việc với nhiều tệp dễ dàng hơn. Nó cũng lưu trữ IV và Salt trong phần đầu của tệp, làm cho nó chỉ cần theo dõi mật khẩu. Vì IV và Salt không cần phải bí mật, điều này làm cho cuộc sống dễ dàng hơn một chút.

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
Giải pháp này dường như sử dụng một số xử lý bộ đệm vụng về và xử lý ngoại lệ hoàn toàn phụ, về cơ bản là ghi nhật ký và sau đó quên chúng. Được cảnh báo rằng sử dụng CBC là OK cho các tệp nhưng không phải để bảo mật vận chuyển. Sử dụng PBKDF2 và AES tất nhiên có thể được bảo vệ, theo nghĩa đó, nó có thể là một nền tảng tốt cho một giải pháp.
Maarten Bodewes

1

(Có thể hữu ích cho những người khác có yêu cầu tương tự)

Tôi có một yêu cầu tương tự để sử dụng AES-256-CBCmã hóa và giải mã trong Java.

Để đạt được (hoặc chỉ định) mã hóa / giải mã 256 byte, Java Cryptography Extension (JCE)chính sách nên được đặt thành"Unlimited"

Nó có thể được đặt trong java.securitytệp bên dưới $JAVA_HOME/jre/lib/security(đối với JDK) hoặc $JAVA_HOME/lib/security(đối với JRE)

crypto.policy=unlimited

Hoặc trong mã như

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

Java 9 và các phiên bản mới hơn được bật theo mặc định.


0

Cân nhắc sử dụng Encryptor4j mà tôi là tác giả.

Trước tiên, hãy đảm bảo bạn đã cài đặt các tệp Chính sách thẩm quyền cường độ không giới hạn trước khi tiến hành để bạn có thể sử dụng các khóa AES 256 bit.

Sau đó làm như sau:

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

Bây giờ bạn có thể sử dụng bộ mã hóa để mã hóa tin nhắn của bạn. Bạn cũng có thể thực hiện mã hóa phát trực tuyến nếu bạn muốn. Nó tự động tạo và chuẩn bị IV an toàn để thuận tiện cho bạn.

Nếu đó là một tệp mà bạn muốn nén, hãy xem câu trả lời này Mã hóa một tệp lớn bằng AES bằng JAVA để có cách tiếp cận thậm chí đơn giản hơn.


2
Xin chào Martin, bạn phải luôn chỉ ra rằng bạn là nhà văn của thư viện nếu bạn muốn chỉ ra nó. Có vô số các trình bao bọc tiền điện tử đang cố gắng làm cho mọi thứ trở nên dễ dàng. Cái này có một tờ giấy bảo mật hay nó đã nhận được bất kỳ đánh giá nào để làm cho nó có giá trị trong thời gian của chúng tôi?
Maarten Bodewes 24/03/19

-1

Sử dụng lớp này để mã hóa. Nó hoạt động.

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

    }
}

Và đây là ivBytes và một khóa ngẫu nhiên;

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
"nó hoạt động" .... có, nhưng theo tôi, nó không đáp ứng các yêu cầu để tạo ra một giải pháp bảo mật bằng mật mã (cũng không đáp ứng các tiêu chuẩn mã hóa Java liên quan đến xử lý ngoại lệ, theo ý kiến ​​của tôi).
Maarten Bodewes

2
IV được khởi tạo về không. Tìm kiếm các cuộc tấn công BEAST và ACPA.
Michele Giuseppe Fadda

Ngoại lệ cho wazoo, phương pháp tạo khóa "ngẫu nhiên" và zero IV là một vấn đề với việc triển khai này, nhưng những vấn đề đó không đáng kể để khắc phục. +1.
Phil
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.