JAVA DES Encryption Output Encoding - java

I have DES Encryption Algorithm implementation in JAVA (javax.crypto.Cipher), it is successfully encoding and decoding (most) strings... the problem is that, sometimes, it message specific blocks (since DES uses 8-character blocks in block mode).
In my case, almost always the 3rd block is messed up and rest shows fine.
for example:
key: thisiskey
message to encrypt: Google is an American multinational technology company specializing in Internet-related services
encrypted message (in UTF-8):
mñqè•ÀPŒ�øf"
ߦ\±õ¤ù'È9¢ëyT ÍQEÁ|;ëâÉ÷JWú
Now, when i go and decrypt this, i get this:
Decrypted message:
Google i,í\O¯‹Ýbº-¸�¬ltinational technology company specializHôJ—=ÊÍnternet-related services
As far as i understand the issue, it is due to the fact that UTF-8 CANNOT show all characters and thus, while showing as well as copying for decryption, this problem occurs.
Can anyone suggest me a solution?
Preferably, either a character-set that can handle this, or, a way to convert Binary directly to HEX (that can be output to user) and then Vice Versa (decrypted, after copying/pasting) in JAVA.
EDIT
This is 'approximate' code, not exact (for example encrypted message is not properly paste-able and these are parts of the function, but it should give the idea). Even in base64 encoding , i am unable to get this decrypted properly.
Encrypt Function code:
boolean base64 = true;
key = "thisiskey";
plainText = "Google is an American multinational technology company specializing in Internet-related services";
SecretKeyFactory MyKeyFactory = SecretKeyFactory.getInstance("DES");
byte[] keyBytes = key.getBytes();
DESKeySpec generatedKeySpec = new DESKeySpec(keyBytes);
SecretKey generatedSecretKey = MyKeyFactory.generateSecret(generatedKeySpec);
Cipher generatedCipher = Cipher.getInstance("DES");
generatedCipher.init(Cipher.ENCRYPT_MODE, generatedSecretKey);
byte[] messsageStringBytes = plainText.getBytes();
byte[] encryptedMessage = generatedCipher.doFinal(messsageStringBytes);
String encryptedMessageString = new String(encryptedMessage);
if (base64) {
encryptedMessageString = Base64.getEncoder().encodeToString(encryptedMessageString.getBytes("utf-8"));
}
return encryptedMessageString;
Decrypt Function code:
boolean dbase64 = true;
dkey = "thisiskey";
messageToDecrypt = "mñqè•ÀPŒ�øf\"ߦ\±õ¤ù'È9¢ëyT ÍQEÁ|;ëâÉ÷JWú"; // Message from above code
SecretKeyFactory MyKeyFactory = SecretKeyFactory.getInstance("DES");
byte[] dkeyBytes = dkey.getBytes();
DESKeySpec generatedKeySpec = new DESKeySpec(dkeyBytes);
SecretKey generatedSecretKey = MyKeyFactory.generateSecret(generatedKeySpec);
Cipher generatedCipher = Cipher.getInstance("DES");
generatedCipher.init(Cipher.DECRYPT_MODE, generatedSecretKey);
if (dbase64) {
byte[] decodedBytes = Base64.getDecoder().decode(dencryptedText);
dencryptedText = new String(decodedBytes, "utf-8");
}
byte[] messsageStringBytes = dencryptedText.getBytes();
byte[] encryptedMessage = generatedCipher.doFinal(messsageStringBytes);
String decryptedMessageString = new String(encryptedMessage);
return decryptedMessageString;

"Encrypted message in UTF-8" makes no sense. The ciphertext is binary and not UTF-8. You need to put it into a byte[], not a String.
If you need a String, use Base64 or Hex encoding.
Even in base64 encoding , i am unable to get this decrypted properly.
String encryptedMessageString = new String(encryptedMessage);
if (base64) {
encryptedMessageString = Base64.getEncoder().encodeToString(encryptedMessageString.getBytes("utf-8"));
}
That does not work. You are encoding to Base64 after the data is already broken (by calling new String). Do not put it in a String at all. Go directly from encryptedMessage (the byte[]) to Base64.

Related

AES-256-CBC encrypted with PHP and decrypt in Java

I am in a situation where a JSON is encrypted in PHP's openssl_encrypt and needs to be decrypted in JAVA.
$encrypted = "...ENCRYPTED DATA...";
$secretFile = "/path/to/secret/saved/in/text_file";
$secret = base64_decode(file_get_contents($secretFile));
var_dump(strlen($secret)); // prints : int(370)
$iv = substr($encrypted, 0, 16);
$data = substr($encrypted, 16);
$decrypted = openssl_decrypt($data, "aes-256-cbc", $secret, null, $iv);
This $decrypted has correct data which is now decrypted.
Now, the problem is when I try to do same things in Java it doesn't work :(
String path = "/path/to/secret/saved/in/text";
String payload = "...ENCRYPTED DATA...";
StringBuilder output = new StringBuilder();
String iv = payload.substring(0, 16);
byte[] secret = Base64.getDecoder().decode(Files.readAllBytes(Paths.get(path)));
String data = payload.substring(16);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(), 0, cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); // This line throws exception :
cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
Here it is:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 370 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:346)
at javax.crypto.Cipher.init(Cipher.java:1394)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.sample.App.main(App.java:70)
I have already visited similar question like
AES-256 CBC encrypt in php and decrypt in Java or vice-versa
openssl_encrypt 256 CBC raw_data in java
Unable to exchange data encrypted with AES-256 between Java and PHP
and list continues.... but no luck there
btw, this is how encryption is done in PHP
$secretFile = "/path/to/secret/saved/in/text_file";
$secret = base64_decode(file_get_contents($secretFile));
$iv = bin2hex(openssl_random_pseudo_bytes(8));
$enc = openssl_encrypt($plainText, "aes-256-cbc", $secret, false, $iv);
return $iv.$enc;
and yes, I forgot to mention that my JRE is already at UnlimitedJCEPolicy and I can't change PHP code.
I am totally stuck at this point and can't move forward. Please help out.
EDIT#1
byte[] payload = ....;
byte[] iv = ....;
byte[] secret = ....; // Now 370 bits
byte[] data = Base64.getDecoder().decode(payload);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOfRange(secret, 0, 32), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv, 0, cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] output = cipher.doFinal(data);
System.out.println(new String(output).trim());
Above snippet seems to be working with openssl_encrypt
EDIT#2
I am not sure if this is correct, but following is what I have done and encryption-decryption on both side are working fine.
Encrypt in PHP, Decrypt in JAVA use AES/CBC/NoPadding
Encrypt in JAVA, Decrypt in PHP use AES/CBC/PKCS5Padding
I won't provide a complete solution, but there are a few differences you should take care of
Encoding:
String iv = payload.substring(0, 16);
String data = payload.substring(16);
are you sure the IV and data are the same in Java and PHP (The IV is string?)? If the data are encrypted, they should be treated as a byte array, not string. Just REALLY make sure they are THE SAME (print hex/base64 in php and java)
For the IV you at the end call iv.getBytes(), but the locale encoding may/will corrupt your values. The String should be use only when it's really string (text). Don't use string for binaries.
Simply treat data and iv as byte[]
Key generation according to the openssl
AES key must have length of 256 bit for aes-256-cbc used. The thing is - openssl by default doesn't use the provided secret as a key (I believe it can, but I don't know how it is to be specified in PHP).
see OpenSSL EVP_BytesToKey issue in Java
and here is the EVP_BytesToKey implementation: https://olabini.com/blog/tag/evp_bytestokey/
you should generate a 256 bit key usging the EVP_BytesToKey function (it's a key derivation function used by openssl).
Edit:
Maarten (in the comments) is right. The key parameter is the key. Seems the PHP function is accepting parameter of any length which is misleading. According to some articles (e.g. http://thefsb.tumblr.com/post/110749271235/using-opensslendecrypt-in-php-instead-of) the key is trucated or padded to necessary length (so seems 370 bit key is truncated to length of 256 bits).
According to your example, I wrote fully working code for PHP and Java:
AesCipher class: https://gist.github.com/demisang/716250080d77a7f65e66f4e813e5a636
Notes:
-By default algo is AES-128-CBC.
-By default init vector is 16 bytes.
-Encoded result = base64(initVector + aes crypt).
-Encoded/Decoded results present as itself object, it gets more helpful and get possibility to check error, get error message and get init vector value after encode/decode operations.
PHP:
$secretKey = '26kozQaKwRuNJ24t';
$text = 'Some text'
$encrypted = AesCipher::encrypt($secretKey, $text);
$decrypted = AesCipher::decrypt($secretKey, $encrypted);
$encrypted->hasError(); // TRUE if operation failed, FALSE otherwise
$encrypted->getData(); // Encoded/Decoded result
$encrypted->getInitVector(); // Get used (random if encode) init vector
// $decrypted->* has identical methods
JAVA:
String secretKey = "26kozQaKwRuNJ24t";
String text = "Some text";
AesCipher encrypted = AesCipher.encrypt(secretKey, text);
AesCipher decrypted = AesCipher.decrypt(secretKey, encrypted);
encrypted.hasError(); // TRUE if operation failed, FALSE otherwise
encrypted.getData(); // Encoded/Decoded result
encrypted.getInitVector(); // Get used (random if encode) init vector
// decrypted.* has identical methods

Looking for an encrypt/decrypt AES example for Android

As I am new to encryption and particularly in Java/Android, I am struggling to find tutorials and code that work fine so that I can learn from them but the results.
As in this site: https://www.owasp.org/index.php/Using_the_Java_Cryptographic_Extensions
I couldn't find the problem that hit me with BASE64Encoder class, which seems to be inside package a sun.utils but I can find Base64 class but I could not tweak code so that it could work for me.
Similarly in this
android encryption/decryption with AES
The encryption is done in Bitmap Image I could not realize the same technique in normal text string.
Would someone supply a simple AES encryption/decryption example in Android just showing how to use key,message, encryption and decryption?
I have used this for my project.
'com.scottyab:aescrypt:0.0.1'(ref: [enter link description here][1]
encryption:
String encryptedMsg = AESCrypt.encrypt(password, message);
decryption:
String messageAfterDecrypt = AESCrypt.decrypt(password, encryptedMsg);
if you are more concerned about security go with SHA1 or SHA256.
Hope this helps.
Edit:
In my case, i have to encrypt login password and send it to server over the network.
String userPassword = password.getText().toString();
try {
encryptedMsg = AESCrypt.encrypt(userPassword, Config.secretlogin);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
So in backend, i have decrypted the secure password with the key and in both sides i am using the same AES(Android and backend).
I couldn't find the problem that hit me with BASE64Encoder class,
which seems to be inside package a sun.utils but i can find Base64
class but i could not tweak code so that it could work for me.
You might not be using the right library for Base64. You mention sun.utils, where the link you sent is using:
import org.apache.commons.codec.binary.Base64;
Since Java 8, you can use java.util.Base64, as detailed on Oracle documentation here. It supports Basic encoding, URL encoding and MIME encoding. You can find some examples in this tutorial.
The second example should work for text strings. Replace
byte[] b = baos.toByteArray();
with
byte[] b = yourString.getBytes();
Note that you have to store the key in some way because at some point have to be deciphered
Stored on the device is not a good idea because you're leaving the key to the door. You can store on your server or use a passphrase (fixed or asked the user). I give you a real sample of the last option
private static String getPassphraseSize16(String key) {
if (TextUtils.isEmpty(key)) {
return null;
}
char controlChar = '\u0014';
String key16 = key + controlChar;
if (key16.length() < 16) {
while (key16.length() < 16) {
key16 += key + controlChar;
}
}
if (key16.length() > 16) {
key16 = key16.substring(key16.length() - 16, key16.length());
}
return key16;
}
public static byte[] encodeAES(byte[] message, String passphrase) {
String passphrase16 = getPassphraseSize16(passphrase);
SecretKeySpec secretKey = new SecretKeySpec(passphrase16.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encodedText = cipher.doFinal(message);
return encodedText;
}
public static byte[] decodeAES(byte[] encodedMessage, String key) {
String passphrase16 = getPassphraseSize16(key);
SecretKeySpec secretKey = new SecretKeySpec(passphrase16.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decodedText = cipher.doFinal(encodedMessage);
return decodedText;
}
This example is similar to provided in https://github.com/scottyab/AESCrypt-Android
The encryption and decryption process worked fine after:
I replaced the byte array obtained from :
Bitmap
by byte array of message string as instructed in
android encryption/decryption with AES
So even though my problem is not fully solved, this question should be marked as answered.

Garbled output in AES/CBC/NoPadding Decryption

I'm trying to decrypt text in java that is encrypted using CryptoJS. I've read on other posts that they use different default modes and padding so I set them both(java/cryptojs) both to use aes/cbc/nopadding. I no longer get an exception in java, but I am getting a garbled output during decryption
Encryption(JS):
var parsedLogin = JSON.parse(login);
var publicKey = "abcdefghijklmnio";
var publiciv = "abcdefghijklmnio";
var key = CryptoJS.enc.Hex.parse(publicKey);
var iv = CryptoJS.enc.Hex.parse(publiciv);
var encrypted = CryptoJS.AES.encrypt(parsedLogin.password, publicKey, {iv: publiciv}, { padding: CryptoJS.pad.NoPadding, mode: CryptoJS.mode.CBC});
// send encrypted to POST request
DECRYPT (Java)
String PUBLIC_KEY = "abcdefghijklmnio";
String PUBLIC_IV = "abcdefghijklmnio";
byte[] byteArr = PUBLIC_KEY.getBytes();
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
final SecretKeySpec secretKey = new SecretKeySpec(byteArr, "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(PUBLIC_IV.getBytes()));
byte[] parsed = Base64.decodeBase64(encrypted.getBytes());
//byte[] parsed = DatatypeConverter.parseBase64Binary(encrypted);
byte[] fin = cipher.doFinal(parsed);
String decryptedString = new String(fin);
The result that I'm getting is like this: Š²Û!aå{’`#"Ûîñ?Œr˜krÆ
I have already tried changing the CHARSET in the getBytes() to US-ASCII, UTF-8 and UTF-16 but all this does is change the garbled text
I have also tried using othe blocking modes and paddings but they failed at the js level. I just need a simple encryption method right now.
NOTE:
Ignore the security issues...like having the key exposed in js, etc. I'll be handling those later..
You shouldn't be able to use AES CBC without padding unless the password is always 16 bytes. It probably applies some sort of default padding that may or may not be a good idea.
Anyway: you need to pass your key and iv to CryptoJS as a WordArray; if you give it a string it will assume you're giving it a passphrase and derive a different key from that. As such, your Java decryption code will be using a different key/iv pair. You can create a WordArray from your strings using
var key = CryptoJS.enc.Utf8.parse("abcdefghijklmnio")
var iv = ...

Original text, Java-run decryption and Android-run decryption are all (slightly) different

I'm attempting to run a simple encryption/decryption Android app and testing it in Java as well. I've searched and haven't found an answer that's related to my problem.
Essentially, the encrypting and decrypting is working for the most part, except the text after decryption in Java and Android are both slightly different than the original text that became encrypted. For example, how's it going? is decrypted as howsitgoing= in Java and howsitgoAAAA in Android.
Here's the Java code:
KeyStore ks = null;
try {
ks = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream is = new FileInputStream("./raw/./akeystore");
ks.load(is, "a".toCharArray());
} catch (Exception e) {
e.printStackTrace();
}
Certificate cert = ks.getCertificate("akeypaircer");
RSAPublicKey publicKey = (RSAPublicKey)cert.getPublicKey();
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
c.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] decodedString = Base64.decodeBase64("how's it going?".getBytes("UTF-8"));
byte [] cipher = c.doFinal(decodedString);
RSAPrivateKey privateKey = (RSAPrivateKey)ks.getKey("akeypair","a".toCharArray());
c.init(Cipher.DECRYPT_MODE, privateKey);
byte [] decrypted_cipher = c.doFinal(cipher);
byte[] encodedBytes = Base64.encodeBase64(decrypted_cipher);
System.out.println(new String(encodedBytes, "UTF-8"));
I'm assuming it has to do with character encoding but I know nothing about that...
I'm also guessing that maybe the difference between Java and Android exists because I have to use two different keystore types (BKS for Android and JKS for Java).
This is patently wrong:
Base64.decodeBase64("how's it going?".getBytes("UTF-8"))
"how's it going?" is not a base 64 string, and you cannot base 64 decode it. The idea is to base 64 encode the ciphertext if you require a ciphertext represented as character string. For the plaintext, you need to convert from a character string to bytes. But you already did that by encoding it as UTF-8.
In short:
you can create text from bytes by performing encoding (e.g. base 64, base 32, hexadecimal encoding)
you can create bytes from text by performing character-encoding (e.g. ASCII, ISO 8859-1 "latin", UTF-8 and UTF-16BE or LE)

How to decrypt Triple Des CryptoJS values in Java class

I was asked to encrypt some text from client side ( web ) before sending it to server side ( java )
So i try to use CryptoJS library for client side.
I encrypt it like this :
var key = "aaaaaaaaaaaaaaaaaaaaaaaa";
var value = "KF169841";
var encryptedString = CryptoJS.TripleDES.encrypt(value, key);
console.log(encryptedString.toString());
And i get something like this : U2FsdGVkX19eYFFHgYGCr3v9/skTOKVp0pLWRNK9JTg=
I use this encryptedString and key in other Decrypt tool online ( Which also use CryptoJS ) and got back exact value KF169841.
After sending this value and key to server ( well key isn't sending directly to server though but for test, it is ), i need to decrypt it using Java.
But i quite don't know how to decrypt it. I'm tried some code from google search but it end up wrong padding if use DESese or get wrong value if i use ECB/NoPadding.
I did try to something like setting sfg for CryptoJS side like:
mode: CryptoJS.mode.EBC,
padding: CryptoJS.pad.NoPadding
But they got javascript exception ( a is not define )
So any have any experience with CryptoJS can help me decrypt this one using java ?
=============================================================
UPDATE : Sorry here my server side code i'm using
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString, String myEncryptionKey) {
String decryptedText = null;
try {
byte[] keyAsBytes = myEncryptionKey.getBytes("UTF8");
KeySpec myKeySpec = new DESedeKeySpec(keyAsBytes);
SecretKeyFactory mySecretKeyFactory =
SecretKeyFactory.getInstance("DESede");
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
SecretKey key = mySecretKeyFactory.generateSecret(myKeySpec);
cipher.init(Cipher.DECRYPT_MODE, key);
// BASE64Decoder base64decoder = new BASE64Decoder();
// byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] encryptedText = org.apache.commons.codec.binary.Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
According to the documentation, your encryptedString variable contains structured data that must be split apart to be sent to Java code. You will need to send encryptedString.iv and encryptedString.ciphertext to your Java code. If you continue to use passwords (see below), you will need to send encryptedString.salt as well.
If you pass your key as a string it will be interpreted as a password and a key will be derived from it. If you actually want to pass an explicit key, follow the documentation and specify the IV and key as suggested by the code snippet below. If you stick with supplying a password, then you must figure out the derivation scheme and use the same process in your Java code.
// Code snippet from http://code.google.com/p/crypto-js/#Custom_Key_and_IV
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });
</script>
Regarding your Java code, it looks mostly OK (although there is plenty of room for error with string conversions). However, you probably want to convert your key from hex to binary rather than grabbing the bytes:
byte[] keyAsBytes = DatatypeConverter.parseHexBinary(myEncryptionKey);
This assumes you alter your JavaScript code to pass the literal key value.
You will also need to switch to DESede/CBC/PKCS5Padding and pass an IVParameterSpec object to your Cipher.init call, specifying the IV value sent from your Java Script code.

Categories

Resources