Length of the decryption(RSA) string is changing - java

I have a String which is encrypted with RSA algorithm, When i was trying to decrypt it's working fine.The decryption output will be in byte[].
My question is when i trying to convert the byte[] to a new String(decrypted String) it's getting different length .
If i try with a random string the length is remains same.But if i decrypt the
generated aesKey(AES) the length of the byte[] is changing while conversion of new String(decrypted String).
Why i am getting different length incase of aesKey decryption???
Is there any difference between "random string" and "generated aeskey"
String ==
"t8xypyI6gKlKTkt4Qec7FCor4EpukZXqYQcIDm6YvbtRB9+YBrX0CqyoHOHN91T8RBQS/JD2osbf4ao9Y"SgNbzhfDa2NpJKMEIBWH4TNlF4Ngb8yWdSm3hz3l8FdeFUIy3pyCxkLjU8n4VAxsmgoIQbgd7DJuPiSMZBA9/IVlcCfo/tZjMtSkezITtoT5aVvLxLaTsp08UREdalvXxb5USKi3cAEdqR9TmLJxB004IMv5Eiuvdmcc3fJzO6mnwiHPuGKArd9LjjiqbPQ75uc8NDOFrvleLc5KwSuThS5Xx7tR1qfoX6qefh6SD7FRk5UzyCEnv+eD+mCQ588Jam1A==";
****The above string is encrypted form of the aesKey(generated by KeyGenerator)
If i decrypt this string by using RSA----
private String decrypt(String text, Key privatekey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
cipher.init(Cipher.DECRYPT_MODE, privatekey);
dectyptedText = cipher.doFinal(Base64.getDecoder().decode(text));
System.out.println(dectyptedText.length); //32
System.out.println(new String(dectyptedText).length()); //30
System.out.println(new String(dectyptedText).getBytes().length); //60
return new String(dectyptedText);
}
above, The length is changing in byte string conversions.
Suppose if i encrypt and decrypt with a normal string the lengths are not changing????why????

Likely, the decryptedText contains funny bytes. The documentation says:
public String(byte[] bytes)
Constructs a new String by decoding the specified array of bytes using the platform's default charset. The length of the new String is a function of the charset, and hence may not be equal to the length of the byte array.
The behavior of this constructor when the given bytes are not valid in the default charset is unspecified. The CharsetDecoder class should be used when more control over the decoding process is required.

Related

Decrypting CommonCrypto encrypted Base 64 encoded string in Java (AES/CBC/PKCS7Padding)

I am trying to decrypt a String with a known key in Java using standard Cipher API.
The encrypted String comes from a Web Service using the standard CommonCrypto Library which responds with some statistics as encrypted strings at regular intervals.
The specs are AES/CBC/PKCS7Padding with KeySize = 32 Bytes and BlockSize = 16 Bytes, and Encoding UTF-8 (raw) & Base64. I intend to write a Java client that can request these statistics, decrypt them and store them for later analyses.
Question 1. Does the CommonCrypto automatically pad keys with extra chars if the key is short? For instance less than 16 Bytes or 32 Bytes.
Question 2. What encoding measures should I take to ensure an identical encryption/decryption on both ends?
Example Strings and Key
String message = "mQp9sp8ri1E0V1Xfso1d5g==Mrf3wtaqUjASlZmUO+BI8MrWsrZSC0MxxMocswfYnqSn/VKB9luv6E8887eCxpLNNAOMB0YXv6OS7rFDFdlvC53pCHo3cVZiLJFqgWN/eNiC9p4RMxyFCcOzWrwKzT5P8sy55DwE25DNJkvMthSaxK5zcP1OdLgBiZFOSxYRsX4rBk7VP7p5xr2uTGjRL+jmGgB9u3TmeCNCr8NxGLNt6g==";
String userKey = "123456789";
private static String decrypt (String message, String userKey) throws UnsupportedEncodingException,
NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
ShortBufferException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchProviderException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
if (message.length() >= 48) {
ivFromEncryptedString = message.substring(0, Math.min(message.length(), 24));
messageFromEncryptedString = message.substring(24, message.length());
System.out.println(ivFromEncryptedString);
System.out.println(messageFromEncryptedString);
byte[] data = decodeBase64(messageFromEncryptedString);
byte[] ivData = decodeBase64(ivFromEncryptedString);
paddedKey = padShortKeys(userKey);
byte[] keyBytes = paddedKey.getBytes(CHARSET);
MessageDigest sha = MessageDigest.getInstance("SHA-256"); //key
keyBytes = sha.digest(keyBytes);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
byte [] encrypted = new byte[cipher.getOutputSize(data.length)];
int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
ctLength += cipher.doFinal(encrypted, ctLength);
} catch (Exception e) {
System.out.println(e);
} finally {
return encrypted;
}
}
return null;
}
private static String encodeBase64(byte [] in){
return Base64.getEncoder().encodeToString(in);
}
private static byte[] decodeBase64(String str) throws UnsupportedEncodingException {
return DatatypeConverter.parseBase64Binary(str);
}
Also with the current code status I am getting placehoder characters instead of the desired result.
Thanks in advance folks. :)
CommonCrypto is unclear, which implementation are you using? Apple, Apache, Java Class Cipher or another, please supply a link to it.
Never assume an encryption will pad the key or IV, they should always be provided in the exact length, there is no standard for such padding. If they need padding (they shouldn't) do it yourself.
Typically if encrypted data needs to be expressed as a character string Base64 encoding is used.
As James states, for one-shot encryption just use doFinal(ByteBuffer input, ByteBuffer output) which
encrypts or decrypts data in a single-part operation.
Note: A 9 digit key only has about 33-bits of security which is not close to sufficient. Simple using a hash function is insufficient for deriving an encryption key from a password, instead PBKDF2 or Argon2 should be used.

Cipher Java not encrypting correctly

I am currently playing with Cipher to create a solution using a key that is always the same. I know this is not the most secure solution but it is what I have been asked to do. I am supposed to use AES256 and EBC, but I can not encrypt correctly. The problem is that I've got unknown characters.
private static String encrypt(String text) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException
{
String keyString = AESEncryption.convertToUTF8("8DJE7K01U8B51807B3E17D21");
text = AESEncryption.convertToUTF8(text);
byte[]keyValue = Base64.getEncoder().encode(keyString.getBytes(StandardCharsets.UTF_8));
Key key = new SecretKeySpec(keyValue, "AES");
Cipher c1 = Cipher.getInstance("AES/ECB/PKCS5Padding");
c1.init(Cipher.ENCRYPT_MODE, key);
byte[] encodedText =Base64.getEncoder().encode(text.getBytes(StandardCharsets.UTF_8));
System.out.println("Encoded text: "+new String(encodedText,StandardCharsets.UTF_8));
byte[] encVal = c1.doFinal(encodedText);
System.out.println("Encoded val: "+new String(encVal,StandardCharsets.UTF_8));
return new String(encVal);
}
Edit: Sorry first time asking. I will give you the full scope. Afterwards I try to decrypt with the following code(I know that I have repeated code, I will clean it) But when I decrypt the output obtained by the encrypt method I recieve the following error. The message I am trying to encrypt and decrypt is "Hola"
public static String desEncrypt(String text) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
String keyString = AESEncryption.convertToUTF8("8DJE7K01U8B51807B3E17D21");
byte[] keyValue = Base64.getEncoder().encode(keyString.getBytes(StandardCharsets.UTF_8));
Key key = new SecretKeySpec(keyValue, "AES");
Cipher c1 = Cipher.getInstance("AES/ECB/PKCS5Padding");
c1.init(Cipher.DECRYPT_MODE, key);
byte[] encodedText = Base64.getDecoder().decode(text.getBytes(StandardCharsets.UTF_8));
byte[] encVal = c1.doFinal(encodedText);
System.out.println(new String(encodedText));
return new String(encVal,StandardCharsets.UTF_8);
}
And the error:
Encoded text: aG9sYWNraXNqbWRlaXJncw==
Encoded val: ???D>??|??i9???Fd?\Zz?A?-
Exception in thread "main" java.lang.IllegalArgumentException: Illegal base64 character -3d
at java.util.Base64$Decoder.decode0(Unknown Source)
at java.util.Base64$Decoder.decode(Unknown Source)
at AESEncryption.desEncrypt(AESEncryption.java:63)
at AESEncryption.main(AESEncryption.java:79)
Thank you very much, and forgive for not providing all the info needed
Your code makes no sense: converting a String to UTF8 and getting back a String makes no sense: a String contains characters. Not bytes.
Encoding a key to base64 doesn't make much sense either. Encoding the plain text to base64 is useless, too.
You need base64 encoding when you have random, binary bytes, and you want to transform them to printable english characters. Only then.
So the process should be:
transform your key to bytes (using String.getBytes(UTF_8)). Unless the String key is in fact a base64-encoded byte array, in which case you need to base64 decode it;
transform the plain text to bytes (using String.getBytes(UTF_8));
encrypt the bytes you got from step 2, using the key obtained from step 1. You obtain completely "random" bytes. These bytes don't represent characters encoded in your platform default charset. So transforming them to a String using new String(bytes) doesn't make any sense, and is a lossy transformation.
Just return the result as a byte array, or if you really want printable characters, base64-encode the bytes, and return the string you obtain from this base64 encoding.
To decrypt, use the reverse process:
Use the same thing as in step 1 above to get the key
take the bytes obtained from step4 above (if you chose to return a byte array), or base64-decode the string, to obtain the original random binary bytes
decrypt the bytes obtained from step 2, using the key obtained from step 1. You get a byte array representing the UTF8-encoded characters of the original plain text.
Use new String(decryptedBytes, UTF_8) to transform this byte array to a String.

AES CBC No padding gives extra characters in decrypt JAVa

I am trying to encrypt and decrypt using AES/CBC/NoPadding in JAVA. I did the encryption in both JAVA and PHP using (mcrypt) and got the same result, using the same key and iv. However, when I try to decrypt in JAVA, I get the word correctly but always with extra characters. I read other questions and found that I need to add padding. So I added Padding5 but got the same result. Anyways, I need it without padding because that is how it works in PHP. Any help is appreciated. My code is below and the result is here:]2
public class RijndaelCrypt {
//private String key = "2a4e2471c77344b3bf1de28ab9aa492a444abc1379c3824e3162664a2c2b811d";
private static String iv = "beadfacebadc0fee";
private static String hashedKey = "6a2dad9f75b87f5bdd365c9de0b9c842";
private static Cipher cipher;
public static String decrypt(String text) throws UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
SecretKeySpec keyspec = new SecretKeySpec(hashedKey.getBytes("UTF-8"), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decodedValue = Base64.decode(text.getBytes("UTF-8"));
byte[] decryptedVal = cipher.doFinal(decodedValue);
return new String(decryptedVal);
}
public static String encryptNew(String data) throws Exception {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes("UTF-8");
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(hashedKey.getBytes("UTF-8"), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes("UTF-8"));
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return DatatypeConverter.printBase64Binary(encrypted);
}
public static void main (String [] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
String data = "Hello";
System.out.println("New Decrypted: " + RijndaelCrypt.decrypt(RijndaelCrypt.encryptNew(data)));
System.out.println("New Encryption: " + RijndaelCrypt.encryptNew(data));
}
}
The PHP mcrypt wrapper (or underlying mcrypt library) pads with zero bytes up to the block length (zero to 15 padding bytes, if 16 is the block size of the cipher). After that the blocks are encrypted by the cipher.
When decrypting in Java you need to manually remove any zero bytes from the right hand side of the plaintext after decryption using NoPadding. The zero valued padding bytes can of course be seen when hex-encoding the decrypted plaintext. However when outputting a string the zero bytes are either left out or converted to a replacement character (depending on the character set and terminal).
Note that the PHP zero padding has one big drawback: if the plaintext ends with one or more zero valued bytes it could be stripped from the decrypted plaintext by any unpadding routine. This is why PKCS#7 padding (which pads 1 to 16 bytes) should be preferred.
Also note that PHP actually needs rtrim("\0") to remove the zero bytes itself; mcrypt just leaves them there, but they generally won't be printed.
Note that Bouncy Castle crypto libraries also has ZeroPadding as option. However, this is zero padding of 1 to 16 bytes (i.e. it always pads/unpads) so it is incompatible with the padding defined used by PHP mcrypt and may fail if the size of the plaintext can be divided by the block size of the cipher.

Encryption and Decryption using Blowfish Error - Input length must be multiple of 8 when decrypting with padded cipher

I am able to encrypt data however when decrypting it i am getting the following error:
Error
HTTP Status 500 - Request processing failed; nested exception is javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
Here is my Encryption and Decryption code
//secret key 8
private static String strkey ="Blowfish";
UPDATED
//encrypt using blowfish algorithm
public static byte[] encrypt(String Data)throws Exception{
SecretKeySpec key = new SecretKeySpec(strkey.getBytes("UTF8"), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, key);
return (cipher.doFinal(Data.getBytes("UTF8")));
}
//decrypt using blow fish algorithm
public static String decrypt(byte[] encryptedData)throws Exception{
SecretKeySpec key = new SecretKeySpec(strkey.getBytes("UTF8"), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(encryptedData);
return new String(decrypted);
}
If you run your encrypt and decrypt methods in a main method, it will work. But if the results of encrypt are put into a url and then the url parameter is decrypted, it will fail.
After encryption, the byte array contains values that are outside the character set of URLS (non-ascii), so this value gets encoded when it is stuffed into a url. And you you receive a corrupted version for decryption.
As an example, when I created a string from an encrypted byte array, it looked like this Ž¹Qêz¦ but if I put it into a URL it turns into Ž%0B¹Qêz¦.
The fix, as suggested in other comments, is to add a encode / decode step. After encryption, the value should be encoded to a format which contains ascii characters. Base 64 is an excellent choice. So you return encrypted and encoded value in the url. When you receive the param, first decode then decrypt, and you'll get the original data.
Here are some notes on the implementation.
Use a library like commons codec. It is my weapon of choice, this class specifically http://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/binary/Base64.html.
In the class that does encryption and decryption, have a shared instance of Base64. To instantiate it use new Base64(true); this produces url safe strings.
Your encrypt and decrypt method signatures should accept and return strings, not byte arrays.
So the last line of your encrypt would become something like return base64.encodeToString(cipher.doFinal(Data.getBytes("UTF8"))); You can now safely pass the encrypted value in a url
In your decrypt, you first step is to decode. So the first line would become something like byte[] encryptedData = base64.decodeBase64(encrypted);
I just took your code and added some base 64 stuff, the result looks like this:
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Test {
private static String strkey ="Blowfish";
private static Base64 base64 = new Base64(true);
//encrypt using blowfish algorithm
public static String encrypt(String Data)throws Exception{
SecretKeySpec key = new SecretKeySpec(strkey.getBytes("UTF8"), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, key);
return base64.encodeToString(cipher.doFinal(Data.getBytes("UTF8")));
}
//decrypt using blow fish algorithm
public static String decrypt(String encrypted)throws Exception{
byte[] encryptedData = base64.decodeBase64(encrypted);
SecretKeySpec key = new SecretKeySpec(strkey.getBytes("UTF8"), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(encryptedData);
return new String(decrypted);
}
public static void main(String[] args) throws Exception {
String data = "will this work?";
String encoded = encrypt(data);
System.out.println(encoded);
String decoded = decrypt(encoded);
System.out.println(decoded);
}
}
Hope this answers your questions.
You can't create a String out of random (in this case encrypted) bytes like you're doing in the last line of your encrypt method - you need to create a Base64 encoded string instead (which you then need to decode back to a byte array in the decrypt method). Alternatively, just have your encrypt method return a byte array and have your decrypt method accept a byte array as its parameter.
The problem is with the way you are creating String instances out of the raw encrypted byte[] data. You need to either use binhex encoding like that provided by javax.xml.bind.DatatypeConverter via the parseHexBinary and printHexBinary methods or base 64 using the parseBase64Binary and printBase64Binary methods of the same object.
One other word of advice, never rely on the default mode and padding, always be explicit. Use something like Cipher.getInstance("Blowfish/CBC/PKCS5Padding") depending on what your needs are.

Byte Array -> String -> Byte Array for encrypted text: not the same size

I am trying to encrypt a message, which works and returns it as a byte array. I then convert this byte array to a string, in order to send via a tcp network message. On the other end, I convert the string back to a byte array, however the resulting array is larger and I can't figure out why. I think it may be something to do with the encoding as if I use "MacRoman", I do not have this problem, however the program needs to be able to run on systems which do not support this encoding, so I decided to use UTF-8.
String message="222233332221";
//Encrypt message
byte[] encryptedMsg = encryptString(message, temp.loadCASPublicKey());
System.out.println("ENCRYPTED MESSAGE byte Length: "+encryptedMsg.length);
//Convert to String in order to send
String stringMessage = new String(encryptedMsg);
System.out.println("ENCRYPTED MESSAGE String Length: "+stringMessage.length());
//Convert String back to Byte[] and decrpt
byte[] byteMessage = stringMessage.getBytes("UTF-8");
System.out.println("ENCRYPTED MESSAGE byte Length: "+byteMessage.length);
Outputs:
ENCRYPTED MESSAGE byte Length: 256
ENCRYPTED MESSAGE String Length: 235
ENCRYPTED MESSAGE byte Length: 446
Can any one please point me in the right direction as to why the resulting byte array is 446 bytes not 256 bytes.
The encryptString part is as follows. I believe this returns a byte array in UTF-8?
private static byte[] encryptString(String message, Key publicKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherData = cipher.doFinal(message.getBytes("UTF-8"));
return cipherData;
}
Managed to fix it using Base64.
byte[] encryptedMsg = Base64.encodeBase64(encryptString(message, temp.loadCASPublicKey()));
System.out.println("ENCRYPTED MESSAGE byte Length: "+encryptedMsg.length);
//Convert to String in order to send
String stringMessage = new String(encryptedMsg, "UTF-8");
System.out.println("ENCRYPTED MESSAGE String Length: "+stringMessage.length());
//Convert String back to Byte[] and decrpt
byte[] byteMessage = Base64.decodeBase64(stringMessage.getBytes("UTF-8"));
System.out.println("ENCRYPTED MESSAGE byte Length: "+byteMessage.length);
It's an encoding issue.
1) You have a byte array. It contains bytes
2) You convert it to a string. As soon as you do this, you have a UTF16 encoded String. So you have taken the bytes and changed them to characters.
3) You now convert those characters back to bytes. But if the original bytes were not UTF8 or UTF16, you might not have the same number of bytes. If the default encoding of the platform is MacRoman, then in step 3 you are translating your UTF16 String into bytes, but treating the characters as MacRoman.
I guess there is a good reason of doing encryption manually, however just in case... Do you consider using SSLSocket instead?

Categories

Resources