Java AES e usando minha própria chave

88

Quero criptografar uma string usando AES com minha própria chave. Mas estou tendo problemas com o comprimento de bits da chave. Você pode revisar meu código e ver o que preciso consertar / alterar.

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

No momento, recebo uma exceção " Comprimento de chave AES inválido: 86 bytes ". Eu preciso preencher minha chave? Como devo fazer isso?

Também preciso definir algo para ECB ou CBC?

obrigado

Bernie Perez
fonte
6
Acho que sua falta de sal aleatório é perturbadora . Falando sério: no contexto da criptografia o SALT deve ser aleatório
João Portela
16
Haha engraçado. Na verdade, tenho um salt aleatório, mas limpei meu código para deixar minha pergunta mais clara. É por isso que a variável é denominada SALT2. Mas uma boa referência para outras pessoas que se deparam com o mesmo problema e gostam de copiar / colar código.
Bernie Perez,

Respostas:

125

Editar:

Conforme escrito nos comentários, o código antigo não é a "melhor prática". Você deve usar um algoritmo de geração de chave como PBKDF2 com uma alta contagem de iteração. Você também deve usar pelo menos parcialmente um salt não estático (significado para cada "identidade" exclusiva). Se possível, gerado aleatoriamente e armazenado junto com o texto cifrado.

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

===========

Resposta Antiga

Você deve usar SHA-1 para gerar um hash de sua chave e cortar o resultado para 128 bits (16 bytes).

Além disso, não gere matrizes de bytes de Strings por meio de getBytes (), ele usa o Charset padrão da plataforma. Portanto, a senha "blaöä" resulta em uma matriz de bytes diferente em plataformas diferentes.

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

Editar: se você precisar de 256 bits como tamanhos de chave, deverá baixar o link de download do Oracle "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" , use SHA-256 como hash e remova a linha Arrays.copyOf . "ECB" é o modo de codificação padrão e "PKCS5Padding" o preenchimento padrão. Você pode usar diferentes modos de criptografia e modos de preenchimento por meio da string Cipher.getInstance usando o seguinte formato: "Cipher / Mode / Padding"

Para AES usando CTS e PKCS5Padding, a string é: "AES / CTS / PKCS5Padding"

mknjc
fonte
Isso vai funcionar, mas é hashing minha senha, então usando apenas os primeiros bits. Não há maneira melhor de fazer isso?
Bernie Perez
4
Não há maneira melhor de gerar a chave, pois o AES precisa de uma chave de 128/192/256 bits. Se você não fizer hash de sua chave e apenas cortar a entrada, ele usará apenas os primeiros 16/24/32 bytes. Portanto, gerar um Hash é a única maneira razoável.
mknjc de
13
Observe que esta resposta não usa uma boa função de derivação de chave e, portanto, não é tão segura quanto deveria ser . Veja a outra resposta para uma função de derivação de chave ligeiramente desatualizada - e infelizmente ainda um salt estático.
Maarten Bodewes de
2
Posso sugerir excluir esta resposta, pois é uma prática extremamente ruim. Uma função de derivação de chave apropriada deve ser usada - pelo menos PBKDF2.
Boris the Spider
1
Sim, a resposta é muito ruim, como disse Maarten anos atrás. Por favor, verifique esta resposta da função de criptografia e derivação de chave
kelalaka,
14

Você deve usar um KeyGenerator para gerar a chave,

Os comprimentos de chave AES são 128, 192 e 256 bits, dependendo da cifra que deseja usar.

Dê uma olhada no tutorial aqui

Aqui está o código para criptografia baseada em senha; a senha está sendo inserida através do System.in você pode alterá-la para usar uma senha armazenada, se desejar.

        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
fonte
3
Como faço para gerar minha chave com a senha usando o KeyGenerator? Quero gerar a mesma chave com base na senha. Assim, posso descriptografar a string mais tarde.
Bernie Perez
O que você está falando é sobre criptografia baseada em senha, não AES. Atualizei minha resposta com o programa de amostra para PBE
Keibosh
5
Tente usar o gerador de chave PBEKDF2, usando a string "PBKDF2WithHmacSHA1" para uma SecretKeyFactorycriptografia mais atualizada.
Maarten Bodewes
12
Na verdade, todas as primitivas criptográficas usadas nesta resposta estão desatualizadas , MD5 e DES com certeza. Fique atento.
Maarten Bodewes
MD5 e DES são
pacotes de
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
fonte
5
Talvez adicione mais algum texto explicativo.
DaGardner
Pergunta, qual é o ponto de ter keyValue, com a matriz de bytes? Eu vejo isso sendo usado para fazer a Chave, por quê? Algo pode ser feito usando like em SecretKeyvez disso? Se sim, como?
Austin,
@Mandrek, o conteúdo do arquivo "plaintext.txt" será criptografado. A lógica acima criptografa os dados / mensagem no arquivo que é lido como argumento no construtor FileReader.
Shankar Murthy
2

Isso vai funcionar.

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
fonte
2

MD5, AES, sem preenchimento

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
fonte
Como criar uma chave segura como SecretKeySpec no angular (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
fonte