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;
}
}
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.
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();
}
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
*/
Essayez ceci, une solution plus simple.
byte [] salt = "ThisIsASecretKey" .getBytes (); clé = new SecretKeySpec (salt, 0, 16, "AES"); Cipher cipher = Cipher.getInstance (" AES ");
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.
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.