Java AES và sử dụng Khóa của riêng tôi


88

Tôi muốn mã hóa một chuỗi bằng AES với khóa của riêng mình. Nhưng tôi đang gặp sự cố với độ dài bit của khóa. Bạn có thể xem lại mã của tôi và xem những gì tôi cần sửa / thay đổi.

public static void main(String[] args) throws Exception {
    String username = "bob@google.org";
    String password = "Password1";
    String secretID = "BlahBlahBlah";
    String SALT2 = "deliciously salty";

    // Get the Key
    byte[] key = (SALT2 + username + password).getBytes();
    System.out.println((SALT2 + username + password).getBytes().length);

    // Need to pad key for AES
    // TODO: Best way?

    // Generate the secret key specs.
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

    // Instantiate the cipher
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

    byte[] encrypted = cipher.doFinal((secrectID).getBytes());
    System.out.println("encrypted string: " + asHex(encrypted));

    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    byte[] original = cipher.doFinal(encrypted);
    String originalString = new String(original);
    System.out.println("Original string: " + originalString + "\nOriginal string (Hex): " + asHex(original));
}

Ngay bây giờ tôi nhận được một ngoại lệ " Độ dài khóa AES không hợp lệ: 86 byte ". Tôi có cần gõ chìa khóa không? Tôi nên làm như thế nào?

Ngoài ra, tôi có cần thiết lập bất kỳ điều gì cho ECB hoặc CBC không?

Cảm ơn



16
Haha buồn cười thật. Tôi thực sự có một muối ngẫu nhiên, nhưng tôi đã xóa mã của mình để làm cho câu hỏi của tôi rõ ràng hơn. Đó là lý do tại sao biến được đặt tên là SALT2. Nhưng tham khảo tốt cho những người khác gặp phải vấn đề tương tự này và thích sao chép / dán mã.
Bernie Perez

Câu trả lời:


125

Biên tập:

Như đã viết trong các nhận xét, mã cũ không phải là "phương pháp hay nhất". Bạn nên sử dụng thuật toán tạo khóa như PBKDF2 với số lần lặp cao. Bạn cũng nên sử dụng ít nhất một phần muối không tĩnh (nghĩa là dành riêng cho mỗi "danh tính"). Nếu có thể được tạo ngẫu nhiên và lưu trữ cùng với bản mã.

    SecureRandom sr = SecureRandom.getInstanceStrong();
    byte[] salt = new byte[16];
    sr.nextBytes(salt);

    PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 128 * 8);
    SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec);
    Cipher aes = Cipher.getInstance("AES");
    aes.init(Cipher.ENCRYPT_MODE, key);

===========

Câu trả lời cũ

Bạn nên sử dụng SHA-1 để tạo mã băm từ khóa của mình và cắt kết quả thành 128 bit (16 byte).

Ngoài ra, không tạo mảng byte từ Chuỗi thông qua getBytes (), nó sử dụng Bộ mã mặc định của nền tảng. Vì vậy, mật khẩu "blaöä" dẫn đến các mảng byte khác nhau trên các nền tảng khác nhau.

byte[] key = (SALT2 + username + password).getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit

SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

Chỉnh sửa: Nếu bạn cần 256 bit làm kích thước khóa, bạn cần tải xuống liên kết tải xuống Oracle "Java Cryptography Extension (JCE) Unlimited Strength Jurisibility Policy Files" , sử dụng SHA-256 làm hàm băm và xóa dòng Arrays.copyOf . "ECB" là Chế độ mật mã mặc định và "PKCS5Padding" là phần đệm mặc định. Bạn có thể sử dụng các Chế độ mật mã và Chế độ đệm khác nhau thông qua chuỗi Cipher.getInstance bằng cách sử dụng định dạng sau: "Cipher / Mode / Padding"

Đối với AES sử dụng CTS và PKCS5Padding chuỗi là: "AES / CTS / PKCS5Padding"


Điều này sẽ hoạt động, nhưng nó băm mật khẩu của tôi, sau đó chỉ sử dụng một vài bit đầu tiên. Không có cách nào tốt hơn để làm điều này?
Bernie Perez

4
Không có cách nào tốt hơn để tạo khóa vì AES cần khóa 128/192/256 bit. Nếu bạn không băm khóa của mình và chỉ cắt bớt đầu vào, nó sẽ chỉ sử dụng 16/24/32 Byte đầu tiên. Vì vậy, tạo Hash là cách hợp lý duy nhất.
mknjc

13
Lưu ý rằng câu trả lời này không sử dụng một chức năng dẫn xuất khóa tốt và do đó không an toàn như nó cần . Xem câu trả lời khác cho một hàm dẫn xuất khóa hơi lỗi thời - và rất tiếc vẫn là một muối tĩnh.
Maarten Bodewes

2
Tôi có thể đề nghị xóa câu trả lời này vì nó là một thực hành cực kỳ tồi tệ. Một chức năng dẫn xuất khóa thích hợp nên được sử dụng - ít nhất là PBKDF2.
Boris the Spider

1
Có, câu trả lời là rất tệ, như Maarten đã nói nhiều năm trước. Vui lòng kiểm tra câu trả lời này từ Mật mã họcChức năng lấy ra khóa
kelalaka

14

Bạn nên sử dụng KeyGenerator để tạo Khóa,

Độ dài khóa AES là 128, 192 và 256 bit tùy thuộc vào mật mã bạn muốn sử dụng.

Hãy xem hướng dẫn tại đây

Đây là mã cho Mã hóa dựa trên mật khẩu, mật khẩu này được nhập thông qua System.in, bạn có thể thay đổi mã đó để sử dụng mật khẩu được lưu trữ nếu bạn muốn.

        PBEKeySpec pbeKeySpec;
        PBEParameterSpec pbeParamSpec;
        SecretKeyFactory keyFac;

        // Salt
        byte[] salt = {
            (byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
            (byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
        };

        // Iteration count
        int count = 20;

        // Create PBE parameter set
        pbeParamSpec = new PBEParameterSpec(salt, count);

        // Prompt user for encryption password.
        // Collect user password as char array (using the
        // "readPassword" method from above), and convert
        // it into a SecretKey object, using a PBE key
        // factory.
        System.out.print("Enter encryption password:  ");
        System.out.flush();
        pbeKeySpec = new PBEKeySpec(readPassword(System.in));
        keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

        // Create PBE Cipher
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");

        // Initialize PBE Cipher with key and parameters
        pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

        // Our cleartext
        byte[] cleartext = "This is another example".getBytes();

        // Encrypt the cleartext
        byte[] ciphertext = pbeCipher.doFinal(cleartext);

3
Làm cách nào để tạo Khóa của tôi bằng mật khẩu bằng KeyGenerator? Tôi muốn tạo cùng một khóa dựa trên mật khẩu. Vì vậy, tôi có thể giải mã chuỗi sau này.
Bernie Perez

Những gì bạn nói về là mã hóa dựa trên mật khẩu không phải AES. Tôi đã cập nhật câu trả lời của mình bằng chương trình mẫu cho PBE
Keibosh.

5
Hãy thử và sử dụng trình tạo khóa PBEKDF2 thay thế, sử dụng chuỗi "PBKDF2WithHmacSHA1" cho SecretKeyFactorymã hóa cập nhật hơn.
Maarten Bodewes

12
Trên thực tế, tất cả các nguyên mẫu mã hóa được sử dụng trong câu trả lời này đã lỗi thời , MD5 và DES chắc chắn. Hãy chú ý.
Maarten Bodewes

MD5 và DES là bộ mã hoá yếu và nên tránh
atom88

6
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;
import java.io.BufferedReader;
import java.io.FileReader;

public class AESFile 
{
private static String algorithm = "AES";
private static byte[] keyValue=new byte[] {'0','2','3','4','5','6','7','8','9','1','2','3','4','5','6','7'};// your key

    // Performs Encryption
    public static String encrypt(String plainText) throws Exception 
    {
            Key key = generateKey();
            Cipher chiper = Cipher.getInstance(algorithm);
            chiper.init(Cipher.ENCRYPT_MODE, key);
            byte[] encVal = chiper.doFinal(plainText.getBytes());
            String encryptedValue = new BASE64Encoder().encode(encVal);
            return encryptedValue;
    }

    // Performs decryption
    public static String decrypt(String encryptedText) throws Exception 
    {
            // generate key 
            Key key = generateKey();
            Cipher chiper = Cipher.getInstance(algorithm);
            chiper.init(Cipher.DECRYPT_MODE, key);
            byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedText);
            byte[] decValue = chiper.doFinal(decordedValue);
            String decryptedValue = new String(decValue);
            return decryptedValue;
    }

//generateKey() is used to generate a secret key for AES algorithm
    private static Key generateKey() throws Exception 
    {
            Key key = new SecretKeySpec(keyValue, algorithm);
            return key;
    }

    // performs encryption & decryption 
    public static void main(String[] args) throws Exception 
    {
        FileReader file = new FileReader("C://myprograms//plaintext.txt");
        BufferedReader reader = new BufferedReader(file);
        String text = "";
        String line = reader.readLine();
    while(line!= null)
        {
            text += line;
    line = reader.readLine();
        }
        reader.close();
    System.out.println(text);

            String plainText = text;
            String encryptedText = AESFile.encrypt(plainText);
            String decryptedText = AESFile.decrypt(encryptedText);

            System.out.println("Plain Text : " + plainText);
            System.out.println("Encrypted Text : " + encryptedText);
            System.out.println("Decrypted Text : " + decryptedText);
    }
}

5
Có thể thêm một số văn bản giải thích.
ChrisG

Câu hỏi, điểm có keyValue, với mảng byte là gì? Tôi thấy nó được sử dụng để làm chìa khóa, tại sao? Có thể làm gì đó bằng cách sử dụng like SecretKeythay thế không? Nếu vậy, làm thế nào?
Austin

@Mandrek, nội dung của tệp "plaintext.txt" sẽ được mã hóa. Logic ở trên mã hóa dữ liệu / thông báo trong tệp được đọc dưới dạng đối số trong phương thức khởi tạo FileReader.
Shankar Murthy

2

Điều này sẽ làm việc.

public class CryptoUtils {

    private  final String TRANSFORMATION = "AES";
    private  final String encodekey = "1234543444555666";
    public  String encrypt(String inputFile)
            throws CryptoException {
        return doEncrypt(encodekey, inputFile);
    }


    public  String decrypt(String input)
            throws CryptoException {
    // return  doCrypto(Cipher.DECRYPT_MODE, key, inputFile);
    return doDecrypt(encodekey,input);
    }

    private  String doEncrypt(String encodekey, String inputStr)   throws CryptoException {
        try {

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);

            byte[] key = encodekey.getBytes("UTF-8");
            MessageDigest sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16); // use only first 128 bit

            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

            byte[] inputBytes = inputStr.getBytes();     
            byte[] outputBytes = cipher.doFinal(inputBytes);

            return Base64Utils.encodeToString(outputBytes);

        } catch (NoSuchPaddingException | NoSuchAlgorithmException
                | InvalidKeyException | BadPaddingException
                | IllegalBlockSizeException | IOException ex) {
            throw new CryptoException("Error encrypting/decrypting file", ex);
       }
     }


    public  String doDecrypt(String encodekey,String encrptedStr) { 
          try {     

              Cipher dcipher = Cipher.getInstance(TRANSFORMATION);
              dcipher = Cipher.getInstance("AES");
              byte[] key = encodekey.getBytes("UTF-8");
              MessageDigest sha = MessageDigest.getInstance("SHA-1");
              key = sha.digest(key);
              key = Arrays.copyOf(key, 16); // use only first 128 bit

              SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

              dcipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            // decode with base64 to get bytes

              byte[] dec = Base64Utils.decode(encrptedStr.getBytes());  
              byte[] utf8 = dcipher.doFinal(dec);

              // create new string based on the specified charset
              return new String(utf8, "UTF8");

          } catch (Exception e) {

            e.printStackTrace();

          }
      return null;
      }
 }

2

MD5, AES, không có đệm

import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.apache.commons.io.Charsets.UTF_8;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class PasswordUtils {

    private PasswordUtils() {}

    public static String encrypt(String text, String pass) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(ENCRYPT_MODE, key);

            byte[] encrypted = cipher.doFinal(text.getBytes(UTF_8));
            byte[] encoded = Base64.getEncoder().encode(encrypted);
            return new String(encoded, UTF_8);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot encrypt", e);
        }
    }

    public static String decrypt(String text, String pass) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(DECRYPT_MODE, key);

            byte[] decoded = Base64.getDecoder().decode(text.getBytes(UTF_8));
            byte[] decrypted = cipher.doFinal(decoded);
            return new String(decrypted, UTF_8);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot decrypt", e);
        }
    }
}

Cách tạo khóa an toàn như SecretKeySpec trong góc (ionic 4);
Nitin Karale

0
    byte[] seed = (SALT2 + username + password).getBytes();
    SecureRandom random = new SecureRandom(seed);
    KeyGenerator generator;
    generator = KeyGenerator.getInstance("AES");
    generator.init(random);
    generator.init(256);
    Key keyObj = generator.generateKey();
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.