Possible error of the encoding of a message - java

I'm trying to make a simple class which display the message to be coded, the encoded message and the decoded message. But I think that my class is wrong. Here is what I did with the explenation of the function:
String messageToEncode = "a";
try {
//We create a key
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
// We choose method AES in order to encode message
Cipher cipher = Cipher.getInstance("AES");
//We enter the encoding phase of the message
cipher.init(Cipher.ENCRYPT_MODE, key);
//We transform the encoded message from String to Byte
byte[] res = cipher.doFinal(messageToEncode.getBytes());
//We transform the encoded message from Byte to String. Here we have a coded message that needs the key to be read
String codedMessage = Base64.getEncoder().encodeToString(res);
//We enter the decoding phase of the message
cipher.init(Cipher.DECRYPT_MODE, key);
//We decode the encoded message
byte[] res2 = cipher.doFinal(Base64.getDecoder().decode(codedMessage));
//We display the decoded message
String decodedMessage = new String(res2);
//We display the message sent at the beggin
System.out.println("Message:" + messageToEncode);
//We display the encoded message
System.out.println("Encoded message:" + codedMessage);
//We display the decoded message
System.out.println("Decoded message:" + decodedMessage);
//We recover the key
byte[] keyByte = key.getEncoded();
//We display the key
System.out.println(Base64.getEncoder().encodeToString(keyByte));
} catch (Exception ex) {
}
}
And I have at the output:
Message to code:a
Encoded message:oIgc5kuv8ROgCqNkpndCPQ==
Decoded message:a
u645vsT3RP5FRHLtGfIhrA==
I think that my class is false because the message to be coded is composed of only one letter but the encoded message is composed of 26 letters ! Shouldn't it be composed of also one letter ? So I would like if what I got is normal please.
I thanks in advances for anyone who takes time to help me.
P.S: I use JDK 12 with NetBeans 11.

What you see is what you should expect.
AES is a block cipher: it encrypts data in blocks of 16 bytes. If the input data is not an even multiple of 16 bytes, it's padded. I would expect the original length of the data to be included somehow so it wouldn't surprise me if the output from encrypting a single byte in AES was a little longer than 16 bytes.
Base64 outputs 4 bytes for every block of 3 bytes in the input. In 16 input bytes there are 6 such blocks, so the encrypted message becomes at least 24 bytes in base64.

Related

Java AES-128 ECB encryption

I have received a message from AT5 (Atrack) unite encoded with AES-128 ECB encryption and this is the key in hex (33333838000000000000000000000000)
the original key is in string "3388"
and this is the cipher that i received but i convert it to hex
Whenever I try to decrypt the message, I face this exception:
Error while decrypting: javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
Here is my code:
orginalString
?U??????}MgD?R???`????
#?j????g:N???#???$=???r?u?>~??Qh,?N?????3?8:?-E??;?b<J??I?v{?'o?m[?|?CY?n????K????>?ã??<?,???? ??f?'???}&?}?7r%??93??!u?0D?3Ig|??%'????*??`?Y^?M?4J?I?>?tIu???;?RR;2??{nrl??us
?R?F?oi????
String strToDecrypt = "E255898281B7C0B67D4D6744DB52FA1789F760B99B86A40A1F238B6ABCC690C1C9673A4E07EA03F79B40B2ACC8243DD098F4B10E72BA75CE3E7EE50F935168042CC74EA2F4BBE3D833F4383AE02D45C0119C3BC8623C4AD5189249FAB0BB767BA2276FB56D5BF27CE017435901DD8F6EC9DECD05B74BF51A99B1933E89C3A3AD06CA893CE72CDDEAE5F509E2D01B66A02717AADD0C917D26BA7DB1377225CCF23933E5F92175BD3044C63349677CEBC6251B27BE1FC9FD7FC32A0785F36019C2595E1EE783B64DB2344A14CE49C63EAB74174975FCE1C53BD95252113B0332CBD7A37B6E726CCFF675730DB052B14605E36F69AF11E8F3D2002D8BABEFFF508187E4C176329A3ABE08CF2B9A9F4812CF4084BACE87AD116F49C2ACD449767E758CE9184C60268AE3AAEADA052C91BF16241682E333671AC209D5BDC34CFD2B2D0C8D6D795D36A5FBD707FB56F71B3740BA86B1CEAC6E784E8E2B999CC6C9260A13F697A115C80F29C5AA38E95964731073CB051BB8A201EBAE6443A057AA69CFF41C9A593F88E2D6A712107EABCDE7042134F818268BF31896072C1B399B878BACCECC096F79A8D1835C2766EA639341E4AB22820D5AAD0F202BC896BD6C6F4D1FB1873C5BE06278D11E67F577D0120E054971088E7DB7E3A8139B20C6E22B86205BC0F4778A1DA0D6E50416FCE55DBC576A9F907FC706148204CA3C79993A4F37756427871C12EB379B4BFA0A518FFCA2BC698B1CA68AC9B3548B241C12669CEFAC9C8ECDB7B5A8149B";
secretKey = new SecretKeySpec("33333838000000000000000000000000".getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] cipherResult = cipher.doFinal(Base64.decodeBase64(strToDecrypt));
Your strToDecrypt seems to be hex encoded, not base64 encoded .. so you need to hex decode it before decrypting, instead of base64 decoding. But actually you encrypted data if hex decoded, would also end up having an invalid size. Your hex string is 1200 hex chars = 600 bytes = 37.5 AES blocks of 16 bytes .. Something seems to be wrong with your data ..
Also, your key is also Hex encoded, so you need to hex decode it .. not just do a getBytes(..).

'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 can't decrypt string encrypted in PHP

Hello everyone i have string encrypted in PHP by openssl_encrypt with algorithm 'aes-256-cbc'
Key: C4E30455853D4949A8E91B2C366BE9DE
Vector: 5686044872102713
Encrypted string: ak9YSTd6RXU5TENocUxQUGxieVhpZ3VqSlFiQUdndGZrbVJvbEliTGZjZz0=
And here is my Java function for decrypt:
public static String Decrypt_AES_FromBase64(String AEncryptedText, String AKey32Bytes, String AVectorNum16Bytes) {
try {
byte[] vEncryptedBytes = Base64.getDecoder().decode(AEncryptedText);
Key SecretKey = new SecretKeySpec(AKey32Bytes.getBytes(), "AES");
IvParameterSpec vSpec = new IvParameterSpec(AVectorNum16Bytes.getBytes());
Cipher vCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
vCipher.init(Cipher.DECRYPT_MODE, SecretKey, vSpec);
return new String(vCipher.doFinal(vEncryptedBytes));
} catch (Exception e) {
Common.mContext.getLogger().log(e.toString());
return "";
}
}
When i try to decrypt i have error:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
Can somebody tell what the wrong?
The encrypted string AKey32Bytes is double Base64 encoded.
Instead of AKey32Bytes.getBytes() you need to double Base64 decode the encrypted data to binary.
Encrypted string:
ak9YSTd6RXU5TENocUxQUGxieVhpZ3VqSlFiQUdndGZrbVJvbEliTGZjZz0=
After one Base64 decode:
jOXI7zEu9LChqLPPlbyXigujJQbAGgtfkmRolIbLfcg=
After a second Base64 decode (displayed in hex because it is not binary):
8CE5C8EF312EF4B0A1A8B3CF95BC978A0BA32506C01A0B5F9264689486CB7DC8
That is what needs to be provided to the decryption function.
The decrypted result is:
(in hex) 257531362A2179704B40577255516272
(in ASCII): "%u16*!ypK#WrUQbr" (all valid ASCII characters)
Note: there is a full block of PKCS#7 padding (in hex): 10101010101010101010101010101010
As much as it pains me to say this, from the correct padding I can assume the decryption was successful.
See Cryptomathic AES CALCULATOR

Generating a Key from the first 10 Characters of a Base64 String

Let's say I have a Base64 String:
data = AOUTl5C2QV2xFRPlzKR0Ag==
I want to generate a Key in Java (Android) from the first 10 characters of this Base64 String and then use it to AES-Decrypt a Message sent from the Server. To do this, I use the below code:
String firstTen = data.substring(0, 10);
byte[] decodedBytes = Base64.decode(firstTen, Base64.DEFAULT);
SecretKeySpec key = new SecretKeySpec(decodedBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] original = cipher.doFinal(Message_to_Decrypt, Base64.DEFAULT));
But then I can a Java.security.Exception:
java.security.InvalidKeyException: Key length not 128/192/256 bits.
Is there a way that I can get a valid Key which I can use for AES decryption from the first 10 Characters of a Base64String?
Extend the 10 characters with a hash function or better yet PBKDF2 (Password Based Key Derivation Function 2).
You really need to provide a key of an expected length, AES keys can be 128, 192 or 256 bytes long. While some AED implementations may null pad the key do not rely on that, it is not part of the standard.
The error message says: Key length not 128/192/256 bits.
You are using 10 Characters, each char is 8 bits. So 10*8=80. Try with 16 characters (128/8=16).

BadPaddingException : Data must start with zero

I implemented data encryption/decryption with RSA. It works if I just encrypt/decrypt locally, however if I send my encrypted data I get BadPaddingException: Data must start with zero.
In order to send my data over network I need to change it from byte array to String (I'm sending it in a header) on the client side and then retrieve it and change it back to byte array on the server side.
Here's my code for local encryption/decryption (I'm using private key to encrypt and public key do decrypt):
// Encryption:
String message = "HELLO";
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, privateKey); // privateKey has type java.security.PrivateKey
byte [] encryptedBytes = rsa.doFinal(message.getBytes());
// Decryption:
rsa.init(Cipher.DECRYPT_MODE, publicKey); // type of publicKey: java.security.PublicKey
byte [] ciphertext = rsa.doFinal(encryptedBytes);
String decryptedString = new String(ciphertext, "UTF-8");
DecryptedString and message are the same and everything works fine.
Then I use the same code on the client side just for encryption plus I change ciphertext to a String using:
String encryptedString = new String(ciphertext, "UTF-8");
And on the server side I do:
String message = request.getHeader("Message");
byte [] msgBytes = message.getBytes("UTF-8");
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.DECRYPT_MODE, publicKey);
byte [] decryptedMsg = rsa.doFinal(msgBytes);
String decryptedString = new String(decryptedMsg, "UTF-8");
This doesn't work and I get BadPaddingException.
I have tried using different instance of cipher, e.g. "RSA/ECB/PKCS1Padding" or "RSA/ECB/NoPadding" but this didn't help. I also tried converting Strings using BASE64 but then I get a different exception: IllegalBlockSizeException.
I know I probably do sth wrong with converting Strings into byte arrays and vice versa, but I just can't figure out the correct way of doing that. Please help!
You can't just convert arbitrary binary data (the encrypted text) into a String. If you want to send the data as text, you need to use some sort of binary -> text encoding like Base64.

Categories