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.
Related
I'm struggling with openssl and aes encryption. I need to encrypt a file in java while forcing the key and IV values instead of using a password. It must be decipher with the following command:
openssl aes-256-cbc -d -K $KEY_VALUE -iv $IV -in hello.txt.ssl -out hello-clear.txt
Sadly, I can not change this command, it's the requirement of the client.
I managed to encrypt the file in java (see following code) but I get this error when decrypting it with openssl
hex string is too short, padding with zero bytes to length
hex string is too short, padding with zero bytes to length
bad decrypt
40874B28DD7F0000:error:1C800064:Provider routines:ossl_cipher_unpadblock:bad decrypt:providers/implementations/ciphers/ciphercommon_block.c:124:
Any idea of what I missed in the encryption process ? Or any other way to do it ?
Here is the full java sample that encrypt and generate the decryption command:
import org.apache.commons.io.IOUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
public class OpenSslEncryptor {
public static void main(String... args) throws Exception {
int keyLength = 256;
int keyLengthInBit = keyLength / 8;
File baseFile = new File("hello.txt");
IOUtils.write("hello openssl !", new FileOutputStream(baseFile), StandardCharsets.UTF_8);
byte[] inBytes = new FileInputStream(baseFile).readAllBytes();
String keyValue = generateRandom(40);
String iv = generateRandom(16);
byte[] keyValueB = Arrays.copyOfRange(keyValue.getBytes(), 0, keyLengthInBit);
byte[] ivB = Arrays.copyOfRange(iv.getBytes(), 0, 16);
final SecretKeySpec key = new SecretKeySpec(keyValueB, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivB));
byte[] data = cipher.doFinal(inBytes);
File outputFile = new File(baseFile.getAbsolutePath() + ".ssl");
IOUtils.write(data, new FileOutputStream(outputFile));
String decryptCommand = "openssl aes-256-cbc -d -K " + keyValue
+ " -iv " + iv
+ " -in " + outputFile.getName()
+ " -out hello-clear.txt";
System.out.println(decryptCommand);
}
private static String generateRandom(int length) {
SecureRandom secureRandom = new SecureRandom();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(Integer.toHexString(secureRandom.nextInt(16)));
}
return builder.toString();
}
}
Here is the command line encrypt with openssl if anyone want it
openssl aes-256-cbc -e -K $KEY_VALUE -iv $IV -in hello.txt -out hello.txt.ssl
The generateRandom() method returns the data hex encoded. When a sequence of y bytes is hex encoded, it consists of 2*y hex digits. Accordingly, key and IV must be generated with twice the required length (64 for the 32 bytes key for AES-256, 32 for the 16 bytes IV for AES). These can then be hex decoded for use in encryption, e.g. with HexFormat.of().parseHex():
String keyValue = generateRandom(64);
String iv = generateRandom(32);
byte[] keyValueB = HexFormat.of().parseHex(keyValue);
byte[] ivB = HexFormat.of().parseHex(iv);
Overall, however, the generation of the random data in generateRandom() is somewhat cumbersome and could be refactored, e.g.:
byte[] keyValueB = generateRandom(32);
byte[] ivB = generateRandom(16);
String keyValue = HexFormat.of().formatHex(keyValueB);
String iv = HexFormat.of().formatHex(ivB);
...
private static SecureRandom secureRandom = new SecureRandom();
private static byte[] generateRandom(int lengthInBytes) {
byte[] data = new byte[lengthInBytes];
secureRandom.nextBytes(data);
return data;
}
Now generateRandom() produces the required data directly as byte[]. These can then be hex encoded for the OpenSSL statement, e.g. with HexFormat.of().formatHex().
I want to encrypt/ decrypt with AES, with shared passwod, my code same as here.
the linked code works fine, but there is no shared passworn in it.
how can I add a shared password to the following implementation?
I need something like
String shared="xxx..";//some password with 16 digits length
Is it possible?
and adding this shared password to the encryption.
It is very important that the key used for AES encryption is not easy to guess so in a lot of implementations the keys are generated randomly. The key itself is a byte array of 16 (128 bit), 24 (192 bit) or 32 (256 bit) byte length and a byte array is not usuable as source for a shared password.
The solution is to encode the byte array into a Base64-encoded string and pass this string to the recepient on a secure way. The recepient decodes the string back to a byte array and further via the SecretKeySpec to a secret key.
The small example shows the way to securly generate a random password with different lengths (the example uses only the 128 bit keylength, encode it and decode it back to a secret key - the orginal SecretKey k is compared to the regenerated SecretKex kReceived.
Just a last notice but it is a security warning: Your encryption method is using the AES ECB mode that is unsecure - please do not use this mode in production (mode is defined here: AES/ECB/PKCS5Padding).
Result:
https://stackoverflow.com/questions/62782129/encrypt-files-with-aes-with-shared-password
sharedKey: UT7PPJwX2fnYTazSOZAhxg==
keySpecReceived equals secretKey: true
Code:
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws NoSuchAlgorithmException {
System.out.println("https://stackoverflow.com/questions/62782129/encrypt-files-with-aes-with-shared-password");
// random key creation taken from https://stackoverflow.com/a/41414233/9114020
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
int keyBitSize = 128; // aes keylength can be 128, 192 or 256 bit
keyGenerator.init(keyBitSize, secureRandom);
SecretKey k = keyGenerator.generateKey();
// encode the key and then base64-encoding
String sharedKey = Base64.getEncoder().encodeToString(k.getEncoded());
System.out.println("sharedKey: " + sharedKey);
// share this key with another party on a secure way
String sharedKeyReceived = sharedKey; // simulates the receiving
byte[] sharedKeyByteReceived = Base64.getDecoder().decode(sharedKeyReceived);
SecretKeySpec kReceived = new SecretKeySpec(sharedKeyByteReceived, "AES");
System.out.println("keySpecReceived equals secretKey: " + kReceived.equals(k));
}
}
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
Hi i am using openSSL command to encrypt and decrypt my message. Now i want this command to be converted in the java code i have tried different solutions provided on the web but none of the code matches the results.
Here is my OpenSSL command with my understating in the comments:
key="FB4FF1BA6F1FCC1A11B8B3910342CBD3A2BEAEB8F52E8910D9B25C0C96280EEA"
# Getting 16 digits from the iv.txt file and putting it into the bin
head -c 16 iv.txt > iv.bin
# Converting iv.bin text into the HEXA value
iv=`xxd -l 16 -p iv.bin`
# encrypt without "-a"
openssl enc -aes-256-cbc -K $key -iv $iv -in plainKey.txt -out encryptedKey.bin
# printing encrypted results in base64 format this need to be matched with my java code.
echo "<enc>"`cat encryptedKey.bin | base64`"</enc>"
This is what i have done in Java :
Note: this code from stack overflow accepted answer with minor change I have tried some other codes as well but cannot mention all here.
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class Test {
public static void main(String[] args) {
try {
runEncryption();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void runEncryption() throws Exception
{
//String to be encrypted
String plainText = "abcd#1234\n";
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// IV text
String iv = "C837E1B6C3D3A7E28F47719DE0C182C9";
// getting 16 characters of iv text
iv = iv.substring(0,16);
// Value of key
String key = "FB4FF1BA6F1FCC1A11B8B3910342CBD3A2BEAEB8F52E8910D9B25C0C96280EEA";
// Logic for converting 16 Digits of IV into HEX
StringBuffer hexString = new StringBuffer();
for (int i=0;i<iv.getBytes().length;i++) {
String hex=Integer.toHexString(0xff & iv.getBytes()[i]);
if(hex.length()==1) hexString.append('0');
hexString.append(hex);
}
// Seems something wrong here because if i am passing all the bytes to keySpe like key.getBytes() it is producing exception so i am passing 16 bytes as previous code was doing in SO
SecretKeySpec keySpec = new SecretKeySpec(hexToBytes(key), 0, 16, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(hexToBytes(hexString.toString()));
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedBase64 = new String(DatatypeConverter.printBase64Binary(encrypted));
System.out.println("");
System.out.println("Encrypted base64 = " + encryptedBase64);
}
private static byte[] hexToBytes(String s)
{
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2)
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
return data;
}
}
I am generating key and iv using openSSL command
openssl enc -aes-256-cbc -k secret -P -md sha1
it Seems something wrong here because if i am passing all the bytes to keySpec like key.getBytes() it is producing exception so i am passing 16 bytes as previous code was doing in SO i have also mentioned this in the code comments please advise on this thanks.
You have several mistakes in your code
let's assume what you want to do is
openssl enc -aes-256-cbc -K FB4FF1BA6F1FCC1A11B8B3910342CBD3A2BEAEB8F52E8910D9B25C0C96280EEA -iv 03B13BBE886F00E00000000000000000 -base64 -in input.txt
in your code you have a few mistakes (wtf moments). I won't fix them for you, but basically:
I'd advice to use some normal library (e.g. commons-codec, ..) to encode/decode from hex, though seems it's working so far
you are using 16 bytes (128 bit) iv and key effectively doing AES-128. You need to use AES-256 having key and iv 256 bit long (32 bytes). You've cut both to 16 bytes so they are different from values used with openssl
the main breaking point: IvParameterSpec ivSpec = new IvParameterSpec(hexToBytes(hexString.toString())); please think about this a little. hexToBytes(iv)
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"}