ECB and CBC AES output is equal in Java - java

I've played around with the Java AES En/Decryption and used different cyper modes for this. Namely I use CBC and ECB. As ECB is considered to be weak, I wanted to go with CBC.
I assumed the output of the encrypted texts ob cbc and ecb are different, but they are equal. How is this possible?
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import com.instana.backend.common.exception.InstanaException;
public class AESTest {
private static String pwd = "etjrgp9user9fu3984h1&(/&%$ยง";
public static void main(String[] args) throws Exception {
System.out.println("UNSECURE WITH ECB:");
String ecbEncrypt = encrypt("YOLO", cypher(Cipher.ENCRYPT_MODE, "AES"));
System.out.println("Encrypted: " + ecbEncrypt);
String ebcDecrypt = decrypt(ecbEncrypt, cypher(Cipher.DECRYPT_MODE, "AES"));
System.out.println("Decrypted: " + ebcDecrypt);
System.out.println("=====================================");
System.out.println("SECURE WITH CBC");
String cbcEncrypt = encrypt("YOLO", cypher(Cipher.ENCRYPT_MODE, "AES/CBC/PKCS5Padding"));
System.out.println("Encrypted: " + cbcEncrypt);
String cbcDecrypt = decrypt(cbcEncrypt, cypher(Cipher.DECRYPT_MODE, "AES/CBC/PKCS5Padding"));
System.out.println("Decrypted: " + cbcDecrypt);
System.out.println("=====================================");
System.out.println("Decrypting CBC with ECB");
}
public static String encrypt(String superDuperSecret, Cipher cipher) throws IOException {
try {
byte[] encrypted = cipher.doFinal(superDuperSecret.getBytes("UTF-8"));
return new String(new Hex().encode(encrypted));
} catch (Exception e) {
throw new InstanaException("Encryption of token failed.", e);
}
}
public static String decrypt(String superDuperSecret, Cipher cipher) {
try {
byte[] encrypted1 = new Hex().decode(superDuperSecret.getBytes("UTF-8"));
return new String(cipher.doFinal(encrypted1));
} catch (Exception e) {
throw new InstanaException("Encrypted text could not be decrypted.", e);
}
}
private static Cipher cypher(int mode, String method)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException,
InvalidAlgorithmParameterException {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(pwd.toCharArray(), pwd.getBytes(), 128, 128);
SecretKey tmp = skf.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance(method);
if(method.contains("CBC")) {
byte[] ivByte = new byte[cipher.getBlockSize()];
IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
cipher.init(mode, key, ivParamsSpec);
}else{
cipher.init(mode, key);
}
return cipher;
}
}

Since you're passing an empty IV (you never put anything inside your ivByte), the operations performed for the first block are identical regardless of the mode being used. Encrypting a longer payload would result in the second block being chained to the first block in the case of CBC and the following blocks would be different between ECB/CBC.
You should pass a non-empty IV when using CBC mode, so the first block will be xorred with the IV, resulting in different encrypted values starting from the first block.

Related

Convert AES encryption java to Python

I hava Java code that do AES encryption, and i would like to convert it so it work in python. This is a main class, i made initVector constants so it would be easier to see if it work:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.io.BufferedWriter;
public class test {
public static void main(String [] args)
{
String ss = "pUypiz-7hJ0y_JtpKWaydp";
String url = "";
String toBeEncrypted = "";
String initVector = "IqtY8jgALtjZNLM5";
String encodedInitVector = Encryptor.encodeB64mod(initVector.getBytes());
toBeEncrypted = "ch21979714702=put";
ss = Encryptor.decodeB64Mod(ss);
url = "i=" + encodedInitVector + "\n&a=" + Encryptor.encrypt(initVector, ss, toBeEncrypted);
System.out.println(url);
}
}
And this is Encryptor class:
import android.util.Base64;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.io.IOUtils;
import java.util.*;
public class Encryptor {
public static String encrypt(String initVector, String key, String clearText) {
try {
byte[] value = clearText.getBytes("UTF-8");
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(Base64.decode(key, 0), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(1, skeySpec, iv);
return encodeB64mod(cipher.doFinal(value));
} catch (Exception ex) {
ex.printStackTrace();
return "";
}
}
public static String encodeB64mod(byte[] bytes) {
return Base64.encodeToString(bytes, 2).replace('+', '-').replace(IOUtils.DIR_SEPARATOR_UNIX, '_').replace("=", "");
}
}
I tried to make it work in python but i am doing something wrong
EDIT:
import base64
from Crypto.Cipher import AES
from Crypto import Random
raw ='ch21979714702=put'.encode('utf-8')
key = 'pUypiz-7hJ0y_JtpKWaydp' #should be conveted to hex
iv = b'IqtY8jgALtjZNLM5'
cipher = AES.new( key, AES.MODE_CBC, iv )
print (base64.b64encode( cipher.encrypt( raw ) ).decode('utf-8') )
You are requiring base64url decoding, which isn't the generic base 64 encoding: it replaces + with - and / with _. This is why the reverse replacement is performed in Java.
Here is the correct way to decode it. Please upvote it and downvote any answers that do not include the replacement characters or final padding with the = character.
The problem was in Python was that you need to use padding
from Crypto.Util import Padding
Padding.pad(raw, 16, style='pkcs7')
https://www.pycryptodome.org/en/latest/src/util/util.html
Unlike in java in Python you need to add that you want to use padding.

java.security.InvalidKeyException: Parameters missing

I need to store the encrypted password in DB without storing any key or salt. And I want to make sure it's safer as well even though it's a two-way encryption. So after googling a bit, I have created a sample program to test it. My idea is to create a custom JPA AttributeConverter class to manage this.
Following is the program:
import java.security.Key;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
private static final String _algorithm = "AES";
private static final String _password = "_pasword*";
private static final String _salt = "_salt*";
private static final String _keygen_spec = "PBKDF2WithHmacSHA1";
private static final String _cipher_spec = "AES/CBC/PKCS5Padding";
public static String encrypt(String data) throws Exception {
Key key = getKey();
System.out.println(key.toString());
Cipher cipher = Cipher.getInstance(_cipher_spec);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.getEncoder().encodeToString(encVal);
System.out.println("Encrypted value of "+data+": "+encryptedValue);
return encryptedValue;
}
public static void decrypt(String encryptedData) throws Exception {
Key key = getKey();
System.out.println(key.toString());
Cipher cipher = Cipher.getInstance(_cipher_spec);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = Base64.getDecoder().decode(encryptedData);
byte[] decValue = cipher.doFinal(decordedValue);
String decryptedValue = new String(decValue);
System.out.println("Decrypted value of "+encryptedData+": "+decryptedValue);
}
private static Key getKey() throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance(_keygen_spec);
KeySpec spec = new PBEKeySpec(_password.toCharArray(), _salt.getBytes(), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), _algorithm);
return secret;
}
public static void main(String []str) throws Exception {
String value = encrypt("India#123");
decrypt(value);
}
}
But its throwing the following exception:
javax.crypto.spec.SecretKeySpec#17111
Encrypted value of India#123: iAv1fvjMnJqilg90rGztXA==
javax.crypto.spec.SecretKeySpec#17111
Exception in thread "main" java.security.InvalidKeyException: Parameters missing
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:469)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:313)
at javax.crypto.Cipher.implInit(Cipher.java:802)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1249)
at javax.crypto.Cipher.init(Cipher.java:1186)
at org.lp.test.Crypto.decrypt(Crypto.java:37)
at org.lp.test.Crypto.main(Crypto.java:54)
I am not able to figure this out.
I have rectified the exception based on #Luke Park's answer I have created a JPA AttributeConverter as below:
import java.security.Key;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply=true)
public class CryptoJPAConverter implements AttributeConverter<String, String> {
private static final String _algorithm = "AES";
private static final String _password = "_pasword*";
private static final String _salt = "_salt*";
private static final String _keygen_spec = "PBKDF2WithHmacSHA1";
private static final String _cipher_spec = "AES/ECB/PKCS5Padding";
#Override
public String convertToDatabaseColumn(String clearText) {
Key key;
Cipher cipher;
try {
key = getKey();
cipher = Cipher.getInstance(_cipher_spec);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = cipher.doFinal(clearText.getBytes());
String encryptedValue = Base64.getEncoder().encodeToString(encVal);
return encryptedValue;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Override
public String convertToEntityAttribute(String encryptedText) {
Key key;
try {
key = getKey();
Cipher cipher = Cipher.getInstance(_cipher_spec);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = Base64.getDecoder().decode(encryptedText);
byte[] decValue = cipher.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Key getKey() throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance(_keygen_spec);
KeySpec spec = new PBEKeySpec(_password.toCharArray(), _salt.getBytes(), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), _algorithm);
return secret;
}
}
I have used the two way encryption because I need to pass the password as clear text to Java mail client.
Suggestion and comments are welcome
You are using AES/CBC/PKCS5Padding but you are not passing an IV to your cipher.init calls.
CBC mode requires a random IV for each encryption operation, you should generate this using SecureRandom each time you encrypt, and pass the value in as an IvParameterSpec. You'll need the same IV for decryption. It is common to prepend the IV to the ciphertext and retrieve when required.
On a seperate note, encrypting passwords is really quite a terrible idea. The fact that you have to ask this question at all somewhat proves that you aren't in a position to be making security-related decisions. Do yourself and your project a favour and hash your passwords instead. PBKDF2 and bcrypt are both decent ways of doing so.

Decrypt OpenSSL command using AES-256/CBC in Java [duplicate]

I need to decrypt in JAVA a file encrypted in UNIX with the following command:
openssl aes-256-cbc -a -salt -in password.txt -out password.txt.enc
mypass
mypass
I have to decrypt in java as I do here I do in UNIX
openssl aes-256-cbc -d -a -in password.txt.enc -out password.txt.new
mypass
Someone can give me a java code to do this?
OpenSSL generally uses its own password based key derivation method, specified in EVP_BytesToKey, please see the code below. Furthermore, it implicitly encodes the ciphertext as base 64 over multiple lines, which would be required to send it within the body of a mail message.
So the result is, in pseudocode:
salt = random(8)
keyAndIV = BytesToKey(password, salt, 48)
key = keyAndIV[0..31]
iv = keyAndIV[32..47]
ct = AES-256-CBC-encrypt(key, iv, plaintext)
res = base64MimeEncode("Salted__" | salt | ct))
and the decryption therefore is:
(salt, ct) = base64MimeDecode(res)
key = keyAndIV[0..31]
iv = keyAndIV[32..47]
pt = AES-256-CBC-decrypt(key, iv, plaintext)
which can be implemented in Java like this:
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.util.encoders.Base64;
public class OpenSSLDecryptor {
private static final Charset ASCII = Charset.forName("ASCII");
private static final int INDEX_KEY = 0;
private static final int INDEX_IV = 1;
private static final int ITERATIONS = 1;
private static final int ARG_INDEX_FILENAME = 0;
private static final int ARG_INDEX_PASSWORD = 1;
private static final int SALT_OFFSET = 8;
private static final int SALT_SIZE = 8;
private static final int CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE;
private static final int KEY_SIZE_BITS = 256;
/**
* Thanks go to Ola Bini for releasing this source on his blog.
* The source was obtained from here .
*/
public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md,
byte[] salt, byte[] data, int count) {
byte[][] both = new byte[2][];
byte[] key = new byte[key_len];
int key_ix = 0;
byte[] iv = new byte[iv_len];
int iv_ix = 0;
both[0] = key;
both[1] = iv;
byte[] md_buf = null;
int nkey = key_len;
int niv = iv_len;
int i = 0;
if (data == null) {
return both;
}
int addmd = 0;
for (;;) {
md.reset();
if (addmd++ > 0) {
md.update(md_buf);
}
md.update(data);
if (null != salt) {
md.update(salt, 0, 8);
}
md_buf = md.digest();
for (i = 1; i < count; i++) {
md.reset();
md.update(md_buf);
md_buf = md.digest();
}
i = 0;
if (nkey > 0) {
for (;;) {
if (nkey == 0)
break;
if (i == md_buf.length)
break;
key[key_ix++] = md_buf[i];
nkey--;
i++;
}
}
if (niv > 0 && i != md_buf.length) {
for (;;) {
if (niv == 0)
break;
if (i == md_buf.length)
break;
iv[iv_ix++] = md_buf[i];
niv--;
i++;
}
}
if (nkey == 0 && niv == 0) {
break;
}
}
for (i = 0; i < md_buf.length; i++) {
md_buf[i] = 0;
}
return both;
}
public static void main(String[] args) {
try {
// --- read base 64 encoded file ---
File f = new File(args[ARG_INDEX_FILENAME]);
List<String> lines = Files.readAllLines(f.toPath(), ASCII);
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line.trim());
}
String dataBase64 = sb.toString();
byte[] headerSaltAndCipherText = Base64.decode(dataBase64);
// --- extract salt & encrypted ---
// header is "Salted__", ASCII encoded, if salt is being used (the default)
byte[] salt = Arrays.copyOfRange(
headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
byte[] encrypted = Arrays.copyOfRange(
headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
// --- specify cipher and digest for EVP_BytesToKey method ---
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
MessageDigest md5 = MessageDigest.getInstance("MD5");
// --- create key and IV ---
// the IV is useless, OpenSSL might as well have use zero's
final byte[][] keyAndIV = EVP_BytesToKey(
KEY_SIZE_BITS / Byte.SIZE,
aesCBC.getBlockSize(),
md5,
salt,
args[ARG_INDEX_PASSWORD].getBytes(ASCII),
ITERATIONS);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
// --- initialize cipher instance and decrypt ---
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
String answer = new String(decrypted, ASCII);
System.out.println(answer);
} catch (BadPaddingException e) {
// AKA "something went wrong"
throw new IllegalStateException(
"Bad password, algorithm, mode or padding;" +
" no salt, wrong number of iterations or corrupted ciphertext.");
} catch (IllegalBlockSizeException e) {
throw new IllegalStateException(
"Bad algorithm, mode or corrupted (resized) ciphertext.");
} catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
Beware that the code specifies ASCII as character set. The character set used may differ for your application / terminal / OS.
In general you should force OpenSSL to use the NIST approved PBKDF2 algorithm, as using the OpenSSL key derivation method - with an iteration count of 1 - is insecure. This may force you to use a different solution than OpenSSL. Note that password based encryption is inherently rather insecure - passwords are much less secure than randomly generated symmetric keys.
OpenSSL 1.1.0c changed the digest algorithm used in some internal components. Formerly, MD5 was used, and 1.1.0 switched to SHA256. Be careful the change is not affecting you in both EVP_BytesToKey and commands like openssl enc.
It's probably best to explicitly specify the digest in the command line interface (e.g. -md md5 for backwards compatibility or sha-256 for forwards compatibility) for the and make sure that the Java code uses the same digest algorithm ("MD5" or "SHA-256" including the dash). Also see the information in this answer.
Below are OpenSSLPBEInputStream and OpenSSLPBEOutputStream which can be used to encrypt/decrypt arbitrary streams of bytes in a way that is compatible with OpenSSL.
Example usage:
// The original clear text bytes
byte[] originalBytes = ...
// Encrypt these bytes
char[] pwd = "thePassword".toCharArray();
ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
OpenSSLPBEOutputStream encOS = new OpenSSLPBEOutputStream(byteOS, ALGORITHM, 1, pwd);
encOS.write(originalBytes);
encOS.flush();
byte[] encryptedBytes = byteOS.toByteArray();
// Decrypt the bytes
ByteArrayInputStream byteIS = new ByteArrayInputStream(encryptedBytes);
OpenSSLPBEInputStream encIS = new OpenSSLPBEInputStream(byteIS, ALGORITHM, 1, pwd);
Where ALGORITHM (using just JDK classes) can be: "PBEWithMD5AndDES", "PBEWithMD5AndTripleDES", "PBEWithSHA1AndDESede", "PBEWithSHA1AndRC2_40".
To handle "openssl aes-256-cbc -a -salt -in password.txt -out password.txt.enc" of the original poster, add bouncey castle to the classpath, and use algorthm= "PBEWITHMD5AND256BITAES-CBC-OPENSSL".
/* Add BC provider, and fail fast if BC provider is not in classpath for some reason */
Security.addProvider(new BouncyCastleProvider());
The dependency:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.44</version>
</dependency>
The input stream:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
public class OpenSSLPBEInputStream extends InputStream {
private final static int READ_BLOCK_SIZE = 64 * 1024;
private final Cipher cipher;
private final InputStream inStream;
private final byte[] bufferCipher = new byte[READ_BLOCK_SIZE];
private byte[] bufferClear = null;
private int index = Integer.MAX_VALUE;
private int maxIndex = 0;
public OpenSSLPBEInputStream(final InputStream streamIn, String algIn, int iterationCount, char[] password)
throws IOException {
this.inStream = streamIn;
try {
byte[] salt = readSalt();
cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.DECRYPT_MODE, algIn, iterationCount);
} catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IOException(e);
}
}
#Override
public int available() throws IOException {
return inStream.available();
}
#Override
public int read() throws IOException {
if (index > maxIndex) {
index = 0;
int read = inStream.read(bufferCipher);
if (read != -1) {
bufferClear = cipher.update(bufferCipher, 0, read);
}
if (read == -1 || bufferClear == null || bufferClear.length == 0) {
try {
bufferClear = cipher.doFinal();
} catch (IllegalBlockSizeException | BadPaddingException e) {
bufferClear = null;
}
}
if (bufferClear == null || bufferClear.length == 0) {
return -1;
}
maxIndex = bufferClear.length - 1;
}
return bufferClear[index++] & 0xff;
}
private byte[] readSalt() throws IOException {
byte[] headerBytes = new byte[OpenSSLPBECommon.OPENSSL_HEADER_STRING.length()];
inStream.read(headerBytes);
String headerString = new String(headerBytes, OpenSSLPBECommon.OPENSSL_HEADER_ENCODE);
if (!OpenSSLPBECommon.OPENSSL_HEADER_STRING.equals(headerString)) {
throw new IOException("unexpected file header " + headerString);
}
byte[] salt = new byte[OpenSSLPBECommon.SALT_SIZE_BYTES];
inStream.read(salt);
return salt;
}
}
The output stream:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
public class OpenSSLPBEOutputStream extends OutputStream {
private static final int BUFFER_SIZE = 5 * 1024 * 1024;
private final Cipher cipher;
private final OutputStream outStream;
private final byte[] buffer = new byte[BUFFER_SIZE];
private int bufferIndex = 0;
public OpenSSLPBEOutputStream(final OutputStream outputStream, String algIn, int iterationCount,
char[] password) throws IOException {
outStream = outputStream;
try {
/* Create and use a random SALT for each instance of this output stream. */
byte[] salt = new byte[PBECommon.SALT_SIZE_BYTES];
new SecureRandom().nextBytes(salt);
cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.ENCRYPT_MODE, algIn, iterationCount);
/* Write header */
writeHeader(salt);
} catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IOException(e);
}
}
#Override
public void write(int b) throws IOException {
buffer[bufferIndex] = (byte) b;
bufferIndex++;
if (bufferIndex == BUFFER_SIZE) {
byte[] result = cipher.update(buffer, 0, bufferIndex);
outStream.write(result);
bufferIndex = 0;
}
}
#Override
public void flush() throws IOException {
if (bufferIndex > 0) {
byte[] result;
try {
result = cipher.doFinal(buffer, 0, bufferIndex);
outStream.write(result);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IOException(e);
}
bufferIndex = 0;
}
}
#Override
public void close() throws IOException {
flush();
outStream.close();
}
private void writeHeader(byte[] salt) throws IOException {
outStream.write(OpenSSLPBECommon.OPENSSL_HEADER_STRING.getBytes(OpenSSLPBECommon.OPENSSL_HEADER_ENCODE));
outStream.write(salt);
}
}
Small common class:
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
class OpenSSLPBECommon {
protected static final int SALT_SIZE_BYTES = 8;
protected static final String OPENSSL_HEADER_STRING = "Salted__";
protected static final String OPENSSL_HEADER_ENCODE = "ASCII";
protected static Cipher initializeCipher(char[] password, byte[] salt, int cipherMode,
final String algorithm, int iterationCount) throws NoSuchAlgorithmException, InvalidKeySpecException,
InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException {
PBEKeySpec keySpec = new PBEKeySpec(password);
SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);
SecretKey key = factory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(cipherMode, key, new PBEParameterSpec(salt, iterationCount));
return cipher;
}
}
In Kotlin:
package io.matthewnelson.java_crypto
import java.util.*
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
class OpenSSL {
/**
* Will decrypt a string value encrypted by OpenSSL v 1.1.1+ using the following cmds from terminal:
*
* echo "Hello World!" | openssl aes-256-cbc -e -a -p -salt -pbkdf2 -iter 15739 -k qk4aX-EfMUa-g4HdF-fjfkU-bbLNx-15739
*
* Terminal output:
* salt=CC73B7D29FE59CE1
* key=31706F84185EA4B5E8E040F2C813F79722F22996B48B82FF98174F887A9B9993
* iv =1420310D41FD7F48E5D8722B9AC1C8DD
* U2FsdGVkX1/Mc7fSn+Wc4XLwDsmLdR8O7K3bFPpCglA=
* */
fun decrypt_AES256CBC_PBKDF2_HMAC_SHA256(
password: String,
hashIterations: Int,
encryptedString: String
): String {
val encryptedBytes = Base64.getDecoder().decode(encryptedString)
// Salt is bytes 8 - 15
val salt = encryptedBytes.copyOfRange(8, 16)
// println("Salt: ${salt.joinToString("") { "%02X".format(it) }}")
// Derive 48 byte key
val keySpec = PBEKeySpec(password.toCharArray(), salt, hashIterations, 48 * 8)
val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val secretKey = keyFactory.generateSecret(keySpec)
// Decryption Key is bytes 0 - 31 of the derived key
val key = secretKey.encoded.copyOfRange(0, 32)
// println("Key: ${key.joinToString("") { "%02X".format(it) }}")
// Input Vector is bytes 32 - 47 of the derived key
val iv = secretKey.encoded.copyOfRange(32, 48)
// println("IV: ${iv.joinToString("") { "%02X".format(it) }}")
// Cipher Text is bytes 16 - end of the encrypted bytes
val cipherText = encryptedBytes.copyOfRange(16, encryptedBytes.lastIndex + 1)
// Decrypt the Cipher Text and manually remove padding after
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
val decrypted = cipher.doFinal(cipherText)
// println("Decrypted: ${decrypted.joinToString("") { "%02X".format(it) }}")
// Last byte of the decrypted text is the number of padding bytes needed to remove
val plaintext = decrypted.copyOfRange(0, decrypted.lastIndex + 1 - decrypted.last().toInt())
return plaintext.toString(Charsets.UTF_8)
}
}
Don't use ase-128-cbc, use ase-128-ecb.
only take first 16 bytes as key because key is 128 bits
hash output is printed in hex, which every 2 chars presents a byte value
hashpwd=echo -n $password| openssl sha1 | sed 's#.*=\\s*##g' | cut -c 1-32
openssl enc -aes-128-ecb -salt -in -out -K $hashpwd
Java Code is here:
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
//openssl enc -nosalt -aes-128-ecb
// -in <input file>
// -out <output file>
// -K <16 bytes in hex, for example : "abc" can be hashed in SHA-1, the first 16 bytes in hex is a9993e364706816aba3e25717850c26c>
private final static String TRANSFORMATION = "AES"; // use aes-128-ecb in openssl
public static byte[] encrypt(String passcode, byte[] data) throws CryptographicException {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, genKeySpec(passcode));
return cipher.doFinal(data);
} catch (Exception ex) {
throw new CryptographicException("Error encrypting", ex);
}
}
public static String encryptWithBase64(String passcode, byte[] data) throws CryptographicException {
return new BASE64Encoder().encode(encrypt(passcode, data));
}
public static byte[] decrypt(String passcode, byte[] data) throws CryptographicException {
try {
Cipher dcipher = Cipher.getInstance(TRANSFORMATION);
dcipher.init(Cipher.DECRYPT_MODE, genKeySpec(passcode));
return dcipher.doFinal(data);
} catch (Exception e) {
throw new CryptographicException("Error decrypting", e);
}
}
public static byte[] decryptWithBase64(String passcode, String encrptedStr) throws CryptographicException {
try {
return decrypt(passcode, new BASE64Decoder().decodeBuffer(encrptedStr));
} catch (Exception e) {
throw new CryptographicException("Error decrypting", e);
}
}
public static SecretKeySpec genKeySpec(String passcode) throws UnsupportedEncodingException, NoSuchAlgorithmException {
byte[] key = passcode.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
return new SecretKeySpec(key, TRANSFORMATION);
}
Tested and passed in jdk6 and jdk8.

What is the difference between These method below? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
i am learning encryption in java and used these two tutorials yet This from Code2learn
and This from howtodoinjava.com
when i try to encrypt hello from these example code both gives different encrypted strings, i have tried a lot to find that what is the main difference between these examples that is causing different encrypted strings of same passwords as both are using AES ..
Code from code 2 learn
package nomad;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.*;
import sun.misc.*;
public class AESencrp {
private static final String ALGO = "AES";
private static final byte[] keyValue =
new byte[] { 'T', 'h', 'e', 'B', 'e', 's', 't',
'S', 'e', 'c', 'r','e', 't', 'K', 'e', 'y' };
public static String encrypt(String Data) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = new BASE64Encoder().encode(encVal);
return encryptedValue;
}
public static String decrypt(String encryptedData) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
private static Key generateKey() throws Exception {
Key key = new SecretKeySpec(keyValue, ALGO);
return key;
}
}
package nomad;
public class Checker {
public static void main(String[] args) throws Exception {
String password = "mypassword";
String passwordEnc = AESencrp.encrypt(password);
String passwordDec = AESencrp.decrypt(passwordEnc);
System.out.println("Plain Text : " + password);
System.out.println("Encrypted Text : " + passwordEnc);
System.out.println("Decrypted Text : " + passwordDec);
}
}
Code for how to do in java
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AES {
private static SecretKeySpec secretKey;
private static byte[] key;
public static void setKey(String myKey)
{
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String encrypt(String strToEncrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
}
catch (Exception e)
{
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
public static String decrypt(String strToDecrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
}
public static void main(String[] args)
{
final String secretKey = "ssshhhhhhhhhhh!!!!";
String originalString = "howtodoinjava.com";
String encryptedString = AES.encrypt(originalString, secretKey) ;
String decryptedString = AES.decrypt(encryptedString, secretKey) ;
System.out.println(originalString);
System.out.println(encryptedString);
System.out.println(decryptedString);
}
I have added the codes as one of the comments says that external links are not accessible by everyone .
Keep in mind that secretkeys and passowrd are the same in my code.
Adding my own code..
name it as code 1
AES Class
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
public class AES {
public static SecretKeySpec secretKeySpec;
private static byte[] key;
public static String setKey(String myKey) throws Exception {
MessageDigest sha=null;
key =myKey.getBytes("UTF-8");
sha=MessageDigest.getInstance("SHA-1");
key=sha.digest(key);
key= Arrays.copyOf(key,16);
secretKeySpec=new SecretKeySpec(key,"AES");
return myKey;
}
public static String encrypt(String strToencrypt, String secret) throws Exception {
String s= setKey(secret);
System.out.println(s);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToencrypt.getBytes("UTF-8")));
}
public static String decryt(String strToDec , String secret) throws Exception {
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,secretKeySpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDec)));
}
}
Main Class of above AES Class
public class Main {
public static void main(String[] args) throws Exception {
AES aes = new AES();
String ss=null;
String sd=null;
ss=aes.encrypt("hello","TheBestSecretKey");
sd=aes.decryt(ss,"TheBestSecretKey");
System.out.println("The Encrypted == " + ss);
System.out.println("The Decrypted == " + sd);
}
}
Another Code
Name It as Code 2
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
public class Main {
private static final String ALGO ="AES";
private static String string = "TheBestSecretKey";
private static byte[] key ;
private static SecretKeySpec secretKeySpec;
public static String aesEncrypt(String en) throws Exception {
Key key = new SecretKeySpec(string.getBytes(),ALGO);
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE,key);
byte[] encValue =c.doFinal(en.getBytes("UTF-8"));
String encryptedValue= new BASE64Encoder().encode(encValue);
return encryptedValue;
}
public static String aesDecrypt(String De) throws Exception{
Key key = new SecretKeySpec(string.getBytes(),"AES");
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE,key);
// return new String(c.doFinal(Base64.getDecoder().decode(De)));
byte[]decodedVlue=new BASE64Decoder().decodeBuffer(De);
byte[] decValue = c.doFinal(decodedVlue);
String deccryptedValue = new String(decValue);
return deccryptedValue;
}
public static void main(String[] args) throws Exception {
String password = "hello";
String passEnc= Main.aesEncrypt(password);
System.out.println(passEnc);
String passDec = Main.aesDecrypt(passEnc);
System.out.println(passDec);
}
}
here are the codes
now i am also posting encrypted strings
using SecretSpecKey TheBestSecretKey and hello as the string to be encrypted
CODE 1 UlQiIs/K0EwcSKbWMjcT1g==
CODE 2 hHDBo1dJYj5RcjdFA6BBfw==

AES Encryption Given Final Block Not Properly Padded

I'm trying to create a class that will allow me to encrypt and decrypt strings using the AES algorithm. I'm using the exception from http://aesencryption.net/#Java-aes-encryption-example but have modified the code to suit my needs.
This is my Main.java:
public class Main {
public static void main(String[] args) {
AES256 aes = new AES256();
aes.setKey("Secret Key");
String enc = "";
enc = aes.encrypt("qwertyuiopasdfgh");
System.out.println(enc);
System.out.println(aes.decrypt(enc));
}
}
And this is my AES256.java:
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class AES256 {
private SecretKeySpec secretKey;
private byte[] key;
public void setKey(String key) {
MessageDigest sha = null;
try {
this.key = key.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
this.key = sha.digest(this.key);
this.key = Arrays.copyOf(this.key, 16);
secretKey = new SecretKeySpec(this.key, "AES");
} catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public String getSecretKey() {
return secretKey.toString();
}
public String getKey() {
return new String(key);
}
public String encrypt(String string) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getMimeEncoder().encodeToString(string.getBytes());
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
return null;
}
public String decrypt(String string) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getMimeDecoder().decode(string.getBytes())));
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return null;
}
}
This is the error that is being thrown:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2121)
at AES256.decrypt(AES256.java:55)
at Main.main(Main.java:13)
Does anybody know what is causing this error?
You return the original string in its base64-encoded form:
return Base64.getMimeEncoder().encodeToString(string.getBytes());
You'd want to use the cipher in there as well:
return Base64.getMimeEncoder().encodeToString(cipher.doFinal(string.getBytes()));
Independent of that, when depolying own crypto please be aware of the impacts of cipher modes, padding, etc. For example the ECB mode you're using will produce the same ciphertext from the same plaintext, e.g. the ciphertext might lead hints about the original text, as in the famous encrypted tux image:
Image Copyright: All uses are permitted provided that Larry Ewing, the owner of the original image, who requires that you mention him, his email address, lewing#isc.tamu.edu, and The GIMP, according to http://www.isc.tamu.edu/~lewing/linux/.
For more details on that, see Wikipedia's article about block cipher modes.

Categories

Resources