web-dev-qa-db-fra.com

Cryptage et décryptage Java AES

Je voudrais chiffrer et déchiffrer un mot de passe en utilisant le chiffrement AES 128 bits avec une clé de 16 octets. Je reçois une erreur javax.crypto.BadPaddingException lors du déchiffrement de la valeur. Est-ce que je manque quelque chose pendant le décryptage?

public static void main(String args[]) {
    Test t = new Test();
    String encrypt = new String(t.encrypt("mypassword"));
    System.out.println("decrypted value:" + t.decrypt("ThisIsASecretKey", encrypt));
}

public String encrypt(String value) {
    try {
        byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'};
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(value.getBytes());
        System.out.println("encrypted string:" + (new String(encrypted)));
        return new String(skeySpec.getEncoded());
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

public String decrypt(String key, String encrypted) {
    try {
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(skeySpec.getEncoded(), "AES"));
        //getting error here
        byte[] original = cipher.doFinal(encrypted.getBytes());
        return new String(original);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}  

Message d'erreur

encrypted string:�Bj�.�Ntk�F�`�
encrypted key:ThisIsASecretKey
decrypted value:null
May 25, 2012 12:54:02 PM bean.Test decrypt
SEVERE: null
javax.crypto.BadPaddingException: Given final block not properly padded
at com.Sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.Sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.Sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at bean.Test.decrypt(Test.Java:55)
at bean.Test.main(Test.Java:24)

Enfin, j'utilise la solution suivante basée sur la réponse @QuantumMechanic

public class Test {

public String encryptionKey;

public static void main(String args[]) {
    Test t = new Test();
    String encrypt = t.encrypt("mypassword");
    System.out.println("decrypted value:" + t.decrypt(t.encryptionKey, encrypt));
}

public String encrypt(String value) {
    try {
        // Get the KeyGenerator
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(256);
        // Generate the secret key specs.
        SecretKey skey = kgen.generateKey();
        byte[] raw = skey.getEncoded();
        String key = new Base64().encodeAsString(raw);
        this.encryptionKey = key;
        System.out.println("------------------Key------------------");
        System.out.println(key);
        System.out.println("--------------End of Key---------------");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        String encrypt = (new Base64()).encodeAsString(cipher.doFinal(value.getBytes()));
        System.out.println("encrypted string:" + encrypt);
        return encrypt;
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

public String decrypt(String key, String encrypted) {
    try {
        Key k = new SecretKeySpec(Base64.getDecoder().decode(key), "AES");
        Cipher c = Cipher.getInstance("AES");
        c.init(Cipher.DECRYPT_MODE, k);
        byte[] decodedValue = Base64.getDecoder().decode(encrypted);
        byte[] decValue = c.doFinal(decodedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

}

24
Praneeth

Si vous n'allez pas utiliser une transformation Cipher qui inclut un schéma de remplissage pour un chiffrement par bloc, le nombre d'octets dans le texte en clair doit être un multiple entier de la taille de bloc du chiffrement.

Vous pouvez donc soit ajouter un multiple de 16 octets (soit la taille de bloc AES) au texte en clair, soit spécifier un schéma de remplissage lorsque vous créez vos objets Cipher. Par exemple, vous pouvez utiliser:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

À moins que vous n'ayez une bonne raison de ne pas le faire, utilisez un schéma de remplissage qui fait déjà partie de la mise en œuvre de JCE. Ils ont réfléchi à un certain nombre de subtilités et de cas particuliers que vous devrez comprendre et traiter vous-même autrement.


Ok, votre deuxième problème est que vous utilisez String pour contenir le texte chiffré.

En général,

String s = new String(someBytes);
byte[] retrievedBytes = s.getBytes();

will not someBytes et retrievedBytes étant identiques.

Si vous voulez/devez conserver le texte chiffré dans une variable String, codez en base64 les octets de texte chiffré et construisez la variable String à partir des octets codés en base64. Ensuite, lorsque vous décryptez, vous aurez getBytes() pour extraire les octets encodés en base64 de la variable String, puis base64-décodez-les pour obtenir le texte chiffré réel, puis déchiffrez-le.

La raison de ce problème est que la plupart (tous?) Des codages de caractères ne sont pas capables de mapper des octets arbitraires en caractères valides. Ainsi, lorsque vous créez votre String à partir du texte chiffré, le constructeur String (qui applique un codage de caractères pour transformer les octets en caractères) doit essentiellement supprimer certains de ces octets, car il ne peut les comprendre. Ainsi, lorsque vous extrayez des octets de la chaîne, ils ne sont pas les mêmes que ceux que vous avez insérés dans la chaîne.

En Java (et dans la programmation moderne en général), vous ne pouvez pas supposer qu'un seul caractère = un octet, à moins que vous sachiez absolument que vous utilisez ASCII. C'est pourquoi vous devez utiliser base64 (ou quelque chose de similaire) si vous souhaitez créer des chaînes à partir d'octets arbitraires.

26
QuantumMechanic

Voici l'implémentation mentionnée ci-dessus:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.Apache.commons.codec.binary.Base64;
import org.Apache.commons.codec.binary.StringUtils;

try
{
    String passEncrypt = "my password";
    byte[] saltEncrypt = "choose a better salt".getBytes();
    int iterationsEncrypt = 10000;
    SecretKeyFactory factoryKeyEncrypt = SecretKeyFactory
            .getInstance("PBKDF2WithHmacSHA1");
    SecretKey tmp = factoryKeyEncrypt.generateSecret(new PBEKeySpec(
            passEncrypt.toCharArray(), saltEncrypt, iterationsEncrypt,
            128));
    SecretKeySpec encryptKey = new SecretKeySpec(tmp.getEncoded(),
            "AES");

    Cipher aesCipherEncrypt = Cipher
            .getInstance("AES/ECB/PKCS5Padding");
    aesCipherEncrypt.init(Cipher.ENCRYPT_MODE, encryptKey);

    // get the bytes
    byte[] bytes = StringUtils.getBytesUtf8(toEncodeEncryptString);

    // encrypt the bytes
    byte[] encryptBytes = aesCipherEncrypt.doFinal(bytes);

    // encode 64 the encrypted bytes
    String encoded = Base64.encodeBase64URLSafeString(encryptBytes);

    System.out.println("e: " + encoded);

    // assume some transport happens here

    // create a new string, to make sure we are not pointing to the same
    // string as the one above
    String encodedEncrypted = new String(encoded);

    //we recreate the same salt/encrypt as if its a separate system
    String passDecrypt = "my password";
    byte[] saltDecrypt = "choose a better salt".getBytes();
    int iterationsDecrypt = 10000;
    SecretKeyFactory factoryKeyDecrypt = SecretKeyFactory
            .getInstance("PBKDF2WithHmacSHA1");
    SecretKey tmp2 = factoryKeyDecrypt.generateSecret(new PBEKeySpec(passDecrypt
            .toCharArray(), saltDecrypt, iterationsDecrypt, 128));
    SecretKeySpec decryptKey = new SecretKeySpec(tmp2.getEncoded(), "AES");

    Cipher aesCipherDecrypt = Cipher.getInstance("AES/ECB/PKCS5Padding");
            aesCipherDecrypt.init(Cipher.DECRYPT_MODE, decryptKey);

    //basically we reverse the process we did earlier

    // get the bytes from encodedEncrypted string
    byte[] e64bytes = StringUtils.getBytesUtf8(encodedEncrypted);

    // decode 64, now the bytes should be encrypted
    byte[] eBytes = Base64.decodeBase64(e64bytes);

    // decrypt the bytes
    byte[] cipherDecode = aesCipherDecrypt.doFinal(eBytes);

    // to string
    String decoded = StringUtils.newStringUtf8(cipherDecode);

    System.out.println("d: " + decoded);

}
catch (Exception e)
{
    e.printStackTrace();
}
5
Greg
import javax.crypto.*;    
import Java.security.*;  
public class Java {

private static SecretKey key = null;         
   private static Cipher cipher = null; 

   public static void main(String[] args) throws Exception
   {

      Security.addProvider(new com.Sun.crypto.provider.SunJCE());

      KeyGenerator keyGenerator =
         KeyGenerator.getInstance("DESede");
      keyGenerator.init(168);
      SecretKey secretKey = keyGenerator.generateKey();
      cipher = Cipher.getInstance("DESede");

      String clearText = "I am an Employee";
      byte[] clearTextBytes = clearText.getBytes("UTF8");

      cipher.init(Cipher.ENCRYPT_MODE, secretKey);
      byte[] cipherBytes = cipher.doFinal(clearTextBytes);
      String cipherText = new String(cipherBytes, "UTF8");

      cipher.init(Cipher.DECRYPT_MODE, secretKey);
      byte[] decryptedBytes = cipher.doFinal(cipherBytes);
      String decryptedText = new String(decryptedBytes, "UTF8");

      System.out.println("Before encryption: " + clearText);
      System.out.println("After encryption: " + cipherText);
      System.out.println("After decryption: " + decryptedText);
   }
}


// Output

/*
Before encryption: I am an Employee  
After encryption: }?ス?スj6?スm?スZyc?ス?ス*?ス?スl#l?スdV  
After decryption: I am an Employee  
*/
5
user2362312

Essayez ceci, une solution plus simple.

 byte [] salt = "ThisIsASecretKey" .getBytes (); 
 clé = new SecretKeySpec (salt, 0, 16, "AES"); 

 Cipher cipher = Cipher.getInstance (" AES "); 
4
Kumar Bibek

Vous indiquez que vous souhaitez chiffrer/déchiffrer un mot de passe. Je ne suis pas certain de votre cas d'utilisation spécifique, mais, en général, les mots de passe ne sont pas stockés dans un formulaire où ils peuvent être déchiffrés. La pratique générale consiste à saler le mot de passe et à utiliser un hachage unidirectionnel assez puissant (tel que PBKDF2).

Jetez un coup d'œil au lien suivant pour plus d'informations.

http://crackstation.net/hashing-security.htm

1
the_new_mr

Exemple complet de cryptage/décryptage _ une vidéo énorme sans lancer JavaOutOfMemoryException et utiliser Java SecureRandom pour la génération de vecteur d'initialisation. Il est également décrit le stockage d’octets de clé dans la base de données, puis la reconstruction de la même clé à partir de ces octets.

https://stackoverflow.com/a/18892960/185022

0
AZ_