Java AES dan menggunakan kunci saya sendiri

89

Saya ingin mengenkripsi string menggunakan AES dengan kunci saya sendiri. Tapi saya mengalami masalah dengan panjang bit kunci. Dapatkah Anda meninjau kode saya dan melihat apa yang perlu saya perbaiki / ubah.

public static void main(String[] args) throws Exception {
    String username = "[email protected]";
    String password = "Password1";
    String secretID = "BlahBlahBlah";
    String SALT2 = "deliciously salty";

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

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

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

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

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

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

Sekarang saya mendapatkan pengecualian " Panjang kunci AES tidak valid: 86 byte ". Apakah saya perlu memasukkan kunci saya? Bagaimana saya harus melakukannya?

Juga apakah saya perlu mengatur sesuatu untuk ECB atau CBC?

Terima kasih

Bernie Perez
sumber
6
Saya menemukan kurangnya garam acak mengganggu . Sekarang serius: dalam konteks kriptografi SALT harus acak
João Portela
16
Haha lucu. Saya sebenarnya memiliki garam acak, tetapi saya membersihkan kode saya untuk membuat pertanyaan saya lebih jelas. Karena itulah variabel tersebut dinamai SALT2. Tetapi referensi bagus untuk orang lain yang menemukan masalah yang sama ini dan suka menyalin / menempelkan kode.
Bernie Perez

Jawaban:

125

Edit:

Seperti yang tertulis di komentar, kode lama bukanlah "praktik terbaik". Anda harus menggunakan algoritma keygeneration seperti PBKDF2 dengan jumlah iterasi yang tinggi. Anda juga harus menggunakan setidaknya sebagian garam non-statis (artinya untuk setiap "identitas" eksklusif). Jika memungkinkan dibuat secara acak dan disimpan bersama dengan ciphertext.

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

===========

Jawaban Lama

Anda harus menggunakan SHA-1 untuk menghasilkan hash dari kunci Anda dan memangkas hasilnya menjadi 128 bit (16 byte).

Selain itu, jangan menghasilkan array byte dari Strings melalui getBytes (), ia menggunakan Charset default platform. Jadi kata sandi "blaöä" menghasilkan array byte yang berbeda pada platform yang berbeda.

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

Sunting: Jika Anda memerlukan 256 bit sebagai ukuran kunci, Anda perlu mengunduh tautan unduhan Oracle "Ekstensi Kriptografi Java (JCE) Unlimited Strength Jurisdiction" , gunakan SHA-256 sebagai hash dan hapus baris Arrays.copyOf . "ECB" adalah Mode Penyandian default dan "Padding PKCS5" adalah padding default. Anda dapat menggunakan Mode Penyandian dan Mode Pengisian yang berbeda melalui string Cipher.getInstance menggunakan format berikut: "Cipher / Mode / Padding"

Untuk AES yang menggunakan CTS dan PKCS5Padding, stringnya adalah: "AES / CTS / PKCS5Padding"

mknjc.dll
sumber
Ini akan berfungsi, tetapi hashing kata sandi saya, kemudian hanya menggunakan beberapa bit pertama. Tidak ada cara yang lebih baik untuk melakukan ini?
Bernie Perez
4
Tidak ada cara yang lebih baik untuk menghasilkan kunci karena AES membutuhkan kunci 128/192/256 bit. Jika Anda tidak mencirikan kunci Anda dan hanya memangkas input, itu hanya akan menggunakan 16/24/32 Bytes pertama. Jadi menghasilkan Hash adalah satu-satunya cara yang masuk akal.
mknjc
13
Perhatikan bahwa jawaban ini tidak menggunakan fungsi derivasi kunci yang baik dan oleh karena itu tidak seaman yang seharusnya . Lihat jawaban lain untuk fungsi derivasi kunci yang agak ketinggalan jaman - dan sayangnya masih merupakan garam statis.
Maarten Bodewes
2
Bolehkah saya menyarankan untuk menghapus jawaban ini karena ini adalah praktik yang sangat buruk. Fungsi derivasi kunci yang tepat harus digunakan - setidaknya PBKDF2.
Boris the Spider
1
Ya, jawabannya sangat buruk, seperti yang dikatakan Maarten beberapa tahun lalu. Silakan periksa jawaban ini dari Fungsi Kriptografi dan Penurunan Kunci
kelalaka
14

Anda harus menggunakan KeyGenerator untuk menghasilkan Kunci,

Panjang kunci AES adalah 128, 192, dan 256 bit tergantung pada sandi yang ingin Anda gunakan.

Simak tutorialnya di sini

Berikut adalah kode untuk Enkripsi Berbasis Kata Sandi, ini memiliki kata sandi yang dimasukkan melalui System.in Anda dapat mengubahnya untuk menggunakan kata sandi yang disimpan jika Anda mau.

        PBEKeySpec pbeKeySpec;
        PBEParameterSpec pbeParamSpec;
        SecretKeyFactory keyFac;

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

        // Iteration count
        int count = 20;

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

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

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

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

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

        // Encrypt the cleartext
        byte[] ciphertext = pbeCipher.doFinal(cleartext);
Keibosh
sumber
3
Bagaimana cara membuat Kunci saya dengan kata sandi menggunakan KeyGenerator? Saya ingin membuat kunci yang sama berdasarkan kata sandi. Jadi saya bisa mendekripsi string nanti.
Bernie Perez
Apa yang Anda bicarakan adalah enkripsi berbasis kata sandi, bukan AES. Saya memperbarui jawaban saya dengan program sampel untuk PBE
Keibosh
5
Coba dan gunakan pembuat kunci PBEKDF2 sebagai gantinya, gunakan string "PBKDF2WithHmacSHA1" untuk SecretKeyFactoryenkripsi yang lebih baru.
Maarten Bodewes
12
Sebenarnya semua primitif kriptografi yang digunakan dalam jawaban ini sudah ketinggalan zaman , MD5 dan DES pasti. Menyimak.
Maarten Bodewes
MD5 dan DES adalah suite penyandian yang lemah dan harus
DIHINDARI
6
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;
import java.io.BufferedReader;
import java.io.FileReader;

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

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

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

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

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

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

            System.out.println("Plain Text : " + plainText);
            System.out.println("Encrypted Text : " + encryptedText);
            System.out.println("Decrypted Text : " + decryptedText);
    }
}
Shankar Murthy
sumber
5
Mungkin tambahkan beberapa teks penjelasan lagi.
ChrisG
Pertanyaan, apa gunanya memiliki keyValue, dengan array byte? Saya melihatnya digunakan untuk membuat Kunci, oleh mengapa? Bisakah sesuatu dilakukan dengan menggunakan like SecretKey? Jika ya, bagaimana caranya?
Austin
@Mandrek, isi file "plaintext.txt" akan dienkripsi. Logika di atas mengenkripsi data / pesan dalam file yang dibaca sebagai argumen dalam konstruktor FileReader.
Shankar Murthy
2

Ini akan berhasil.

public class CryptoUtils {

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


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

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

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);

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

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

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

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

            return Base64Utils.encodeToString(outputBytes);

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


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

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

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

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

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

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

          } catch (Exception e) {

            e.printStackTrace();

          }
      return null;
      }
 }
Taran
sumber
2

MD5, AES, tanpa padding

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

public class PasswordUtils {

    private PasswordUtils() {}

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

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

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

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

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

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot decrypt", e);
        }
    }
}
Mike
sumber
Cara membuat kunci aman seperti SecretKeySpec dalam sudut (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();
sonnykwe
sumber