I have to implement java cipher space padding. I am trying to write like to like implementation of Perl code where Blowfish/CBC/space mode is supported. I couldn't find relevant mode in Java Cipher list and only supports PCK5Padding and NoPadding. Is Java supports space padding if so could anyone assist me.
Space padding is adding spaces so the length is a multiple of 8 when encrypting, or trimming spaces after decrypting. In Java, you can use NoPadding and manage the spacing yourself.
For example, to encrypt in Perl, and decrypt in Java:
Perl:
use Crypt::CBC;
$cipher = Crypt::CBC->new(
-literal_key => 1,
-key => pack("H*",
"11223344556677889900112233445566778899001122334"
. "455667788990011223344556677889900112233445566"
. "77889900112233445566"),
-cipher => 'Blowfish',
-header => 'none',
-padding => 'space',
-iv => pack("H*", '1234567812345678')
);
$ciphertext = $cipher->encrypt_hex("Message");
print(join('',unpack('H*',$cipher->iv())));
print $ciphertext;
Output: 1234567812345678e70b9ab0a4262ba8
Java:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.binary.Hex;
public class Main {
public static void main(String[] args) throws Exception {
Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(Hex.decodeHex(
"11223344556677889900112233445566778899001122334"
+ "455667788990011223344556677889900112233445566"
+ "77889900112233445566"),
"Blowfish");
byte[] iv = Hex.decodeHex("1234567812345678");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] result = cipher.doFinal(Hex.decodeHex("e70b9ab0a4262ba8"));
System.out.println(new String(result, Charsets.UTF_8).trim());
}
}
Output: Message
Related
I amn trying to replicate the AES-Decrypt function from CyberChef, using GCM mode and HEX input.
Screenshot of the operation:
So far, I've written the following code:
package decryption;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
public class Main {
public static final int GCM_TAG_LENGTH = 32;
public static void main(String[] args) throws Exception {
String IV = "9092d522e11120919fce8492";
String input = "90fab0";
String GCMTag = "02883e111ad6f79cd53674b5f833abab";
String key = "8cda92dcb3283da821daa275359642c7a05d60a4badb5769618193a930c1cdec";
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), 0, key.getBytes(StandardCharsets.UTF_8).length, "AES");
System.out.println(decrypt(input.getBytes(StandardCharsets.UTF_8), secretKey, IV.getBytes(StandardCharsets.UTF_8)));
}
public static String decrypt(byte[] cipherText, SecretKeySpec key, byte[] IV) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
}
For the above code I am getting:
Exception in thread "main" org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$InvalidKeyOrParametersException: Invalid value for MAC size: 256
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(Unknown Source)
at java.base/javax.crypto.Cipher.init(Cipher.java:1442)
at java.base/javax.crypto.Cipher.init(Cipher.java:1375)
at decryption.Main.decrypt(Main.java:29)
at decryption.Main.main(Main.java:19)
Fairly certain I am way off, but I did not find any articles/tutorials on how the GCM Tag can be used as input.
The length of the GCM tag used here is not 32, but 16 bytes.
Furthermore, the BC provider expects ciphertext and tag in concatenated form (ciphertext|tag).
And you have to hex decode key, IV, ciphertext and tag. Since you are running BouncyCastle, you can use org.bouncycastle.util.encoders.Hex.decode(...).
Overall:
import org.bouncycastle.util.encoders.Hex;
...
public static final int GCM_TAG_LENGTH = 16;
...
SecretKeySpec secretKey = new SecretKeySpec(Hex.decode(key), "AES");
System.out.println(decrypt(Hex.decode(input + GCMTag), secretKey, Hex.decode(IV))); // 985
It is not clear from your code whether you are using a static IV/nonce. If so, you should be aware that using a static IV/nonce for GCM is a serious problem, s. e.g. here.
Instead, for each encryption, a random (non-secret) IV/nonce is generated, concatenated with the ciphertext (and tag), and sent together to the decrypting side, which can separate the IV/nonce based on the known IV/nonce size (12 bytes for GCM).
AES/GCM is also supported by the SunJCE provider (at least as of Java 8, s. e.g. here), so you may not need BouncyCastle. For hex decoding you can then use a solution from this post. Starting with Java 17 there is a built-in support.
I have a PHP code for encrypting a string(token). I need to generate this token and call the legacy backend API. Backend API decrypts this and validates it before allowing access to the API. We are building the client application in Java. So this code needs to be implemented in Java.
$stringToEncode="****String which needs to be encrypted. This string length is multiple of 32********************";
$key="32ByteKey-asdcxzasdsadasdasdasda";
echo base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$key,
$stringToEncode,
MCRYPT_MODE_CBC,
$key
)
);
Here IV is same as key.
I tried with below Java code using "org.bouncycastle.crypto"
private static void encrypt(String key, String data) throws InvalidCipherTextException {
byte[] givenKey = key.getBytes(Charset.forName("ASCII"));
final int keysize = 256;
byte[] keyData = new byte[keysize / Byte.SIZE];
System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));
KeyParameter keyParameter = new KeyParameter(keyData);
BlockCipher rijndael = new RijndaelEngine(256);
ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
CipherParameters ivAndKey = new ParametersWithIV(keyParameter, key.getBytes(Charset.forName("ASCII")));
pbbc.init(true, ivAndKey);
byte[] plaintext = data.getBytes(Charset.forName("UTF8"));
byte[] ciphertext = new byte[pbbc.getOutputSize(plaintext.length)];
int offset = 0;
offset += pbbc.processBytes(plaintext, 0, plaintext.length, ciphertext, offset);
offset += pbbc.doFinal(ciphertext, offset);
System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(ciphertext));
}
But getting below exception -
Exception in thread "main" java.lang.IllegalArgumentException: invalid parameter passed to Rijndael init - org.bouncycastle.crypto.params.ParametersWithIV
I even tried with "javax.crypto" as shown below -
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class aes{
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String key = "32ByteKey-asdcxzasdsadasdasdasda";
String data = "****String which needs to be encrypted. This string length is multiple of 32********************";
encrypt(key, data);
}
public static void encrypt(String key1, String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
byte[] key = key1.getBytes(Charset.forName("ASCII"));
byte[] decrypted = data.getBytes(Charset.forName("UTF8"));
IvParameterSpec ivSpec = new IvParameterSpec(key);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);
byte[] encrypted = Base64.getEncoder().encodeToString(cipher.doFinal(decrypted)).getBytes();
System.out.println(encrypted);
}
}
But getting beow exception -
Exception in thread "main" java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
Can someone please suggest on how can I implement this?
The PHP code uses Rijndael with a blocksize of 256 bits. This isn't supported by the SunJCE Provider, only AES (which is a subset of Rindael with a blocksize of 128 bits). For this reason the second Java code snippet doesn't work either. Therefore a third party provider like BouncyCastle (BC) must be applied.
There are two bugs in the Java / BC code:
Contrary to the PHP code no CBC is applied. This must be changed using CBCBlockCipher.
The Zero padding variant of BC is different from the mcrypt variant. The BC variant always pads, i. e. even if the length of the plaintext already corresponds to an integer multiple of the blocksize (32 bytes here), the mcrypt variant doesn't (i. e. the latter only pads if the length of the plaintext doesn't correspond to an integer multiple of the blocksize).Since according to the description the plaintext already corresponds to an integer multiple of the blocksize, the mcrypt variant doesn't pad. In the Java / BC code, this can be achieved if simply no padding is used at all.But be careful: If the length of the plaintext doesn't correspond to an integer multiple of the blocksize, padding must of course be used (with regard to compatibility with the Zero padding variant used in the PHP code).
Both problems can be solved by replacing the lines
ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
by
BufferedBlockCipher pbbc = new BufferedBlockCipher(new CBCBlockCipher(rijndael));
Then the ciphertexts produced by both codes are identical.
A few additional notes:
Using the key as IV is generally not useful. Since, for security reasons, an IV may only be used once for a given key, a new key must be used here for each encryption. See also here.Usually in practice a fresh, random IV is generated for each encryption.
It's generally more secure to use a standard, i.e. AES and not Rijndael with a block size of 256 bits, which isn't a standard.
Zero-padding isn't reliable (besides, there are several variants which, as you have experienced, can cause problems). More reliable is PKCS7 padding.
I'm using openssl's aes-256-cfb algorithm (from NodeJS's crypto module).
While both the NodeJS and the Java code successfully encrypt and decrypt data, the ciphertext is different, even when the iv, key and plaintext are all identical.
openssl/NodeJS cipherText:
05c2aad7bac42ed0846e9a52ce73df9ff9d7ff914feea49fed27d55ad690782a43107914c1b307ec92753227728c95b8e59c546d
Java cipherText:
05C2AAD7BAC42ED084739340D47CEC9F03D8E94AC7B1E11A56A6654F76AD2C8076BCA162303E39B44D043732E98FDD28C52D
I have guessed that openssl's aes-256-cfb translates to Java's AES/CFB/NoPadding.
The ciphertexts both share the same initial 9 bytes, which is odd - I would have expected them to share the first 16 bytes if there had been some difference in the common mode used. (I hope that "common mode" is the collective term for CFB/CBC/CTR/etc.)
Is Java's AES/CFB/NoPadding the correct translation of OpenSSL's aes-256-cfb?
Does the existence of the common first nine bytes imply that Java's AES/CFB/NoPadding is at least using AES256, as opposed to AES128?
If so, what else might account for the differing ciphertext?
Test cases follow:
OpenSSL/NodeJS:
var assert = require('assert');
var crypto = require('crypto');
describe('crypto', function() {
it('should work', function () {
var plainText = new Buffer('a secret plainText which has more than sixteen bytes');
var key = new Buffer('fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210', 'hex');
var iv = new Buffer('0123456789abcdef0123456789abcdef', 'hex');
var cipher = crypto.createCipheriv('aes-256-cfb', key, iv);
var decipher = crypto.createDecipheriv('aes-256-cfb', key, iv);
var cipherText = cipher.update(plainText);
cipher.final(); // no need to use this value with cfb, it seems to always be empty?
assert.equal(plainText.length, cipherText.length);
assert.equal('05c2aad7bac42ed0846e9a52ce73df9ff9d7ff914feea49fed27d55ad690782a43107914c1b307ec92753227728c95b8e59c546d', cipherText.toString('hex'));
var deciphered = decipher.update(cipherText);
decipher.final(); // no need to use value this with cfb, it seems to always be empty?
assert.equal(plainText, deciphered.toString('utf8'));
});
});
Java/Android:
import android.test.InstrumentationTestCase;
import com.google.protobuf.ByteString;
import bit.Twiddling;
import java.security.AlgorithmParameters;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoTest extends InstrumentationTestCase {
public void test_encrypt_and_decrypt() throws Exception {
byte[] plainText = "a secret message which has more than sixteen bytes".getBytes();
byte[] key = Twiddling.hexStringBEToBytes("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
byte[] iv = Twiddling.hexStringBEToBytes("0123456789abcdef0123456789abcdef");//0123456789abcdef0123456789abcdef");
SecretKeySpec aesSecret = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
Cipher decipher = Cipher.getInstance("AES/CFB/NoPadding");
IvParameterSpec ivps = new IvParameterSpec(iv);
assertEquals(16, iv.length);
cipher.init(Cipher.ENCRYPT_MODE, aesSecret, ivps);
byte[] cipherText = cipher.doFinal(plainText);
assertEquals(plainText.length, cipherText.length);
assertEquals("05C2AAD7BAC42ED084739340D47CEC9F03D8E94AC7B1E11A56A6654F76AD2C8076BCA162303E39B44D043732E98FDD28C52D", Twiddling.bytesToHexStringBE(cipherText));
decipher.init(Cipher.DECRYPT_MODE, aesSecret, ivps);
byte[] deciphered = decipher.doFinal(cipherText);
assertEquals(new String(plainText), new String(deciphered));
}
}
It was a find/replace error - the two plainTexts differ after the first nine bytes.
Java's name for OpenSSL's aes-256-cfb is AES/CFB/NoPadding.
I am trying to encode using a static key say as an example: "B3FFCA612CD0C3D9050A4DE3588E2830F26BEF6D7E1CEC77DD2F22FAFC038D33" in AES and ECB mode. when i try this with openssl, I successfully get a result with no issues but when I code it in groovy or java:
Invalid AES key length: 64 bytes
when I researched this, the issue occurs because the key length can be 32 bytes at most, now I am confused because the API i am sending these encrpions to, completely works with the 64 byte key i am sending but fails with the 32 byte one. openssl encodes this too with the 64 byte key I provide as below:
openssl enc -aes-256-ecb -e -in infile.txt -a -A -K B3FFCA612CD0C3D9050A4DE3588E2830F26BEF6D7E1CEC77DD2F22FAFC038D33 -iv 0 -p
I want to be able to do the same but in groovy/java.
I am not sure what I am missing, I would really apprecaite your help on this please!
here is my code:
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import java.security.spec.KeySpec;
import javax.crypto.spec.PBEKeySpec;
class AESCrypt {
// encrypt
def encrypt (def plainText, def secret) {
def cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
// converty string secret to SecretKeySpec
byte[] decodedKey = Base64.getDecoder().decode(secret);
SecretKeySpec key= new SecretKeySpec(decodedKey , 0, decodedKey.length,
"AES");
cipher.init(Cipher.ENCRYPT_MODE, key)
return cipher.doFinal(plainText.getBytes("UTF-8")).encodeBase64().toString()
}
}
//Main
for( int i = 0; i < dataContext.getDataCount(); i++ ) {
InputStream is = dataContext.getStream(i);
Properties props = dataContext.getProperties(i);
def c = new AESCrypt()
def secret =
"B3FFCA612CD0C3D9050A4DE3588E2830F26BEF6D7E1CEC77DD2F22FAFC038D33"
//get plaintext of payload
Scanner s = new Scanner(is).useDelimiter("\\A");
String plainPayload = s.hasNext() ? s.next() : "";
//encrypt plaintext of payload
def encryptedPayload = c.encrypt(plainPayload, secret)
println encryptedPayload + "\n"
}
openssl encodes this too with the 64 byte key I provide as below.
It's not a 64-byte key. It's a 32-byte key that's been hexadecimal encoded, which results in 64 letters.
You are trying to base64 decode your secret, which is not the same as hexadecimal.
I'm not a Java or Groovy expert, but may be able to use decodeHex() on your string to hex decode it instead of base64 decode it.
I am trying to encrypt data in java and decrypt data in ruby.
I found almost same asks, but my case is little bit different.
Encrypt in Ruby and Decrypt in Java - Why is it not working?
AES/CBC encrypt in Java, decrypt in Ruby
My code is ...
Encrypt in java
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Hex;
class Crypt {
public static void main(String[] args) throws Exception {
Map<String, String> node = new HashMap<String, String>();
node.put("timestamp", "1377499097199");
JSONObject jsonObject = JSONObject.fromObject(node);
String json = jsonObject.toString();
System.out.println(json);
//key
String skeyString = "97128424897797a166913557a6f4cc8e";
byte[] skey = Hex.decodeHex(skeyString.toCharArray());
System.out.println("key : " + skeyString);
//iv
String ivString = "84e8c3ea8859a0e293941d1cb00a39c3";
byte[] iv = Hex.decodeHex(ivString.toCharArray());
System.out.println("iv : " + ivString);
//encrypt
SecretKeySpec skeySpec1 = new SecretKeySpec(skey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec1, new IvParameterSpec(iv));
byte[] encrypted = cipher.doFinal(json.getBytes());
String encryptedString = Hex.encodeHexString(encrypted);
System.out.println("=============>");
System.out.println("encrypted string: " + encryptedString);
}
}
Result is
{"timestamp":"1377499097199"}
key : 97128424897797a166913557a6f4cc8e
iv : 84e8c3ea8859a0e293941d1cb00a39c3
=============>
encrypted string: 395f6c0e8ad27f57c4a5a8975aa633e5b26f288d37ce18c6971779951f3b3527
and I hope to decrypt (encrypted string) in Ruby
Ruby code is ...(got error)
require 'openssl'
key = "97128424897797a166913557a6f4cc8e"
iv = "84e8c3ea8859a0e293941d1cb00a39c3"
encrypted_string = "395f6c0e8ad27f57c4a5a8975aa633e5b26f288d37ce18c6971779951f3b3527"
de_cipher = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
de_cipher.decrypt
de_cipher.key = key
de_cipher.iv = iv
de_cipher.update(encrypted_string) << de_cipher.final
I expected to get
{"timestamp":"1377499097199"}
But it returns error
`final': bad decrypt (OpenSSL::Cipher::CipherError)
I think the problem is cipher.padding and type of key/iv.
But I don't know exactly how to complete ruby code.
Please let me know How to complete this code.
Thank you.
There's two problems with your Ruby code.
First, you're using AES 256 when you should be using AES 128. Java uses AES 128 or 256 based on the size of the key you use, and you're using a 128 bit key.
Second, you need to Hex decode your key, iv, and encrypted_string values in Ruby. OpenSSL Cipher is expecting binary, not hex strings.
require 'openssl';
key = "97128424897797a166913557a6f4cc8e";
iv = "84e8c3ea8859a0e293941d1cb00a39c3";
encrypted_string = "395f6c0e8ad27f57c4a5a8975aa633e5b26f288d37ce18c6971779951f3b3527";
de_cipher = OpenSSL::Cipher::Cipher.new("AES-128-CBC");
de_cipher.decrypt;
de_cipher.key = [key].pack('H*');
de_cipher.iv = [iv].pack('H*');
puts de_cipher.update([encrypted_string].pack('H*')) << de_cipher.final;
Output:
{"timestamp":"1377499097199"}