I want to decrypt the EncryptedAssertion. I tried with OpenSaml Decrypter but its not working for me.I am getting Failed to decrypt EncryptedData
I have already ask that question - EncryptedAssertion Decryption failing
While I am waiting for any solution I am trying to decrypt it manually. Its a Hybrid encryption
I tried below code
CipherValue cv = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0).getCipherData().getCipherValue();
String cvalue = cv.getValue();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
String decryptedValue = new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(cvalue)));
I am not sure if I am on the right path, but above decryptedValue is the decryption key for my Encrypted Data.This decryptedValue is not in readable format. Not sure what to do next.
getPrivateKey method
public PrivateKey getPrivateKey(){
Key key = null;
PrivateKey privateKey = null;
try {
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream("prvkey.pfx"),"".toCharArray());
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()){
String alias = aliases.nextElement();
key = ks.getKey(alias, "".toCharArray());
privateKey = (PrivateKey)key;
}
} catch (Exception e) {
e.printStackTrace();
}
}
Based on the suggestion I coded like below. Not sure if I am doing it correct also I am getting errors
`CipherValue cv = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0).getCipherData().getCipherValue();
String cvalue = cv.getValue();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.UNWRAP_MODE, getPrivateKey());
Key decryptionKey = cipher.unwrap(DatatypeConverter.parseBase64Binary(cvalue), "RSA/ECB/PKCS1Padding", Cipher.SECRET_KEY);
CipherValue cdata = encryptedAssertion.getEncryptedData().getCipherData().getCipherValue();
String cdataValue = cdata.getValue();
byte[] iv = new byte[256 / 16];
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
Cipher cipher2 = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec spec = new SecretKeySpec(decryptionKey.getEncoded(), "AES");
cipher2.init(Cipher.DECRYPT_MODE, spec, ivParamSpec );
String decryptedValue = new String(cipher2.doFinal(DatatypeConverter.parseBase64Binary(cdataValue)));`
Error -
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2121)
UPDATE ::
hope I am doing it correctly based on the comments.
byte[] iv = new byte[256/16];
iv = Arrays.copyOfRange(DatatypeConverter.parseBase64Binary(cdataValue), 0, 16);
byte[] cipherBlock = Arrays.copyOfRange(DatatypeConverter.parseBase64Binary(cdataValue), 16, DatatypeConverter.parseBase64Binary(cdataValue).length);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
Cipher cipher2 = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec spec = new SecretKeySpec(decryptionKey.getEncoded(), "AES");
cipher2.init(Cipher.DECRYPT_MODE, spec, ivParamSpec );
String decryptedValue = new String(cipher2.doFinal(cipherBlock)); // Same error - Given final block not properly padded
I won't provide you a complete answer but I hope to get you on the right track
You should not just simply decrypt the calue with the private key.
First decrypt the KeyInfo value (unwrap the aes key) using RSA/ECB/PKCS1Padding (according to the provided saml snippet)
It should give you a 256 bit (32 bytes) random key used to encrypt data itself
then use the AES key to decrypt the data . Please note that first bytes (128 bit / 16 bytes, aes block size) is used as IV.
further reading
https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#sec-Processing-Encryption
https://gusto77.wordpress.com/2017/10/30/encryption-reference-project/
public static byte[] decrypt(byte[] cryptoBytes, byte[] aesSymKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
// https://github.com/onelogin/java-saml/issues/23
String cipherMethod = "AES/CBC/ISO10126Padding"; // This should be derived from Cryptic Saml
AlgorithmParameterSpec iv = new IvParameterSpec(cryptoBytes, 0, 16);
// Strip off the the first 16 bytes because those are the IV
byte[] cipherBlock = Arrays.copyOfRange(cryptoBytes,16, cryptoBytes.length);
// Create a secret key based on symKey
SecretKeySpec secretSauce = new SecretKeySpec(aesSymKey, "AES");
// Now we have all the ingredients to decrypt
Cipher cipher = Cipher.getInstance(cipherMethod);
cipher.init(Cipher.DECRYPT_MODE, secretSauce, iv);
// Do the decryption
byte[] decrypedBytes = cipher.doFinal(cipherBlock);
return decrypedBytes;
}
ISO10126Padding should work....
Related
I have to encrypt some parameters for a web portal that recibe encrypted parameters via POST with AES/CBC/PKCS5Padding, the encryption method was provided by the web portal creator(another team), but when I use their method the result is not accepted by ther web portal. I assume that the parameters are not well provided to the method. Am I missing somethig?
I requested some fixed IV value but the another teams told me that they used random IV's.
public static byte[] Encrypt(byte[] plaintext, SecretKey key, byte[] IV) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
//this is the way I build the parameters
String input_text = txtPlainText.getText();
byte[] plaintext = input_text.getBytes("UTF-8");
String encodedKey = "6qp4Y?kLmaY8+Fsd";
byte[] decodedKey = encodedKey.getBytes();
SecretKey key = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
byte[] IV = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
byte[] cipherText = Encrypt(plaintext,key,IV);
String Encrypted_text = java.util.Base64.getEncoder().encodeToString(cipherText);
txtEncryptedText.setText(Encrypted_text );
Every timeI use this encription method i get a diferent value, for the plain text 'System Administrator' I get vlaues like 'U+za3baTIvMpiFgCPCfqoKe+ljDutraUrUTJhroqHPg=', 'lHT0Kq6GlyzYx2TEyQmqdPp4KfVi5sdlC0udtSlZ5uk='
I have a problem where I need to decrypt a message which was encrypted using AES=256. I am already provided with a key and vector. I have to hash the provided key using SHA-256 and then use this hash to encrypt a message. The decryption code runs fine but the result is not the original String.
Result: ?m?>? ???????z?p???>??<3? (the exact text is different, but after copying and pasting it, it is different).
My code below:
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest("someKey".getBytes(ENCODING_UTF8));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec("somevector".getBytes(ENCODING_UTF8));
SecretKeySpec skeySpec = new SecretKeySpec(hashBytes, AES);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] cipherText = cipher.doFinal(plainText.getBytes(ENCODING_UTF8));
encrypted = EACECryptoUtils.base64Encode(cipherText);
Cipher decryptCipher = Cipher.getInstance(TRANSFORMATION_TYPE);
IvParameterSpec decryptIV = new IvParameterSpec("somevector".getBytes(ENCODING_UTF8));
SecretKeySpec decryptSkeySpec = new SecretKeySpec(hashBytes, AES);
decryptCipher.init(Cipher.DECRYPT_MODE, decryptSkeySpec, decryptIV);
byte[] original = cipher.doFinal(EACECryptoUtils.base64Decode(encrypted));
decrypted = new String(original);
} catch (Exception e) {
log.error(new LogRecord(FUNCTION_NAME + "Exception while encrypting the data", e));
throw e;
}
}
byte[] original = cipher.doFinal(EACECryptoUtils.
I believe you should use decryptCipher instead of cipher
I have an application that needs to store some secret passwords in a configuration file such as database and ftp passwords/detail. I've looked around and found a lot of encryption/decryption solutions using AES, but I can't seem to figure out how to make it work without changing the key. That means I can encrypt and decrypt (using the same SecretKey), but to maintain persistence across restarts etc. I can't seem to make the SecretKey stay the same. The example below shows my methods working:
String secret = Encryptor.encrpytString("This is secret");
String test = Encryptor.decrpytString(secret);
System.out.println(test); //This is secret is printed
So far so good. However if I run it once I might get the value of '2Vhht/L80UlQ184S3rlAWw==' as my secret, the next time it is 'MeC4zCf9S5wUUKAu8rvpCQ==', so presumably the key is changing. I'm assuming I am applying some counter-intuative logic to the problem and would appreciate if someone can shed some light on either a) what I'm doing wrong, or b) a solution that would allow me to store the password information encrypted and retrievable with the information provided.
My methods are as follows:
private static final String salt = "SaltySalt";
private static byte [] ivBytes = null;
private static byte[] getSaltBytes() throws Exception {
return salt.getBytes("UTF-8");
}
private static char[] getMasterPassword() {
return "SuperSecretPassword".toCharArray();
}
private static byte[] getIvBytes() throws Exception {
if (ivBytes == null) {
//I don't have the parameters, so I'll generate a dummy encryption to create them
encrpytString("test");
}
return ivBytes;
}
public static String encrpytString (String input) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536,256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(input.getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}
public static String decrpytString (String input) throws Exception {
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(input);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536, 256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(getIvBytes()));
byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
return new String(decryptedTextBytes);
}
Thanks for the help!
OK, looks like I've found the answer to my question. I sourced my information from this Stackoverflow post.
From what I understand, the IV (initialisation vector) is used to add entropy into the encryption process. Each time you create a new cipher, Java creates a slightly different IV. There are therefore two solutions:
User a fixed IV, or
Store the IV along with the encrypted data.
From what I've read, option 1 is not very good practice; so option 2 it is. I understand that it should be possible to simply append the IV to the encrypted string (as the secret is still required) and therefore the IV can be reconstructed when it comes time to decrypt.
Here is the almost complete solution. I'm still getting some padding errors on decryption (see my comment). I don't have time to spend on it now, so as a temporary measure I immediately try decrypting an encrypted string and keep on trying (iterating) until it works. It seems to have about a 50% hit rate + I'm not encrypting often enough for it to be a performance concern. Would be nice if someone could suggest a fix though (just for completeness sake).
private static final String salt = "SaltySalt";
private static final int IV_LENGTH = 16;
private static byte[] getSaltBytes() throws Exception {
return salt.getBytes("UTF-8");
}
private static char[] getMasterPassword() {
return "SuperSecretPassword".toCharArray();
}
public static String encrpytString (String input) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536,256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(input.getBytes("UTF-8"));
byte[] finalByteArray = new byte[ivBytes.length + encryptedTextBytes.length];
System.arraycopy(ivBytes, 0, finalByteArray, 0, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, finalByteArray, ivBytes.length, encryptedTextBytes.length);
return DatatypeConverter.printBase64Binary(finalByteArray);
}
public static String decrpytString (String input) throws Exception {
if (input.length() <= IV_LENGTH) {
throw new Exception("The input string is not long enough to contain the initialisation bytes and data.");
}
byte[] byteArray = DatatypeConverter.parseBase64Binary(input);
byte[] ivBytes = new byte[IV_LENGTH];
System.arraycopy(byteArray, 0, ivBytes, 0, 16);
byte[] encryptedTextBytes = new byte[byteArray.length - ivBytes.length];
System.arraycopy(byteArray, IV_LENGTH, encryptedTextBytes, 0, encryptedTextBytes.length);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536, 256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
return new String(decryptedTextBytes);
}
Use a static Initialization Vector, e.g. a zero IV:
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
Since you're storing passwords you probably want to use a random IV and/or random salt and store them with the cipher text so the same passwords don't encrypt to the same ciphertext.
You need to setSeed() before
class Encryptor {
static final String salt = "SaltSalt";
public static byte[] encryptString(String input) throws Exception {
byte[] bytes = input.getBytes("UTF-8");
Cipher cipher = Cipher.getInstance("AES");
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(salt.getBytes("UTF-8"));
keyGenerator.init(256, secureRandom);
Key key = keyGenerator.generateKey();
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] a = cipher.doFinal(bytes);
return a;
}
public static String decryptString(byte[] input) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(salt.getBytes("UTF-8"));
keyGenerator.init(256, secureRandom);
Key key = keyGenerator.generateKey();
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(input);
String result = new String(decrypted, "UTF-8");
return result;
}
}
I want to learn the basics of AES encryption so I started to make a very simple Java program. The program loads a text file in to a String and asks for a key from the user. The program then uses AES to encrypt the text creating a new text file with the encrypted text. The program prints the Initialization Vector (IV) to the user.
The program also has the decryption function. The user specifies the encrypted text file along with the Initialization Vector and the key to decrypt it back to the original text in a new text file.
However I think I'm doing something wrong. Is it normal procedure in AES encryption that the user needs to have both key and IV to decrypt the file? I have browsed through the internet and almost in every example, the encrypted data can be decrypted by the user specifying only the key but in my case the user needs to have both the key and the IV. The program is working fine but I think it isn't efficient.
So should I use a constant, known IV which is used in all the encryptions and decryptions or what? Also some tutorials are using "salt", what is it and should I use it?
Here are my encrypt and decrypt methods:
public String encrypt(String stringToEncrypt, String userKey)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
// User gives string key which is formatted to 16 byte and to a secret
// key
byte[] key = userKey.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
// Cipher initialization
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// Encryption and encoding
String encryptedData = new BASE64Encoder().encode(cipher
.doFinal(stringToEncrypt.getBytes()));
// IV is printed to user
System.out.println("\nENCRYPTION IV: \n"
+ new BASE64Encoder().encode(cipher.getIV()) + "\n");
// Function returns encrypted string which can be writed to text file
return encryptedData;
}
public String decrypt(String stringToDecrypt, String userKey, String userIv)
throws NoSuchAlgorithmException, IOException,
NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException {
// User gives the same string key which was used for encryption
byte[] key = userKey.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
// Decode string iv to byte
byte[] iv = new BASE64Decoder().decodeBuffer(userIv);
// Cipher initialization
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
// Decryption and decoding
String decryptedData = new String(cipher.doFinal(new BASE64Decoder()
.decodeBuffer(stringToDecrypt)));
// Function returns decrypted string which can be writed to text file
return decryptedData;
}
UPDATE
I updated my code now to use "PBKDF2WithHmacSHA256" algorithm with salt and etc. I also combined the Initialization Vector (IV) byte array to the cipher text byte array as prefix so I can split them in decrypt method and get the IV there (That's working fine).
However there's now an issue with the key, because I'm generating new encrypted key also in decryption method which of course is a wrong key for encrypted data. I want to be able to close the program so I can't store the key as a class variable. It's very hard to explain the issue but I hope you understand the problem...
public static byte[] getEncryptedPassword(String password, byte[] salt,
int iterations, int derivedKeyLength)
throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec mKeySpec = new PBEKeySpec(password.toCharArray(), salt,
iterations, derivedKeyLength);
SecretKeyFactory mSecretKeyFactory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA256");
return mSecretKeyFactory.generateSecret(mKeySpec).getEncoded();
}
public String encrypt(String dataToEncrypt, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
byte[] mEncryptedPassword = getEncryptedPassword(key, generateSalt(),
16384, 128);
SecretKeySpec mSecretKeySpec = new SecretKeySpec(mEncryptedPassword, "AES");
Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec);
byte[] ivBytes = mCipher.getIV();
byte[] encryptedTextBytes = mCipher.doFinal(dataToEncrypt.getBytes());
byte[] combined = new byte[ivBytes.length+encryptedTextBytes.length];
System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, combined, ivBytes.length, encryptedTextBytes.length);
return Base64.getEncoder().encodeToString(combined);
}
public String decrypt(String dataToDecrypt, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
byte[] encryptedCombinedBytes = Base64.getDecoder().decode(dataToDecrypt);
byte[] mEncryptedPassword = getEncryptedPassword(key, generateSalt(),
16384, 128);
byte[] ivbytes = Arrays.copyOfRange(encryptedCombinedBytes,0,16);
SecretKeySpec mSecretKeySpec = new SecretKeySpec(mEncryptedPassword, "AES");
Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, new IvParameterSpec(ivbytes));
byte[] encryptedTextBytes = Arrays.copyOfRange(encryptedCombinedBytes, 16, encryptedCombinedBytes.length);
System.out.println(encryptedTextBytes.length);
byte[] decryptedTextBytes = mCipher.doFinal(encryptedTextBytes);
return Base64.getEncoder().encodeToString(decryptedTextBytes);
}
public byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte saltBytes[] = new byte[16];
random.nextBytes(saltBytes);
return saltBytes;
}}
I hope somebody knows how to make this better. Thanks!
Just save the IV in the file before the encrypted data.
You should never use the same IV more than once (it's ok-ish, if you roll a new IV every time, and it just so happens that you roll the same twice, so you don't have to store and check that). Using the same IV many times poses a great security risk, as encrypting the same content twice reveals that it's - in fact - the same content.
Storing IV alongside the encrypted data is a common, and secure procedure, as it's role is to introduce "randomness" to the encryption scheme, and it shouldn't be secret, just securely (and in some schemes randomly) generated.
I'm in need of a simple AES cryptosystem in ECB. I have one working at the moment in the sense that given the same key twice in a row, it will correctly encrypt and decrypt a message.
However, if I use two different keys for encrypting/decrypting, the program throws a javax.crypto.BadPaddingException: Given final block not properly padded. I need the program to provide an incorrect decryption, presumably something that looks like some encrypted string. Here's my code:
public static byte[] encrypt(byte[] plaintext, String key) throws Exception {
char[] password = key.toCharArray();
byte[] salt = "12345678".getBytes();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] ciphertext = cipher.doFinal(plaintext);
return ciphertext;
}
public static byte[] decrypt(byte[] ciphertext, String key) throws Exception {
char[] password = key.toCharArray();
byte[] salt = "12345678".getBytes();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
byte[] plaintext = cipher.doFinal(ciphertext);
return plaintext;
}
(Note: I'm aware of the disadvantages of using ECB, salt = "12345678", etc., but it's not my concern at the moment.) Thanks for any and all help.
PKCS#5 padding has a very specific structure, so you cannot continue using it if you want decryption with the wrong key to complete without error.
A good way to achieve your goal might be to use a stream mode of operation, rather than a block-mode. In a stream mode, the input key is used to produce a never-ending stream of seemingly random data, which is XORed with the ciphertext to produce plaintext (and vice versa). If you use the wrong key, you get nonsense data out which is the same size as the original plaintext.
Here's a simple example, based on your original code. I use an IV of all zeroes, but you may wish to improve that to be a random value in due course (note: you'll need to store this value with the ciphertext).
public static void main(String[] args) throws Exception {
byte[] plaintext = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
byte[] ciphertext = encrypt(plaintext, "foo");
byte[] goodDecryption = decrypt(ciphertext, "foo");
byte[] badDecryption = decrypt(ciphertext, "bar");
System.out.println(DatatypeConverter.printHexBinary(goodDecryption));
System.out.println(DatatypeConverter.printHexBinary(badDecryption));
}
public static SecretKey makeKey(String key) throws GeneralSecurityException {
char[] password = key.toCharArray();
byte[] salt = "12345678".getBytes();
SecretKeyFactory factory =
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), "AES");
}
public static byte[] encrypt(byte[] plaintext, String key) throws Exception {
SecretKey secret = makeKey(key);
Cipher cipher = Cipher.getInstance("AES/OFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
return cipher.doFinal(plaintext);
}
public static byte[] decrypt(byte[] ciphertext, String key) throws Exception {
SecretKey secret = makeKey(key);
Cipher cipher = Cipher.getInstance("AES/OFB8/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
return cipher.doFinal(ciphertext);
}
Output:
00010203040506070809
5F524D4A8D977593D34C