I am trying to decrypt an encrypted text.
I have the salt value, iteration count and key length. But i don't have the initialization vector (IV) value, how can i go about and decrypt this. I also have secret key.
For time being I am using some random IV value whose size is 16 bytes.
But still i am not able to decrypt the value properly.
Can anyone please help as i am stuck with this for a long time?
Below are the values which were given to me.
salt= EW0h0yUcDX72WU9UiKiCwDpXsJg=, Iteration=128,Keylenght=16.
MasterKeyName="Passphrase1", MACMethod algo = hmac-sha1, MACKey="jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH 6Mlg" encrypted kae = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE" and valuemac="lbu+9OcLArnj6mS7KYOKDa4zRU0=".
Secret key = "xxxxxxxxxxx".
Below is the code which I am using to decrypt.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(secretkey.toCharArray(), salt, iterationCount, keyStrength);
SecretKey tmp = factory.generateSecret(spec);
key = new SecretKeySpec(tmp.getEncoded(), "AES");
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameters params = dcipher.getParameters();
iv = "0000000000000000".getBytes();
System.out.println("IV " + new sun.misc.BASE64Encoder().encodeBuffer(iv));
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decryptedData = new sun.misc.BASE64Decoder().decodeBuffer(base64EncryptedData);
byte[] utf8 = dcipher.doFinal(decryptedData);
You cannot decrypt the first block of CBC encrypted ciphertext if you don't know the IV.
It is however not unlikely that you can retrieve the IV value:
often the IV value is 16 bytes retrieved after the key bytes) generated from the PBKDF;
the IV is often prepended to the ciphertext, resulting in one block of garbage before the full plaintext during decryption;
not secure but the IV is also left out or set to a constant value, with an all-zero IV being the most common (this is identical in CBC mode to not using any IV.)
Related
Currently, I'm having a problem that I don't know how to solve. It's the decryption and encryption of the string using AES256. Everything was working fine until I restarted the server and I couldn't decode the previous data.
I tried saving the salt and IVParameter to decrypt for next time, but it doesn't work.
private static final String SECRET_KEY = "my_key";
private static final byte[] SALT;
private static final SecureRandom random;
private static final IvParameterSpec ivspec;
static {
random = new SecureRandom();
SALT = new byte[16];
random.nextBytes(SALT);
byte[] bytesIV = new byte[16];
random.nextBytes(bytesIV);
ivspec = new IvParameterSpec(bytesIV);
}
public static String encrypt(String stringToEncrypt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e);
}
return null;
}
public static String decrypt(String stringToDecrypt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e);
}
return null;
}
SecureRandom initialises itself differently everytime you instantiate it. I.e., it will also create a different sequence of random values each time. Even if you initialise SALT with a fixed initial value, in the next step you overwrite it again by calling random.nextBytes(SALT). Either don't do that or instantiate SecureRandom with a seed, so it creates the same sequence of random numbers every time. But this is kind of counter-productive. Similarly, you also randomise IvParameterSpec.
You only need the nextBytes() result, if you want to generate new salt or IV values for a multiple users or a sequence of distinct encryption/decryption actions. AES being a symmetric cypher, you need to make sure that when decrypting a message, you use the same salt and IV (if any) which were used for encryption. Try this in order to get identical encryption results:
static {
random = new SecureRandom(); // not used in this example
SALT = "I am so salty!".getBytes(StandardCharsets.UTF_8);
byte[] bytesIV = "my super fancy IV".getBytes(StandardCharsets.UTF_8);
ivspec = new IvParameterSpec(Arrays.copyOfRange(bytesIV, 0, 16));
}
Of course, in the example above I am assuming that actually salt and IV were initially created randomly, then securely saved or transmitted to the recipient, and then loaded/received and used to decrypt the message. In a real-world scenario, you would transmit or store salt and IV asymmetrically encrypted (using public-key cryptography), while the message itself (which usually is much bigger than secret key, salt and IV) is encrypted using the much faster and more efficient symmetric AES256 algorithm.
P.S.: The Arrays.copyOfRange(bytesIV, 0, 16) is necessary, because in contrast to the salt the IV must be exactly 16 bytes long. The salt is more flexible.
Update: Actually, it is not necessary to encrypt salt and IV. They just make sure that the same input and secret key do not yield the same encrypted message in order to make attacks based on known cleartext more difficult. This is also why e.g. when storing salted hashes in a database, you store the salt values as cleartext along with the salted password hash (not the password itself!), because you need them every time you want to validate a user password.
I'm trying to change encryption algorithm of existing project. But i have a little bit confusion. When i use "PBEWithHmacSHA512AndAES_256" as a parameter, it produce different result but when i use "PBEWithMD5AndDES" as a parameter it produce same result. My functions are :
public static synchronized String encrypt1(final String textToEncrypt, final String pathPublicKey) throws Exception {
final KeySpec pbeKeySpec = new PBEKeySpec(DbKeyHandler.getDbKey(pathPublicKey).toCharArray());
final SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(pbeKeySpec);
// Prepare the parameter to the ciphers
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
final Cipher cipher = Cipher.getInstance(pbeKey.getAlgorithm());
// Create the ciphers
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);
// Encode the string into bytes using utf-8
final byte[] utf8 = textToEncrypt.getBytes("UTF8");
// Encrypt
final byte[] enc = cipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
}
public static synchronized String encrypt2 (final String textToEncrypt, final String pathPublicKey) throws Exception {
final KeySpec pbeKeySpec = new PBEKeySpec(DbKeyHandler.getDbKey(pathPublicKey).toCharArray());
final SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithHmacSHA512AndAES_256").generateSecret(pbeKeySpec);
// Prepare the parameter to the ciphers
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
final Cipher cipher = Cipher.getInstance(pbeKey.getAlgorithm());
// Create the ciphers
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);
// Encode the string into bytes using utf-8
final byte[] utf8 = textToEncrypt.getBytes("UTF8");
// Encrypt
final byte[] enc = cipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
}
Any suggestions, ideas will help me to figure out what's going on here.
Also this is produce different results:
KeyStore keyStore = KeyStore.getInstance("JCEKS");
keyStore.load(new FileInputStream((pathOfJKSfile)), password.toCharArray());
Key key = keyStore.getKey(keyName, keyPass.toCharArray());
byte[] raw = key.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "PBEWithHmacSHA512AndAES_256");
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS);
final Cipher cipherEncrypt = Cipher.getInstance(ALGORITHM);
cipherEncrypt.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
final byte[] enc = cipherEncrypt.doFinal(messageBytes);
System.out.println( new sun.misc.BASE64Encoder().encode(enc));
And i know that cipher.init() using "JceSecurity.RANDOM" for pruducing different results.
Both algorithms, PBEWithHmacSHA512AndAES_256 and PBEWithMD5AndDES, first generate an encryption key by processing a password, a salt and an iteration count (using HmacSHA512 and MD5, respectively) and then encrypt the plain text (with AES-256 and DES, respectively) using this key and the CBC-mode. When the Cipher-instance is initialized, a pseudo-random initialization vector (IV) is generated that is required for the CBC- mode.
In the context of PBEWithHmacSHA512AndAES_256, the IV is generated using the SecureRandom implementation of the highest-priority installed provider, at least for the Cipher#init()-method used in the code (note that there are several overloads of the Cipher#init()-method and that a SecureRandom-instance can also be passed explicitly). I.e. with each Cipher-initialization a new (random) IV is generated and therefore the encrypted text is always different, even for an identical plain text. For this reason, the encrypted text in your examples changes in this context.
In the context of PBEWithMD5AndDES, the IV is only determined by the password, the salt, the iteration count (and of course the MD5-hash-algorithm itself). Therefore, the IV and the encrypted text do not change in case of repetition (provided that password, salt, iteration count etc. are the same). For this reason, the encrypted text in your example does not change in this context.
The generation of a new, random IV during the Cipher-initalization makes sense with regard to the following requirements for the IV: For security reasons, an IV in CBC-mode (btw this also applies to other modes) may only be used once under the same key. In addition the IV must be unpredictable.
PBEWithMD5AndDES is deprecated.
EDIT:
The use of an IV is standard nowadays (for security reasons). A lot of information can be found on the Internet on this topic e.g. here. In the following I will only describe a few basic things.
The IV used for encryption must be stored in some way because it is required for decryption. The IV does not have to be kept secret, so that it is usually concatenated with the encrypted data (e.g. before the encrypted data) and stored together with them. During decryption, both parts can be separated because the length of the IV is known (16 Byte for AES). E.g for the concatenation in the encryption-method something like the following is used (let iv and enc be the byte-arrays with the IV and the encrypted data, respectively):
byte[] result = new byte[enc.length + iv.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(enc, 0, result, iv.length, enc.length);
and in the decryption-method the corresponding counterpart (having in mind that the length of an IV in AES is 16 Byte).
In the encryption-method the IV can be determined with Cipher#getIV() (this must of course happen after calling Cipher#init()).
In the decryption-method you have to pass the IV to the PBEParameterSpec-ctor (e.g. let iv be the byte-array with the IV):
IvParameterSpec ivSpec = new IvParameterSpec(iv);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount, ivSpec);
The generation of an IV can also take place outside the Cipher-class, see e.g. Generating random IV for AES in Java. Then you have to pass that IV in the encryption-method in the same way as above described for the decryption-method.
Note, in connection with an IV some points have to be considered e.g. using a mode without an IV (e.g. ECB), using an IV consisting exclusively of 0-values, using a predictable IV or using an IV more than once under the same key etc. drastically reduces security in general, see e.g. here!
I am using following code to decrypt cipher text encrypted by 128 bit AES.
final IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
final SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
final Cipher cipherSpec = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipherSpec.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
cipherSpec.doFinal(DatatypeConverter.parseBase64Binary(encrypted));
My encrypted text is simple one line statement and I am using following key and initial verctor
Key = "77696567683763656548616574326F6F"; // 128 bit key, hext string
initVector = "6F68706865726F68563274686F6F3761"; // 16 bytes IV, hex string
Everything worked fine except following scenario.
1)Encrypt the plain text using key as the initial vector and initial vector ad the key. Decryption failed with BadPaddingException
2)Encrypt using key as the key and initial vector as the initial vector. Above code decrypts the cipher text without exception but output is garbage. Not what I encrypted.
3)Decrypts again above correctly encrypted cipher text (in 2 step) and now it is decrypted to the correct plain text.
My question is why the decryption fails to decrypt into correct cipher text in step 2 ?
I believe your key, if 128 bits, need to be a 16 character(utf-8) byte array as opposed to 32 characters. So you'd want to do something like :
Key = ("7769656768376365").getBytes("UTF-8");
I have been using aes-js in Node to encrypt/decrypt using AES counter mode.
As you can see in the example, I'm using it without padding and I can specify which block (0 in this case) I want to start with.
var aesCTR = new aesjs.ModeOfOperation.ctr(keyBytes, new aesjs.Counter(0));
var encryptedBytes = aesCTR.encrypt(plaintextBytes);
I wanted to reproduce the same behavior above in Java. I'm using BouncyCastle like the example below.
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted=cipher.doFinal(msgBytes);
But this implementation doesn't seem to be outputting the sames values as the one above. Plus it seems to increment the counter automatically each run (undesirable behavior in this case).
Is there a way to match the Node JS implementation using Java ?
You should get the same behaviour if you provide an IV / initial ctr value that is all zero e.g:
byte[] iv = new byte[16];
Arrays.fill(iv, (byte)0);
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] encrypted=cipher.doFinal(msgBytes);
The way it is set up in your code, a random IV is generated every time you call init().
BTW, if you omit BC, you will get the stock AES implementation
An Exception caught at the line
encryptedData = cipher.doFinal(data);
javax.crypto.IllegalBlockSizeException: Data must not be longer than 501 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)
The key size is given by: keyPairGenerator.initialize(4096);
How to solve this problem without increasing the size of key?
With asymmetric encryption there is no way to encrypt data longer than key minus padding. Since it's 11 bytes for you I can conclude you use PKCS#1 padding. What you can do is try to compress data, but depending on data length and nature it easily can fail. Another option is to combine symmetric block ciphers (which has no limitation for the size of data) and asymmetric encryption:
Generate random AES key
byte[] keyData = new byte[32];
SecureRandom random = new SecureRandom();
random.nextBytes(keyData);
Encrypt data with AES.
// zero filled input vector
byte[] ivData = new byte[32];
IvParameterSpec iv = new IvParameterSpec(ivData);
SecretKeySpec keySpec = new SecretKeySpec(keyData, "AES");
Cipher aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
aes.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] cipherText = aes.doFinal(data);
Encrypt AES key (for AES-256 it's 32 bytes) with RSA private key.
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE, rsaKeyPair.getPublic());
byte[] wrappedKey = cipher.doFinal(keyData);
Combine wrappedKey with cipherText. Can be done with just appending one to another, but also some binary format can be used.