Hex.encodeHexString after AES encryption? - java

I am receiving encrypted message from server. They are using the following code for encryption
public static String encrypt(String plaintext, String key) throws Exception {
byte[] pk = Hex.decodeHex(key.toCharArray());
X509EncodedKeySpec spec = new X509EncodedKeySpec(pk);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(spec);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = cipher.doFinal(plaintext.getBytes());
return Hex.encodeHexString(encrypted);
}
My doubt is, they are using Hex.encodeHexString(encrypted). why they are using this line of code? I know it converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. can't they send the encrypted text directly with out using Hex.encodeHexString? Can any one please give any idea?

Encrypted data is an array of arbitrary bytes. If it has to be passed by means supporting only printable strings such as XML or JSON this will be a problem. In order to deal with that binary data is encoded to form a printable string. However, Base64 encoding is used more often for this purpose.
Then prior to decrypting string has to be converted back to byte array with Hex.decode().

Related

Is There a Way to Decode an AES Key from Base64 to UTF-8 String?

I am generating an AES-256 SecretKey using:
KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES");
aesKeyGen.init(256);
SecretKey secretKey = aesKeyGen.generateKey();
clientAesKey = secretKey.getEncoded();
Is there a way that I can see the array of bytes as a string? Only way that works for me is this:
Base64.getEncoder().encodeToString(clientAesKey);
If I print it and copy the string, and try to decode it using an online Base64 decoder, it complains that the string is not UTF-8.
I know it defeats the purpose of encryptions, but is there a way that I can assign a static 32-characters long string as a secret key?
This is a simple way to 'visualize' a key:
System.out.println(new java.math.BigInteger(1, keyBytes).toString(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.

why decrypted data is different from original data which was encrypted using java?

I am using AES encryption and decryption using java. And I use Appache commons library for conversion from string to byte and vice versa. But when I decrypt data then it is different from the input data that was encrypted using same key? why is so
Here is my code:
public static void main(String[] args) throws Exception {
String key="this is key";
String message="This is just an example";
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(Base64.decodeBase64(key)));
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted= cipher.doFinal(Base64.decodeBase64(message));
String encryptedString=Base64.encodeBase64String(encrypted);
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original =
cipher.doFinal(Base64.decodeBase64(encryptedString));
System.out.println(Base64.encodeBase64String(original));
}
I get the output "Thisisjustanexamplc=" where it should have been "This is just an example". what I need to change in my code. Thanks in advance
You are base-64–decoding your plain text message. You should use message.getBytes(StandardCharsets.UTF_8) (or some other encoding) to convert to bytes instead. Base-64 encode the result of the encryption operation, then base-64 decode it before decrypting. Use new String(original, StandardCharsets.UTF_8) to convert the result of the decryption operation back to text.
In other words, use a character encoding to convert between text and bytes. Use base-64 encoding and decoding to encode binary data in a text form.

Java RSA Encryption

I am trying to encode a simple String "test" back and forth.
public static String encode(Key publicKey, String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] byteData = data.getBytes(); // convert string to byte array
Cipher cipher = Cipher.getInstance(ALGORITHM); // create conversion processing object
cipher.init(Cipher.ENCRYPT_MODE, publicKey); // initialize object's mode and key
byte[] encryptedByteData = cipher.doFinal(byteData); // use object for encryption
return new String(encryptedByteData); // convert encrypted byte array to string and return it
}
public static String decode(Key privateKey, String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] byteData = data.getBytes(); // convert string to byte array
Cipher cipher = Cipher.getInstance(ALGORITHM); // create conversion processing object
cipher.init(Cipher.DECRYPT_MODE, privateKey); // initialize object's mode and key
System.out.println(byteData.length);
byte[] decryptedByteData = cipher.doFinal(byteData); // use object for decryption
return new String(decryptedByteData); // convert decrypted byte array to string and return it
}
However, although the encryption works just fine (ALGORITHM is "RSA"), when trying to decrypt the string I have just gotten from encrypting "test", I get following exception:
javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
Should I split the encrypted bytes in chunks of 256 in order to be able to decrypt it?
You can't reliably convert random bytes to a String. The results will depend on what your default character encoding is on the machine where you run this. With many encodings, the cipher text will be corrupted, and information will be lost.
Modify your code to use a byte[] instead (the result of the 'doFinal()` method.
If you need to convert the byte[] to a character string, use an encoding like Base-64.
From here:
The RSA algorithm can only encrypt data that has a maximum byte length
of the RSA key length in bits divided with eight minus eleven padding
bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.
If you want to encrypt larger data, then use a larger key, for example,
a key with 4096 bits will allow you to encrypt 501 bytes of data.
If you have a long data, you should either split it to data chunks that fits and encrypt / decrypt each of them (not such a good idea) or encrypt / decrypt them using a symmetric algorithm (AES / DES / RC4 / etc.), encrypt the symmetric key with the RSA public key and send both to the other side. (much better idea).
The second approach is a very common approach, since asymmetric encryption algorithms are much more expensive than symmetric algorithms (for both encryption and decryption).

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