Java and Javascript Blowfish - java

I need to send some data encrypted with Blowfish from a java-based server to a client. I can successfully encrypt data but I can't decrypt it on the client side.
Here is my java code:
byte[] kd = key.getBytes("UTF-8");
SecretKeySpec ks = new SecretKeySpec(kd, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, ks);
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
String str = new String(encrypted, "UTF-8");
As for js library I decided to use this one.
out = blowfish.decrypt(code, skey, {cipherMode: 1, outputType: 0})
As a result I get some strange characters. What's wrong with my code?
UPD:
This code works perfectly:
byte[] kd = key.getBytes("UTF-8");
SecretKeySpec ks = new SecretKeySpec(kd, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, ks);
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
String str = new String(Base64.encodeBase64(encrypted), "UTF-8");
JS:
out = blowfish.decrypt(code, skey, {cipherMode: 0, outputType: 0})

Sending text with \u0000 bytes in it to a browser can lead to all kinds of odd problems. That's why you should encode the data BASE64, send it to the client and then decode it locally.
Another issue is new String(encrypted, "UTF-8"); since the encoded byte array will contain illegal UTF-8 sequences. Try new String(encrypted, "iso-8859-1"); instead, it's a 1:1 encoding for arbitrary bytes. But again, the 0 bytes could confuse some component in between.

Related

Does any one knows how the password was encrypted in xshell sessions

i try to decrypt the encrypted password in .xsh file, i'v searched the answers for sometime, but got almost nothing, some one say there is a python version, i try to rewrite it in java, but it does't work.
[here is the phython version]: https://github.com/dzxs/Xdecrypt/blob/master/Xdecrypt.py
this is what i got
String sid = "*********";
String encryptedPwd = "************";
byte[] encryptedPwdBytes = Base64.decode(encryptedPwd);
byte[] keyBytes = DigestUtil.digest(sid.getBytes(), "SHA-256");
SecretKeySpec key = new SecretKeySpec(keyBytes, "ARC4");
Cipher cipher = Cipher.getInstance("ARC4", new BouncyCastleProvider());
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] bytes = cipher.update(encryptedPwdBytes, 0, encryptedPwdBytes.length);
System.out.println(new String(bytes));
it doen't work.

Encrypt string in ASP decrypt in Java

I have below ASP code to encrypt and decrypt in asp. It works fine. I want string to be encrypted in asp and decrypt it in java.
set obj=Server.CreateObject("System.Security.Cryptography.RijndaelManaged")
Set endocde = Server.CreateObject("System.Text.endocdeEncoding")
un = endocde.GetBytes_4("Test Encryption")
obj.key = endocde.GetBytes_4("SomeRandomKey")
obj.iv = endocde.GetBytes_4("SomeRandomIv")
set enc=obj.CreateEncryptor()
uncUn=enc.TransformFinalBlock((un),0,lenb(un))
eUn=endocde.GetString((uncUn))
set dec=obj.CreateDecryptor()
byted=dec.TransformFinalBlock((uncUn),0,lenb(uncUn))
sd=endocde.GetString((byted))
I tried encrypted string to be decrypted using below code in java but doesnt work. I tried sending encrypted data in UTF8/Base64 but doesnt work. Please help.
String iv = "sameIVasASP";
String key = "sameKeyasASP";
IvParameterSpec iv = new IvParameterSpec(iv.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "Rijndael");
Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decode = Base64.getDecoder().decode(<encrpted string>);
System.out.println( new String(cipher.doFinal(decode)).getBytes("UTF-8"));
How to encode/decode UTF-8 string to base64 in c#:
byte[] bytes = Encoding.UTF8.GetBytes("string-to-encode");
string base64 = Convert.ToBase64String(bytes);
How to encode/decode UTF-8 string to base64 in Java
Java8 version:
byte[] decodedBytes = Base64.getDecoder().decode(base64encodedstring);
Pre-Java8 example (with Base64 class of Commons Codec):
byte[] decodedBytes = Base64.decodeBase64(encodedBytes);
(In Java you have many option to do that (not just these), if you are using at least Java8, there is a built in class for that, for previous versions I usually use the Base64 class in Apache Commons Codec)

Java NIO + AES Encryption from Client to Server - ByteBuffer issue

I'm quite a newbie regarding encryption and NIO,
I have the following code for client:
String key1 = "1234567812345678";
byte[] key2 = key1.getBytes();
SecretKeySpec secret = new SecretKeySpec(key2, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] encrypted = cipher.doFinal(msg.getBytes());
System.out.println("Encrypted info: " + encrypted);
String send = encrypted.toString();
bytebuf = ByteBuffer.allocate(48);
bytebuf.clear();
bytebuf.put(send.getBytes());
bytebuf.flip();
while(bytebuf.hasRemaining()) {
nBytes += client.write(bytebuf);
}
and the following code for server:
// Server receives data and decrypts
SocketChannel socket = (SocketChannel) key.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
nBytes = socket.read(buf);
String data = new String(buf.array()).trim();
String key1 = "1234567812345678";
byte[] key2 = key1.getBytes();
SecretKeySpec secret = new SecretKeySpec(key2, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secret);
byte[] decrypted = cipher.doFinal(data.getBytes());
System.out.println("Decrypted Info: " + new String(decrypted));
When a message is sent from the Client to the Server, "HELLO" for example is encrypted to [B#34d74aa5 and on the Server side I get *Data packet found as [B#34d74aa5.
Till here everything looks fine, but I get the following exception:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
I suspect that I have some issue with the way the data is coming out of the buffer on the server side?
Any ideas on this?
UPDATE:
**Based on Erickson's answer this is the final solution
javax.crypto.BadPaddingException: Given final block not properly padded
Client Code:
String key1 = "1234567812345678";
byte[] key2 = key1.getBytes();
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeySpec secret = new SecretKeySpec(key2, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
byte[] encrypted = cipher.doFinal(msg.getBytes(StandardCharsets.UTF_8));
String text = DatatypeConverter.printBase64Binary(encrypted);
System.out.println("Encrypted info: " + text);
bytebuf = ByteBuffer.allocate(32);
bytebuf.clear();
bytebuf.put(text.getBytes());
bytebuf.flip();
while(bytebuf.hasRemaining()) {
nBytes += client.write(bytebuf);
}
Server Code:
LOGGER.info("Confirming write");
String data = new String(buf.array());
LOGGER.info("Data packet found as {}", data);
/*******************************************************/
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
String key1 = "1234567812345678";
byte[] key2 = key1.getBytes();
SecretKeySpec secret = new SecretKeySpec(key2, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
byte[] encrypted = DatatypeConverter.parseBase64Binary(data);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("Decrypted Info: " + new String(decrypted, StandardCharsets.UTF_8));
Your cipher text, encrypted, is a byte[], and invoking toString() on an array doesn't render the array content, it returns type ([B) and hash code (#34d74aa5) information as described by Object.toString().
You can't just use new String(encrypted) either. When a byte array is decoded to text, the decoder will replace any invalid byte sequences with the replacement character, \uFFFD (�). Thus, information is lost and subsequent decryption will fail.
Use an encoding like base-64 to convert byte sequences to printable characters instead. Don't junk up your code with third-party libraries for this; you can use javax.xml.bind.DatatypeConverter.
/* Client: */
byte[] encrypted = cipher.doFinal(msg.getBytes(StandardCharsets.UTF_8));
String text = DatatypeConverter.printBase64Binary(encrypted);
…
/* Server: */
byte[] encrypted = DatatypeConverter.parseBase64Binary(data);
byte[] decrypted = Cipher.doFinal(encrypted);
System.out.println(new String(decrypted, StandardCharsets.UTF_8);
You should also be explicit in selecting your mode and padding (like "AES/CBC/PKCS5Padding") because there's no guarantee the recipient will use the same provider, or that the same provider will use the same defaults over time. Same goes for specifying character encodings, like UTF-8.
The AES scheme is a "block cipher" it works on fixed-size blocks of data. You are creating a "raw" Cipher instance, which will expect you to make sure that every byte array that you pass to the cipher is aligned to the cipher's "native" block length. That's usually not what you want to do.
An additional problem that you are exposing yourself to in using the cipher "raw", although it's not causing an actual error, is that if you were to pass it the same block of data on separate occasions, each time, that block would be encrypted identically, therefore giving an attacker clues as to the structure of the data. Again, that's usually not what you want to do in a practical application.
So usually, you need to specify two extra things: a padding scheme, which determines what happens when sections of data are not exactly aligned to a block size, and a block mode, which determines what scheme the cipher will use to avoid identical input blocks being encrypted to identical output blocks. The block mode generally needs initialising with a "starting state" called the initialisation vector (you could use a default state of "all zero", but that's less secure).
So you need to do two things:
You need to initialise you cipher with a padding scheme and block
mode, e.g. "AES/CBC/PKCS5PADDING"
For additional security, you would also usually set up (and transmit
before the data) a random initialisation vector. See this example for more
information.
You are converting the ciphertext, which is a byte[], to a String here:
byte[] encrypted = cipher.doFinal(msg.getBytes());
String send = encrypted.toString();
This is incorrect. You also cannot do new String(byte[]) because the byte[] is random, not a stream of character data in the platform default encoding assumed by new String(byte[]). You should convert the byte[] data to a String by using a hex or base64 encoding (I recommend Apache Commons Codec) e.g.
hexEncodedCipherText = new String(Hex.encodeHex(binaryCipherText))
On the server-side, use the opposite operation to convert the hex or base64 encoded data back to a byte[] before decryption e.g.
binaryCipherText = Hex.decodeHex(hexEncodedCipherText.toCharArray());
UPDATE:
The updated question is not working during decryption because of the incorrect use of the initialization vector. You don't specify an IV during encryption, which means Java will generate a random one. You need to obtain this random IV from the cipher by calling cipher.getIV() after the encryption (or specify it explicitly, though generating a random one is more secure). Then, during the decryption, create the IvParameterSpec using the IV created during encryption. In addition, you will need to encode/decode the IV in the same manner as the ciphertext, since it is also binary data.
UPDATE 2:
I see you have updated your question with the IV, but you are using a null IV. Generally, this is only "safe" when you have a unique key for every message you send. If your key is fixed or re-used for any significant length of time, you should generate a unique IV for each encryption/decryption. Otherwise, you are leaving yourself open to cryptanalysis based on multiple ciphertexts encrypted with the same key and IV.

Java encrypt/decript data from PHP to Java, IllegalBlockSizeException

I'm trying to read a base64 encoded and AES 128-bit encrypted string from PHP, but I'm getting IllegalBlockSizeException.
PHP encrypt:
encrypt("My f awesome test !");
function encrypt($string){
$td = mcrypt_module_open('rijndael-128', '', 'cbc', "1cc251f602cf49f2");
mcrypt_generic_init($td, "f931c96c4a4e7e47", "1cc251f602cf49f2");
$enc = mcrypt_generic($td, $string);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return base64_encode($enc);
}
And the returned value is:
McBeY73GQ5fawxIunVKpqUupipeRlt9ntyMRzjbPfTI=
Now I want to read it in Java:
static public String decrypt(String data) throws Exception {
data = new String( Base64.decode(data, Base64.NO_WRAP) );
byte[] keyByte = "f931c96c4a4e7e47".getBytes("UTF-8");
byte[] ivByte = "1cc251f602cf49f2".getBytes("UTF-8");
Key key = new SecretKeySpec(keyByte, "AES");
IvParameterSpec iv = new IvParameterSpec(ivByte);
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(Cipher.DECRYPT_MODE, key, iv);
byte[] bval = c.doFinal( data.getBytes("UTF-8") );
return new String( bval );
}
And I'm getting an Exception:
javax.crypto.IllegalBlockSizeException: data not block size aligned
This might be caused by padding?
EDIT
Your error was caused by the conversion of the plaintext to and from a string. It's not necessary anyway - just use byte arrays:
byte[] data = Base64
.decodeBase64("McBeY73GQ5fawxIunVKpqUupipeRlt9ntyMRzjbPfTI=");
byte[] keyByte = "f931c96c4a4e7e47".getBytes("UTF-8");
byte[] ivByte = "1cc251f602cf49f2".getBytes("UTF-8");
Key key = new SecretKeySpec(keyByte, "AES");
IvParameterSpec iv = new IvParameterSpec(ivByte);
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(Cipher.DECRYPT_MODE, key, iv);
byte[] bval = c.doFinal(data);
System.out.println(new String(bval)); // Prints My f awesome test !
I recommend you use padding in your encryption, otherwise you cannot cope with arbitrarily-sized input.
the IllegalBlockSizeException thrown on call to doFinal() if: "cipher is a block cipher, no padding has been requested (only in encryption mode), and the total input length of the data processed by this cipher is not a multiple of block size; or if this encryption algorithm is unable to process the input data provided." -http://docs.oracle.com/javase/6/docs/api/javax/crypto/Cipher.html#doFinal%28%29. So its either bad input data or block size.
This is a working version of encryption/decryption between
https://github.com/chaudhuri-ab/CrossPlatformCiphers
Hope this helps someone as it took me a while to work through the little details between the platforms.

Java encryption by client and decryption by server, using PBKDF2WithHmacSHA1 and AES/CBC/PKCS5Padding

I'm going for secure confidentiality as long as the private key stays secret, and I get following error in my app when decrypting: javax.crypto.BadPaddingException: Given final block not properly padded
The code:
// Encryption, client side
byte[] plainData = "hello plaintext!".getBytes("UTF-8");
byte[] salt = new byte[64];
new SecureRandom().nextBytes(salt);
KeySpec spec = new javax.crypto.spec.PBEKeySpec("password".toCharArray(), salt, 1024, 256);
SecretKey sk = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sk.getEncoded(), "AES"));
byte[] iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal(plainData);
System.out.println("ciphertext: "+new String(ciphertext, "UTF-8")); // cipher
// Decryption, server side
KeySpec spec2 = new javax.crypto.spec.PBEKeySpec("password".toCharArray(), salt, 1024, 256);
SecretKey sk2 = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec2);
Cipher cipher2 = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher2.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sk2.getEncoded(), "AES"), new IvParameterSpec(iv)); // Get the same IV value from client/encryptor aswell, still random
String plaintext = new String(cipher2.doFinal(ciphertext), "UTF-8");
System.out.println("decrypted plaintext: "+plaintext); // plain
Is it the randomness of salt that causing the problem?
I can decrypt it fine when I make use of the object references on the client side, but I need my own instances on the server.
Great thanks in advance for correcting my error(s)!
*EDIT: * Code updated and corrected
Just from quickly looking at your code I can see that you are creating a different salt at the client and server side. In order for the server side to be able to decrypt that salt and the key have to be the same.
Now I'm not a Java developer but all the other code to me looks ok but like I said if you are creating a different salt at each end the decryption is not going to work.

Categories