Java AES ve kendi Anahtarımı kullanma


89

AES kullanarak kendi anahtarımla bir dizeyi şifrelemek istiyorum. Ama anahtarın biraz uzunluğuyla ilgili sorun yaşıyorum. Kodumu gözden geçirip neyi düzeltmem / değiştirmem gerektiğini görebilir misiniz?

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

Şu anda " Geçersiz AES anahtar uzunluğu: 86 bayt " bir istisna alıyorum . Anahtarımı doldurmam gerekiyor mu? Nasıl yapmalıyım

Ayrıca ECB veya CBC için herhangi bir şey ayarlamam gerekir mi?

Teşekkürler



16
Haha komik. Aslında rastgele bir tuzum var, ancak sorumu daha net hale getirmek için kodumu temizledim. Bu nedenle değişkenin adı SALT2'dir. Ancak aynı sorunla karşılaşan ve kodu kopyalayıp yapıştırmak isteyenler için iyi bir referans.
Bernie Perez

Yanıtlar:


125

Düzenle:

Yorumlarda yazıldığı gibi eski kod "en iyi uygulama" değildir. Yüksek yineleme sayısına sahip PBKDF2 gibi bir anahtar oluşturma algoritması kullanmalısınız. Ayrıca, en azından kısmen statik olmayan (her "kimlik" hariç) tuz kullanmalısınız. Mümkünse, şifreli metinle birlikte rastgele oluşturulur ve saklanır.

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

===========

Eski Cevap

Anahtarınızdan bir karma oluşturmak ve sonucu 128 bit'e (16 bayt) kırpmak için SHA-1'i kullanmalısınız.

Ayrıca getBytes () aracılığıyla Strings'den bayt dizileri üretmeyin , platform varsayılan Karakter Kümesini kullanır. Dolayısıyla "blaöä" şifresi, farklı platformlarda farklı bayt dizileri ile sonuçlanır.

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

Düzen: Anahtar boyutları olarak 256 bit'e ihtiyacınız varsa, "Java Şifreleme Uzantısı (JCE) Sınırsız Güç Yargı İlkesi Dosyaları" Oracle indirme bağlantısını indirmeniz gerekir , karma olarak SHA- 256'yı kullanın ve Arrays.copyOf satırını kaldırın . "ECB" varsayılan Şifreleme Modudur ve "PKCS5Padding" varsayılan dolgudır. Aşağıdaki biçimi kullanarak Cipher.getInstance dizesi aracılığıyla farklı Cipher Modları ve Padding Modları kullanabilirsiniz : "Cipher / Mode / Padding"

CTS ve PKCS5Padding kullanan AES için dize: "AES / CTS / PKCS5Padding"


Bu işe yarayacak, ancak şifremi hash ediyor, sonra sadece ilk birkaç biti kullanarak. Bunu yapmanın daha iyi bir yolu yok mu?
Bernie Perez

4
Anahtar oluşturmanın daha iyi bir yolu yoktur, çünkü AES'in 128/192/256 bitlik bir anahtara ihtiyacı vardır. Anahtarınıza hashing uygulamazsanız ve yalnızca girdiyi kırparsanız, yalnızca ilk 16/24/32 Byte'ı kullanır. Dolayısıyla, bir Hash oluşturmak tek mantıklı yoldur.
mknjc

13
Bu cevabın iyi bir anahtar türetme işlevi kullanmadığını ve bu nedenle olması gerektiği kadar güvenli olmadığını unutmayın . Biraz güncel olmayan bir anahtar türetme işlevi için diğer yanıta bakın - ve ne yazık ki hala statik bir tuz.
Maarten Bodewes

2
Son derece kötü bir uygulama olduğu için bu cevabı silmenizi önerebilir miyim ? Uygun bir anahtar türetme işlevi kullanılmalıdır - en azından PBKDF2.
Örümcek Boris

1
Evet, Maarten'in yıllar önce söylediği gibi cevap çok kötü. Lütfen Kriptografi ve Anahtar Türetme Fonksiyonu'ndaki
kelalaka

14

Anahtarı oluşturmak için bir KeyGenerator kullanmalısınız,

AES anahtar uzunlukları, kullanmak istediğiniz şifreye bağlı olarak 128, 192 ve 256 bittir.

Buradaki eğiticiye bir göz atın

İşte Parola Tabanlı Şifreleme için kod, burada System.in üzerinden girilen parola var, isterseniz bunu kayıtlı bir parola kullanmak için değiştirebilirsiniz.

        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
Anahtarımı KeyGenerator kullanarak şifre ile nasıl oluşturabilirim? Şifreye bağlı olarak aynı anahtarı oluşturmak istiyorum. Böylece dizenin şifresini daha sonra çözebilirim.
Bernie Perez

Bahsettiğiniz şey parola tabanlı şifrelemedir, AES değil. Cevabımı PBE için örnek programla güncelledim
Keibosh

5
Daha SecretKeyFactorygüncel şifreleme için "PBKDF2WithHmacSHA1" dizesini kullanarak PBEKDF2 anahtar oluşturucuyu deneyin ve kullanın .
Maarten Bodewes

12
Aslında bu cevapta kullanılan tüm şifreleme ilkelleri güncelliğini yitirmiş , MD5 ve DES kesinlikle. Dikkat etmek.
Maarten Bodewes

MD5 ve DES zayıf şifreleme paketleridir ve KAÇINILMALIDIR
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
Belki biraz daha açıklama metni ekleyin.
ChrisG

Soru, keyValuebayt dizisine sahip olmanın anlamı nedir? Anahtarı yapmak için kullanıldığını görüyorum, neden? SecretKeyBunun yerine gibi kullanılarak bir şey yapılabilir mi? Öyleyse nasıl?
Austin

@Mandrek, "plaintext.txt" dosyasının içeriği şifrelenecektir. Yukarıdaki mantık, FileReader yapıcısında bağımsız değişken olarak okunan dosyadaki verileri / mesajı şifreler.
Shankar Murthy

2

Bu işe yarayacak.

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, dolgu yok

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

SecretKeySpec gibi açısal (ionic 4) güvenli anahtar nasıl oluşturulur;
Nitin Karale 07

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();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.