Java AES et utilisation de ma propre clé

88

Je souhaite crypter une chaîne en utilisant AES avec ma propre clé. Mais j'ai des problèmes avec la longueur en bits de la clé. Pouvez-vous revoir mon code et voir ce que j'ai besoin de corriger / modifier.

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

En ce moment, j'obtiens une exception " Longueur de clé AES non valide: 86 octets ". Dois-je remplir ma clé? Comment dois-je le faire?

Dois-je également définir quelque chose pour ECB ou CBC?

Merci

Bernie Perez
la source
6
Je trouve votre manque de sel aléatoire dérangeant . Maintenant sérieusement: dans le contexte de la cryptographie, SALT devrait être aléatoire
João Portela
16
Haha drôle. En fait, j'ai un sel aléatoire, mais j'ai nettoyé mon code pour rendre ma question plus claire. C'est pourquoi la variable s'appelle SALT2. Mais bonne référence pour les autres qui rencontrent ce même problème et aiment copier / coller du code.
Bernie Perez

Réponses:

125

Éditer:

Comme indiqué dans les commentaires, l'ancien code n'est pas une «meilleure pratique». Vous devez utiliser un algorithme de génération de clé comme PBKDF2 avec un nombre d'itérations élevé. Vous devez également utiliser au moins en partie un sel non statique (signifiant pour chaque "identité" exclusive). Si possible généré de manière aléatoire et stocké avec le texte chiffré.

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

===========

Ancienne réponse

Vous devez utiliser SHA-1 pour générer un hachage à partir de votre clé et réduire le résultat à 128 bits (16 octets).

De plus, ne générez pas de tableaux d'octets à partir de chaînes via getBytes (), il utilise le jeu de caractères par défaut de la plate-forme. Ainsi, le mot de passe "blaöä" donne un tableau d'octets différent sur différentes plates-formes.

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

Modifier: Si vous avez besoin de 256 bits comme tailles de clé, vous devez télécharger le lien de téléchargement Oracle "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" , utilisez SHA-256 comme hachage et supprimez la ligne Arrays.copyOf . "ECB" est le mode de chiffrement par défaut et "PKCS5Padding" le remplissage par défaut. Vous pouvez utiliser différents modes de chiffrement et modes de remplissage via la chaîne Cipher.getInstance en utilisant le format suivant: "Cipher / Mode / Padding"

Pour AES utilisant CTS et PKCS5Padding, la chaîne est: "AES / CTS / PKCS5Padding"

mknjc
la source
Cela fonctionnera, mais son hachage mon mot de passe, puis en utilisant uniquement les premiers bits. Il n'y a pas de meilleure façon de faire cela?
Bernie Perez
4
Il n'y a pas de meilleur moyen de générer la clé car AES a besoin d'une clé de 128/192/256 bits. Si vous ne hachez pas votre clé et ne coupez que l'entrée, il n'utilisera que les 16/24/32 premiers octets. Donc, générer un Hash est le seul moyen raisonnable.
mknjc
13
Notez que cette réponse n'utilise pas une bonne fonction de dérivation de clé et n'est donc pas aussi sûre qu'elle devrait l'être . Voir l' autre réponse pour une fonction de dérivation de clé légèrement obsolète - et malheureusement toujours un sel statique.
Maarten Bodewes du
2
Puis-je suggérer de supprimer cette réponse car c'est une très mauvaise pratique. Une fonction de dérivation de clé appropriée doit être utilisée - au moins PBKDF2.
Boris the Spider
1
Oui, la réponse est très mauvaise, comme l'a dit Maarten il y a des années. Veuillez vérifier cette réponse dans Cryptographie et fonction de dérivation de clé
kelalaka
14

Vous devez utiliser un KeyGenerator pour générer la clé,

Les longueurs de clé AES sont de 128, 192 et 256 bits selon le chiffrement que vous souhaitez utiliser.

Jetez un œil au tutoriel ici

Voici le code pour le cryptage basé sur le mot de passe, le mot de passe étant entré via System.in, vous pouvez le modifier pour utiliser un mot de passe stocké si vous le souhaitez.

        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
la source
3
Comment générer ma clé avec le mot de passe à l'aide du KeyGenerator? Je souhaite générer la même clé en fonction du mot de passe. Je peux donc déchiffrer la chaîne plus tard.
Bernie Perez
Ce dont vous parlez, c'est le cryptage basé sur un mot de passe et non AES. J'ai mis à jour ma réponse avec l'exemple de programme pour PBE
Keibosh
5
Essayez d'utiliser le générateur de clés PBEKDF2 à la place, en utilisant la chaîne «PBKDF2WithHmacSHA1» pour un SecretKeyFactorychiffrement plus à jour.
Maarten Bodewes
12
En fait, toutes les primitives cryptographiques utilisées dans cette réponse sont obsolètes , MD5 et DES bien sûr. Prenez garde.
Maarten Bodewes
MD5 et DES sont des suites de chiffrement faibles et devraient être
évitées
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
la source
5
Peut-être ajouter un texte d'explication supplémentaire.
DaGardner
Question, quel est l'intérêt d'avoir keyValue, avec le tableau d'octets? Je vois qu'il est utilisé pour fabriquer la clé, pourquoi? Peut-on faire quelque chose en utilisant comme à la SecretKeyplace? Si c'est le cas, comment?
Austin
@Mandrek, le contenu du fichier "clairxt.txt" sera chiffré. La logique ci-dessus crypte les données / messages dans le fichier qui est lu comme argument dans le constructeur FileReader.
Shankar Murthy
2

Cela fonctionnera.

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
la source
2

MD5, AES, sans rembourrage

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
la source
Comment créer une clé sécurisée comme SecretKeySpec en angulaire (ionique 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
la source