Incorrect Key Length for Padded Encryption [duplicate] - java

This question already has an answer here:
bytes in 'str = new String(bytes, "UTF8") ' and 'bytes = str.getBytes("UTF8")' not the same value
(1 answer)
Closed 6 years ago.
I have been having a problem with doing padded encryption. I think I have isolated the issue to this function:
static String AESEncryptStringWithPassword(String s, String p) throws...{
//function to create key from string password
SecretKey secret = deriveAESKey(p);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal(s.getBytes(Charset.forName("UTF-8")));
String str = Base64.getEncoder().encodeToString(ciphertext);
return str;
}
Down the line when I go to decrypt I will get an error like so: Input length must be multiple of 16 when decrypting with padded cipher.
I have checked and this method is the origin of the bad length string. No further methods corrupt it. I'm not sure exactly what I'm doing wrong. I have based my code on the question here: Java 256-bit AES Password-Based Encryption
EDIT: I have changed the encoding slightly to use String(byte[], charset) instead. However now with defaultcharset I have an appropriate string length and with utf-8 I do not.

The conversion between byte[] and String throughout the program seemed to be the problem. Kept it as byte[] as much as possible and then it worked.
So, lesson learned: Careful converting between byte[] and String.

Related

JAVA - AES Decryption - Input length must be multiple of 16 when decrypting with padded cipher

I am trying to decrypt the ResponseText variable which i get from an API. I am getting the following error.
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be
multiple of 16 when decrypting with padded cipher
Below is my code snippet for decrypting the response. The Decrytpt method is throwing the error.
public static String decrypt(String encryptedText) throws Exception
{
Key key = generateKey();
Cipher chiper = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivspec = new IvParameterSpec(iv);
chiper.init(Cipher.DECRYPT_MODE, key, ivspec);
byte[] encVal = chiper.doFinal(encryptedText.getBytes("UTF-8"));
Base64.Encoder base64Encoder = Base64.getEncoder();
String decryptedValue = base64Encoder.encodeToString(encVal);
String decryptedString= new String("");
return decryptedString;
}
I have not posted the actual encrypted value here as the length of the encrypted value is too high. I am new to Java. Thanks in advance.
You should probably base 64 decode the ciphertext, decrypt the binary ciphertext and then decode the resulting plaintext to UTF-8.
You haven't correctly reversed the encryption routine (encode UTF-8, encrypt, encode base64), in other words.
There is a generateKey() for the decryption; unless it returns a static value (and doesn't generate one, as the method name implies) decryption will likely fail. So either the name is wrong, or the decryption.
The IV doesn't seem to be included with the ciphertext either, which will mean that that's the next problem to deal with.
Finally, you will want to know how to handle exceptions for encryption / decryption routines.

'javax.crypto.BadPaddingException' while using cipherInputStream

I'm writing a program to encrypt and decrypt data.
for encrypting,
I created a symmetric key using keyGenerator.
I transferred the key to the cipher, and created a string version of the key:
String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());
in order to store it in a configuration file (so I can retrieve the key in the decrypt function).
Now, in the decrypt function I need to get that string back to key format, so I can send it as a parameter to the cipher in dercypt mode.
I convert it back to key this way:
byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));
Key newkey = new SecretKeySpec(keyBytes,0,keyBytes.length, "AES");
And I transffer it to the cipher and write the output (the decrypted data) using CipherInputStream:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, newkey, newiv, SecureRandom.getInstance("SHA1PRNG"));
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(encryptedBytes), cipher);
ArrayList<Byte> decryptedVal = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
decryptedVal.add((byte) nextByte);
}
byte[] bytes = new byte[decryptedVal.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = decryptedVal.get(i);
}
String decryptedData = new String(bytes);
cipherInputStream.close();
System.out.println("decryptedData: " + decryptedData);
I get this error:
Exception in thread "main" java.io.IOException: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
So I suspect that there might be a problem with the way I treat the key.
Any suggestions? help would be appreciated!
I think you have not sent IV to decryption function. For decryption in CBC mode, you must provide an IV which is used in encryption process.
Update:
IV will affect only first block in CBC decryption mode. So my answer may affect the unpadding if your data is less than 1 block. It will just change the decrypted plaintext of the first block otherwise.
Of course you get this error: first you apply base 64 encoding:
String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());
and then you use character-encoding to turn it back into bytes:
byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));
which just keeps be base64 encoding, probably expanding the key size from 16 bytes to 24 bytes which corresponds with a 192 bit key instead of a 128 bit key. Or 24 bytes key to a 32 bytes key of course - both seem to work.
To solve this you need to use Base64.getDecoder() and decode the key.
Currently you get a key with a different size and value. That means that each block of plaintext, including the last one containing the padding, will decrypt to random plaintext. As random plaintext is unlikely to contain valid padding, you will be greeted with a BadPaddingException.
Reminder:
encoding, e.g. base 64 or hex: encoding bytes to a text string
character-encoding, e.g. UTF-8 or ASCII: encoding a text string into bytes
They are not opposites, that would be decoding and character-decoding respectively.
Remarks:
yes, listen to Ashfin; you need to use a random IV during encryption and then use it during decryption, for instance by prefixing it to the ciphertext (unencrypted);
don't use ArrayList<Byte>; that stores a reference to each separate byte (!) - use ByteArrayOutputStream or any other OutputStream instead;
you can better use a byte buffer and use that to read / write to the streams (note that the read function may not fill the buffer, even if at the start or in the middle of the stream) - reading a single byte at the time is not performant;
lookup try-with-resources for Java;
using a KeyStore may be better than storing in a config file;
GCM mode (AES/GCM/NoPadding) also authenticates data and should be preferred over CBC mode.

Java simple helloworld AES encryption decryption [duplicate]

This question already has answers here:
UTF-8 byte[] to String
(11 answers)
Closed 8 years ago.
I got a code snippet from another SO question and modified it a bit but I can't seem to get it to work. Can anyone figure out why? it's currently printing [B#405e70bc or similar values. All I'm trying to do is store a password in an encrypted form just for the sole purpose of keeping it away from curious eyes, is there a better way to do that?
String secret = "1234567812345678";
Key key = new SecretKeySpec(secret.getBytes(), "AES");
// Encrypt
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal("helloworld".getBytes());
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedData = cipher.doFinal(encryptedData);
System.out.println(decryptedData.toString());
There is nothing wrong with your encryption / decryption logic. The problem is with you calling .toString() on a byte [].
Use this output statement instead:
System.out.println(new String(decryptedData));
Thanks Alexander.
This worked
String secret = "1234567812345678";
Key key = new SecretKeySpec(secret.getBytes(), "AES");
// Encrypt
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal("helloworld".getBytes());
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedData = cipher.doFinal(encryptedData);
System.out.println(new String(decryptedData, "UTF-8"));
Please don't use this for anything critical. The standard Java AES code makes use of a fixed-length block cipher which can result in identical encrypted byte data. The upshot is a gradual leakage of plaintext information.
You should look into Cipher Block Chaining and Initialisation Vectors if you want your code to be more robust. http://www.javamex.com/tutorials/cryptography/block_modes.shtml
It may be overkill for your needs, but just thought i'd mention it.

Decrypt SHA encrypted string [duplicate]

This question already has answers here:
How to decrypt a SHA-256 encrypted string?
(4 answers)
Closed 9 years ago.
I have encrypted a string using the above code.
public String encrypt(String generatedKey)
{
try {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(generatedKey.getBytes("UTF-8"));
byte digest[] = md.digest();
return (new BASE64Encoder()).encode(digest);
}
catch (Exception e) {
return null;
}
}
Similarly i need a code to decrypt the above generated code. How can i do this?
SHA is a digest algorithm, not an encryption algorithm. Digest values are not decryptable. That's why they are secure. Two different inputs may give same digest values. But it is a very little possibility. For sha256 it is 1/(2^256).
Output of digest algorithms have constant length. For SHA256 it is always 256 bit, regardless of your input length, 1 bit or 100 Gbs. If we could decrypt 256 bit digest value and have the original 1Gb input back, we would never need compression algorithms :)
Message digests produce a small "fingerprint" of a larger set of data. It's a one way procedure.
What you probably is looking for is encryption.
Key key = new SecretKeySpec(secret.getBytes(), ALGORITHM);
// Encrypt
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal(plainText);
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, key)
byte[] decryptedData = cipher.doFinal(encryptedData);
Where ALGORITHM can be one of
http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html#Cipher
I am pretty sure that we cannot decode a SHA encrypted string directly.
See this for a clear explanation: How to decrypt SHA-256 encrypted String?

Java public private key decryption issue

I am trying to encrypt and decrypt a message as mentioned in the below code. Basically I want to encrypt a message with a public key and convert that encrypted message from byte array to String. And decrypt this string into original text. Here are the both methods. Here encryption works fine but decryption fails (error is "Data must start with zero"). I think this is causing because I convert encrypted byte array into String.
How do I solve this? (I want to have encrypted byte array as string and use it for decryption) Is there any other approach (with public and private keys)
public static String getEncryptedMessage(String publicKeyFilePath,
String plainMessage) {
byte[] encryptedBytes;
try {
Cipher cipher = Cipher.getInstance("RSA");
byte[] publicKeyContentsAsByteArray = getBytesFromFile(publicKeyFilePath);
PublicKey publicKey = getPublicKey(publicKeyContentsAsByteArray);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plainMessage.getBytes());
return new String(encryptedBytes);
} catch (Throwable t) {
}
}
public static String getDecryptedMessage(
String privateKeyFilePath, String encryptedMessage)
{
byte[] decryptedMessage;
try {
Cipher cipher = Cipher.getInstance("RSA");
byte[] privateKeyContentsAsByteArray = getBytesFromFile(privateKeyFilePath);
PrivateKey privateKey = getPrivateKey(privateKeyContentsAsByteArray);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
decryptedMessage = cipher.doFinal(encryptedMessage.getBytes());
return new String(decryptedMessage);
} catch (Throwable t) {
}
If you look at this page (http://www.wikijava.org/wiki/Secret_Key_Cryptography_Tutorial) you will need to do base-64 encoding to turn the bytes into a string, then to decrypt it you would just decode it then decrypt.
Base-64 encoding uses the first 7 bits of a byte, to make something that is printable or emailable, for example.
UPDATE:
I made a mistake, there are 64 characters that it would be encoded in, again, in order to make it easier to use as something printable.
Why don't you treat the message as byte array from encryption to decryption? Why changing it to String in the middle? (I know it seems like a question, but it's actually an answer...)
Using RSA directly on unformatted data may leave your application vulnerable to an adaptive chosen ciphertext attack. For details please see Chapter 8, pages 288-289, of the Handbook of Applied Cryptography, a freely-available book from CRC Press. (It's well worth buying the bound edition, if you're really interested in cryptography -- you'll be stunned at the quality for the price.)
Because of this attack, most protocols that integrate RSA use RSA for encrypting randomly-generated session keys or signing hash functions with outputs that ought to be indistinguishable from random, OR using very carefully formatted messages that will fail to be correctly interpreted. (See Note 8.63 in HAC for details.)

Categories