Decrypt aes-128-gcm encoded content with JAVA Cipher using PHP OpenSSL - java

I must decrypt some data send to my website, using aes-128-gcm encrypted with JAVA Cipher.
The customer has a provider working with Talend who must send me some information via URL parameter, and I'm stuck trying to decrypt the Data using PHP Openssl. The provider is not really able to tell me how is it working on his side, and is not too sure about the variables used on the generated code (like IV Length for instance).
The encryption method is done on JAVA using a Talend component. The provider send me the Talend generated code as an example.
private static String encrypt(String data, String mainKey, int ivLength) throws Exception {
final byte[] dataBytes = data.getBytes(UNICODE_FORMAT);
byte[] initializationVector = generateInitializationVector(ivLength);
final Cipher cipher = getAesGcmCipher(Cipher.ENCRYPT_MODE, mainKey, initializationVector);
final byte[] encryptedData = cipher.doFinal(dataBytes);
final byte[] encryptedBytes = new byte[encryptedData.length + ivLength];
System.arraycopy(initializationVector, 0, encryptedBytes, 0, ivLength);
System.arraycopy(encryptedData, 0, encryptedBytes, ivLength, encryptedData.length);
return BASE64_ENCODER.apply(encryptedBytes);
}
private static String decrypt(String data, String mainKey, int ivLength) throws Exception {
final byte[] encryptedBytes = BASE64_DECODER.apply(data.getBytes(UNICODE_FORMAT));
final byte[] initializationVector = new byte[ivLength];
System.arraycopy(encryptedBytes, 0, initializationVector, 0, ivLength);
final Cipher cipher = getAesGcmCipher(Cipher.DECRYPT_MODE, mainKey, initializationVector);
return new String(cipher.doFinal(encryptedBytes, ivLength, encryptedBytes.length - ivLength),
UNICODE_FORMAT);
}
He also send me some constants initialised with the class he is using:
static final String ALGO = "AES"; //$NON-NLS-1$
static final String GCMALGO = "AES/GCM/NoPadding"; //$NON-NLS-1$
static final String UNICODE_FORMAT = "UTF8"; //$NON-NLS-1$
static final String DES_ENCRYPTION_SCHEME = "DES"; //$NON-NLS-1$
private static final int DEFAULT_IV_LENGTH = 16;
public static final String NULL_PARAMETER_MESSAGE = "The parameter should not be null"; //$NON-NLS-1$
public static final String EMPTY_PARAMETER_MESSAGE = "String is empty"; //$NON-NLS-1$
private static final String KEY_GEN_ALGO = "PBKDF2WithHmacSHA256"; //$NON-NLS-1$
static final Random random = new SecureRandom();
static final BASE64Encoder b64Encoder = new DataMasking().new BASE64Encoder();
static final BASE64Decoder b64Dencoder = new DataMasking().new BASE64Decoder();
public static final Function<byte[], String> BASE64_ENCODER = bytes -> Base64.getEncoder().encodeToString(bytes);
public static final Function<byte[], byte[]> BASE64_DECODER = bytes -> Base64.getDecoder().decode(bytes);
As I understand the Initialisation Vector is placed at the beginning of the string, and I learn that the Tag is automatically placed at the end by the Java Cipher object.
I tried to encrypt and decrypt some content by my own on my PHP side, using openssl_encrypt()
and openssl_decrypt(), and it is working fine, but I cannot manage to decrypt the data the JAVA application is sending me.
I wonder if it has anything to do with the manipulation of string I have to do on binary data, or if the provider didn't give me all the necessary information I needed.
I also don't know what would be the Tag_length I have to use to decrypt the data.
The provider also tells me the IV_Length is 16 characters long, but when I use openssl_cipher_iv_length('aes-128-gcm'), it propose 12.
Here is the code I'm having for now on my PHP side:
/**
* #param string $str
* The URL parameter string
*/
function test_decrypt($str) {
$key = 'MySuperPassword7';
$cipher = 'aes-128-gcm';
$iv_len = 16;
$tag_length = 16;
echo $str . '<br>';
/**
* Encryption test
*/
// $tag = "";
// $iv = openssl_random_pseudo_bytes($iv_len);
// $enc_str = openssl_encrypt('Test of data to send', $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag, "", $tag_length);
// $encrypt = base64_encode($iv.$enc_str.$tag);
// echo $key . '::' . $iv . '::' . $tag . '<br>';
// echo '$enc_str<pre>';
// var_dump($enc_str);
// echo '</pre>';
// echo '$encrypt<pre>';
// var_dump($encrypt);
// echo '</pre>';
/**
* Decryption part
*/
$encrypt = base64_decode($str);
$iv = substr($encrypt, 0, $iv_len);
$tag = substr($encrypt, - $tag_length);
$ciphertext = substr($encrypt, $iv_len, -$tag_length);
$uncrypt = openssl_decrypt($ciphertext, $cipher, $key, 0, $iv, $tag);//OPENSSL_RAW_DATA + OPENSSL_NO_PADDING
echo $iv_len . '::' . $tag_length . '<br>';
echo $key . '::' . $iv . '::' . $tag . '<br>';
echo '$encrypt<pre>';
var_dump($encrypt);
echo '</pre>';
echo '$ciphertext<pre>';
var_dump($ciphertext);
echo '</pre>';
echo '$uncrypt<pre>';
var_dump($uncrypt);
echo '</pre>';
exit;
}
I also tried to use online tools to decrypt the data the Java app is sending me, but I couldn't find anything working for so far.
Edit
As asked by #JohnConde in the comment, I can share the encrypted string and key I'm currently trying to decrypt.
We decided to test with the same data as the answer of Michael Fehr :
Key : '1234567890123456'
Plain text : "Secret data for TytooF"
Encrypted text : VODKjhFETSxMcaa7x/LIOYCfmqD1iWSCuxX80reQ1KoFhmU8/A5AlH0Pg/ZoK1eNSdhBpUed
The length is the same as the encrypted one of the answer (P8kU/Gf27LU9qgmLpA8gpfHmdVuW5cC7XeKVIgmb3rgGzyjG0YHX5P/cFfRRoZpUxxTEKzj8).
As before, I'm not able to decrypt the data...
Here is the full code of the Talend component:
// ============================================================================
//
// Copyright (C) 2006-2019 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package routines;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PushbackInputStream;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* created by talend on 2016-04-08 Detailled comment.
*
*/
public class DataMasking {
static final String ALGO = "AES"; //$NON-NLS-1$
static final String GCMALGO = "AES/GCM/NoPadding"; //$NON-NLS-1$
static final String UNICODE_FORMAT = "UTF8"; //$NON-NLS-1$
static final String DES_ENCRYPTION_SCHEME = "DES"; //$NON-NLS-1$
private static final int DEFAULT_IV_LENGTH = 16;
public static final String NULL_PARAMETER_MESSAGE = "The parameter should not be null"; //$NON-NLS-1$
public static final String EMPTY_PARAMETER_MESSAGE = "String is empty"; //$NON-NLS-1$
private static final String KEY_GEN_ALGO = "PBKDF2WithHmacSHA256"; //$NON-NLS-1$
static final Random random = new SecureRandom();
static final BASE64Encoder b64Encoder = new DataMasking().new BASE64Encoder();
static final BASE64Decoder b64Dencoder = new DataMasking().new BASE64Decoder();
public static final Function<byte[], String> BASE64_ENCODER = bytes -> Base64.getEncoder().encodeToString(bytes);
public static final Function<byte[], byte[]> BASE64_DECODER = bytes -> Base64.getDecoder().decode(bytes);
public static class DataMaskingRoutineException extends RuntimeException {
private static final long serialVersionUID = -8622896150657449668L;
public DataMaskingRoutineException() {
super();
}
public DataMaskingRoutineException(String s) {
super(s);
}
public DataMaskingRoutineException(String s, Object o) {
super(s);
System.out.println(o);
}
}
/**
* Encrypt String: Encrypts a string using AES 128 .
* warning: this is not considered a secure function.
*
*
* {talendTypes} String
*
* {Category} Data Masking
*
* {param} String("foo") encryptString: The string to be encrypted.
*
* {param} byte[](new byte[] { 'T', 'a', 'l', 'e', 'n', 'd', 's', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' })
* keyValue: the
* key material of the secret key. The contents of the array are copied to protect against subsequent modification.
*
* {example} encryptAES("foo", new byte[] { 'T', 'a', 'l', 'e', 'n', 'd', 's', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' })
* result is UQ0VJZq5ymFkMYQeDrPi0A==
*
* #deprecated use {#link #encryptAESGCM(String, String, int)} instead of it
*
*/
#Deprecated
public static String encryptAES(String encryptString, byte[] keyValue) {
if (encryptString == null || keyValue == null) {
return NULL_PARAMETER_MESSAGE;
}
if (encryptString.length() == 0) {
return EMPTY_PARAMETER_MESSAGE;
}
try {
Key key = new SecretKeySpec(keyValue, ALGO);
Cipher cipher = Cipher.getInstance(ALGO);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = cipher.doFinal(encryptString.getBytes());
String encryptedValue = b64Encoder.encode(encVal);
return encryptedValue;
} catch (Exception e) {
throw new DataMaskingRoutineException(e.getMessage(), e);
}
}
/**
* Encrypt String: Encrypts a string using AES GCM 128 .
*
* {talendTypes} String
*
* {Category} Data Masking
*
* {param} String("foo") encryptString: The string to be encrypted.
*
* {param} String("TalendMainKey123") the main key used to encrypt the data.
*
* {example} encryptAESGCM("foo","TalendMainKey123") result could be
* +ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4= (but it should change from an execution to another).
*
*/
public static String encryptAESGCM(String encryptString, String mainKey) {
return encryptAESGCM(encryptString, mainKey, DEFAULT_IV_LENGTH);
}
/**
* Encrypt String: Encrypts a string using AES GCM 128 .
*
* {talendTypes} String
*
* {Category} Data Masking
*
* {param} String("foo") encryptString: The string to be encrypted.
*
* {param} String("TalendMainKey123") the main key used to encrypt the data.
*
* {param} int the length of initializationVector. must be one of 12/13/14/15/16.
*
* {example} encryptAESGCM("foo","TalendMainKey123",16) result could be
* +ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4= (but it should change from an execution to another).
*
*/
public static String encryptAESGCM(String encryptString, String mainKey, int ivLength) {
if (encryptString == null || mainKey == null) {
return NULL_PARAMETER_MESSAGE;
}
if (encryptString.length() == 0) {
return EMPTY_PARAMETER_MESSAGE;
}
try {
return encrypt(encryptString, mainKey, ivLength);
} catch (Exception e) {
throw new DataMaskingRoutineException(e.getMessage(), e);
}
}
/**
* decrypt String: Decrypts a string using AES 128.
* warning: this is not considered a secure function.
*
*
* {talendTypes} String
*
* {Category} Data Masking
*
* {param} String("UQ0VJZq5ymFkMYQeDrPi0A==") encryptedString: The string to be decrypted.
*
* {param} byte[](new byte[] { 'T', 'a', 'l', 'e', 'n', 'd', 's', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' })
* keyValue: the key material of the secret key. The contents of the array are copied to protect against subsequent
* modification.
*
* {example} decryptAES("UQ0VJZq5ymFkMYQeDrPi0A==",new byte[] { 'T', 'a', 'l', 'e', 'n', 'd', 's', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' })
* result is "foo"
*
* #deprecated use {#link #decryptAESGCM(String, String, int)} instead of it
*
*/
#Deprecated
public static String decryptAES(String encryptedString, byte[] keyValue) {
if (encryptedString == null || keyValue == null) {
return NULL_PARAMETER_MESSAGE;
}
if (encryptedString.length() == 0) {
return EMPTY_PARAMETER_MESSAGE;
}
try {
Key key = new SecretKeySpec(keyValue, ALGO);
Cipher cipher = Cipher.getInstance(ALGO);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = b64Dencoder.decodeBuffer(encryptedString);
byte[] decValue = cipher.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
} catch (Exception e) {
throw new DataMaskingRoutineException(e.getMessage(), e);
}
}
/**
* decrypt String: Decrypts a string using AES GCM 128.
*
* {talendTypes} String
*
* {Category} Data Masking
*
* {param} String("+ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4=") encryptedString: The string to be decrypted.
*
* {param} String("TalendMainKey123") the main key used to decrypt the data.
*
* {param} int the length of initializationVector. must be one of 12/13/14/15/16.
*
* {example} decryptAESGCM("+ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4=","TalendMainKey123",16) result
* is "foo"
*
*/
public static String decryptAESGCM(String encryptedString, String mainKey, int ivLength) {
if (encryptedString == null || mainKey == null) {
return NULL_PARAMETER_MESSAGE;
}
if (encryptedString.length() == 0) {
return EMPTY_PARAMETER_MESSAGE;
}
try {
return decrypt(encryptedString, mainKey, ivLength);
} catch (Exception e) {
throw new DataMaskingRoutineException(e.getMessage(), e);
}
}
/**
* decrypt String: Decrypts a string using AES GCM 128.
*
* {talendTypes} String
*
* {Category} Data Masking
*
* {param} String("+ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4=") encryptedString: The string to be decrypted.
*
* {param} String("TalendMainKey123") the main key used to decrypt the data.
*
* {example} decryptAESGCM("+ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4=","TalendMainKey123") result
* is "foo"
*
*/
public static String decryptAESGCM(String encryptedString, String mainKey) {
return decryptAESGCM(encryptedString, mainKey, DEFAULT_IV_LENGTH);
}
/**
* Encrypt String: Encrypts a string using DES .
* warning: this is not considered a secure function.
*
*
* {talendTypes} String
*
* {Category} Data Masking
*
* {param} String("foo") unencryptedString: The string to be encrypted.
*
* {param} String("ThisIsSecretEncryptionKey") myEncryptionKey: the string with the DES key material.
*
* {example} encryptDES("foo") result is DmNj+x2LUXA=
*
* #throws Exception
*/
public static String encryptDES(String unencryptedString, String myEncryptionKey) {
if (unencryptedString == null || myEncryptionKey == null) {
return NULL_PARAMETER_MESSAGE;
}
if (unencryptedString.length() == 0) {
return EMPTY_PARAMETER_MESSAGE;
}
try {
String encryptedString = null;
String myEncryptionScheme = DES_ENCRYPTION_SCHEME;
byte[] keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
KeySpec myKeySpec = new DESKeySpec(keyAsBytes);
SecretKeyFactory mySecretKeyFactory = SecretKeyFactory.getInstance(myEncryptionScheme);
Cipher encipher = Cipher.getInstance(myEncryptionScheme);
SecretKey key = mySecretKeyFactory.generateSecret(myKeySpec);
encipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = encipher.doFinal(plainText);
encryptedString = b64Encoder.encode(encryptedText);
return encryptedString;
} catch (Exception e) {
throw new DataMaskingRoutineException(e.getMessage());
}
}
/**
* Decrypt String: Decrypts a string using DES .
* warning: this is not considered a secure function.
*
*
* {talendTypes} String
*
* {Category} Data Masking
*
* {param} String("DmNj+x2LUXA=") encryptedString: the string with the DES key material.
*
* {param} String("ThisIsSecretEncryptionKey") myDecryptionKey: The string to be encrypted.
*
* {example} decryptDES("DmNj+x2LUXA=") result is "foo"
*
*/
public static String decryptDES(String encryptedString, String myDecryptionKey) {
if (encryptedString == null || myDecryptionKey == null) {
return NULL_PARAMETER_MESSAGE;
}
if (encryptedString.length() == 0) {
return EMPTY_PARAMETER_MESSAGE;
}
try {
String decryptedText = null;
String myDecryptionScheme = DES_ENCRYPTION_SCHEME;
byte[] keyAsBytes = myDecryptionKey.getBytes(UNICODE_FORMAT);
KeySpec myKeySpec = new DESKeySpec(keyAsBytes);
Cipher decipher = Cipher.getInstance(myDecryptionScheme);
SecretKeyFactory mySecretKeyFactory = SecretKeyFactory.getInstance(myDecryptionScheme);
SecretKey key = mySecretKeyFactory.generateSecret(myKeySpec);
decipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = b64Dencoder.decodeBuffer(encryptedString);
byte[] plainText = decipher.doFinal(encryptedText);
StringBuilder stringBuilder = new StringBuilder();
for (byte element : plainText) {
stringBuilder.append((char) element);
}
decryptedText = stringBuilder.toString();
return decryptedText;
} catch (Exception e) {
throw new DataMaskingRoutineException(e.getMessage(), e);
}
}
/**
* This method generates a secret Key using the key-stretching algorithm PBKDF2 of
* javax.crypto.
* It is basically a hashing algorithm slow by design, in order to increase the time
* required for an attacker to try a lot of passwords in a bruteforce attack.
* <br>
* About the salt :
* <ul>
* <li>The salt is not secret, the use of Random is not critical and ensure determinism.</li>
* <li>The salt is important to avoid rainbow table attacks.</li>
* <li>The salt should be generated with SecureRandom() in case the passwords are stored.</li>
* <li>In that case the salt should be stored in plaintext next to the password and a unique user identifier.</li>
* </ul>
*
* #param password a password given as a {#code String}.
* #param keyLength key length to generate
* #return a {#code SecretKey} securely generated.
* #throws NoSuchAlgorithmException
* #throws InvalidKeySpecException
*/
private static byte[] generateSecretKeyFromPassword(String password, int keyLength)
throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] salt = new byte[keyLength];
new Random(password.hashCode()).nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_GEN_ALGO);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, keyLength << 3);
return factory.generateSecret(spec).getEncoded();
}
private static String encrypt(String data, String mainKey, int ivLength) throws Exception {
final byte[] dataBytes = data.getBytes(UNICODE_FORMAT);
byte[] initializationVector = generateInitializationVector(ivLength);
final Cipher cipher = getAesGcmCipher(Cipher.ENCRYPT_MODE, mainKey, initializationVector);
final byte[] encryptedData = cipher.doFinal(dataBytes);
final byte[] encryptedBytes = new byte[encryptedData.length + ivLength];
System.arraycopy(initializationVector, 0, encryptedBytes, 0, ivLength);
System.arraycopy(encryptedData, 0, encryptedBytes, ivLength, encryptedData.length);
return BASE64_ENCODER.apply(encryptedBytes);
}
private static String decrypt(String data, String mainKey, int ivLength) throws Exception {
final byte[] encryptedBytes = BASE64_DECODER.apply(data.getBytes(UNICODE_FORMAT));
final byte[] initializationVector = new byte[ivLength];
System.arraycopy(encryptedBytes, 0, initializationVector, 0, ivLength);
final Cipher cipher = getAesGcmCipher(Cipher.DECRYPT_MODE, mainKey, initializationVector);
return new String(cipher.doFinal(encryptedBytes, ivLength, encryptedBytes.length - ivLength),
UNICODE_FORMAT);
}
}
** EDIT 2 **
After debugging for some times with the provider, we finally decided to use the code provided by Michael Fehr.
What I understood from the code of the component, is that the difference is in the way the main key is used. But I'm not a Java developer, and maybe I misunderstood something.
Just in case it can help someone, here is the additional Java code missing for the IV generation, and the Secret key generation.
private static Cipher getAesGcmCipher(int encryptMode, String mainKey, byte[] initializationVector)
throws Exception {
int ivLength = initializationVector.length;
if (Stream.of(12, 13, 14, 15, 16).noneMatch(i -> i == ivLength)) {
throw new IllegalArgumentException("Invalid IV length"); //$NON-NLS-1$
}
final Cipher cipher = Cipher.getInstance(GCMALGO);
SecretKey key = new SecretKeySpec(generateSecretKeyFromPassword(mainKey, mainKey.length()), ALGO);
final GCMParameterSpec spec = new GCMParameterSpec(ivLength * 8, initializationVector);
cipher.init(encryptMode, key, spec);
return cipher;
}
private static byte[] generateSecretKeyFromPassword(String password, int keyLength)
throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] salt = new byte[keyLength];
new Random(password.hashCode()).nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_GEN_ALGO);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, keyLength << 3);
return factory.generateSecret(spec).getEncoded();
}

I setup your code in Java and made an encryption with a (fixed) key and an random initialization vector:
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Random;
public class SO_Main_Final {
static final String ALGO = "AES"; //$NON-NLS-1$
static final String GCMALGO = "AES/GCM/NoPadding"; //$NON-NLS-1$
static final String UNICODE_FORMAT = "UTF8"; //$NON-NLS-1$
static final Random random = new SecureRandom();
public static void main(String[] args) throws Exception {
System.out.println("https://stackoverflow.com/questions/62129604/decrypt-aes-128-gcm-encoded-content-with-java-cipher-using-php-openssl");
String myData = "Secret data for TytooF";
String myKey = "1234567890123456";
String encryptString = encrypt(myData, myKey, 16);
String decryptString = decrypt(encryptString, myKey, 16);
System.out.println("encryptString: " + encryptString);
System.out.println("decryptString: " + decryptString);
}
private static String encrypt(String data, String mainKey, int ivLength) throws Exception {
final byte[] dataBytes = data.getBytes(UNICODE_FORMAT);
// byte[] initializationVector = generateInitializationVector(ivLength);
byte[] initializationVector = new byte[ivLength];
random.nextBytes(initializationVector);
SecretKeySpec secretKeySpec = new SecretKeySpec(mainKey.getBytes(UNICODE_FORMAT), ALGO);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, initializationVector);
Cipher cipher = Cipher.getInstance(GCMALGO);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
final byte[] encryptedData = cipher.doFinal(dataBytes);
final byte[] encryptedBytes = new byte[encryptedData.length + ivLength];
System.arraycopy(initializationVector, 0, encryptedBytes, 0, ivLength);
System.arraycopy(encryptedData, 0, encryptedBytes, ivLength, encryptedData.length);
System.out.println("data [String] : " + data);
System.out.println("data length: " + dataBytes.length
+ " data: " + bytesToHex(dataBytes));
System.out.println("mainKey length: " + mainKey.getBytes(UNICODE_FORMAT).length
+ " data: " + bytesToHex(mainKey.getBytes(UNICODE_FORMAT)));
System.out.println("initvector length: " + initializationVector.length
+ " data: " + bytesToHex(initializationVector));
System.out.println("encryptedBytes length: " + encryptedBytes.length
+ " data: " + bytesToHex(encryptedBytes));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
private static String decrypt(String data, String mainKey, int ivLength) throws Exception {
final byte[] encryptedBytes = Base64.getDecoder().decode(data.getBytes(UNICODE_FORMAT));
final byte[] initializationVector = new byte[ivLength];
System.arraycopy(encryptedBytes, 0, initializationVector, 0, ivLength);
SecretKeySpec secretKeySpec = new SecretKeySpec(mainKey.getBytes(UNICODE_FORMAT), ALGO);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, initializationVector);
Cipher cipher = Cipher.getInstance(GCMALGO);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
return new String(cipher.doFinal(encryptedBytes, ivLength, encryptedBytes.length - ivLength),
UNICODE_FORMAT);
}
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
}
In the end I got this result - the "encryptString" needs to get transfered to your website:
https://stackoverflow.com/questions/62129604/decrypt-aes-128-gcm-encoded-content-with-java-cipher-using-php-openssl
data [String] : Secret data for TytooF
data length: 22 data: 536563726574206461746120666f72205479746f6f46
mainKey length: 16 data: 31323334353637383930313233343536
initvector length: 16 data: 3fc914fc67f6ecb53daa098ba40f20a5
encryptedBytes length: 54 data: 3fc914fc67f6ecb53daa098ba40f20a5f1e6755b96e5c0bb5de29522099bdeb806cf28c6d181d7e4ffdc15f451a19a54c714c42b38fc
encryptString: P8kU/Gf27LU9qgmLpA8gpfHmdVuW5cC7XeKVIgmb3rgGzyjG0YHX5P/cFfRRoZpUxxTEKzj8
decryptString: Secret data for TytooF
On webserver/PHP-side I slightly modified your code (the "main change" was the change on the line openssl_decrypt($ciphertext, $cipher, $key, true, $iv, $tag) because we do not present Base64-encoded data to the decrypt
method as this is done some codelines before ($encrypt = base64_decode($str).
Here is the PHP-code:
<?php
/**
* #param string $str
* The URL parameter string
*/
function test_decrypt($str)
{
$key = '1234567890123456';
$cipher = 'aes-128-gcm';
$iv_len = 16;
$tag_length = 16;
echo $str . '<br>';
/**
* Decryption part
*/
$encrypt = base64_decode($str);
$iv = substr($encrypt, 0, $iv_len);
$tag = substr($encrypt, -$tag_length);
$ciphertext = substr($encrypt, $iv_len, -$tag_length);
echo "" . "\n";
$value = unpack('H*', $iv);
echo '<br>iv:' . $value[1];
echo "" . "\n";
$value = unpack('H*', $ciphertext);
echo '<br>ciphertext:' . $value[1];
echo "" . "\n";
$value = unpack('H*', $tag);
echo '<br>tag:' . $value[1];
echo "<br>" . "\n";
$uncrypt = openssl_decrypt($ciphertext, $cipher, $key, true, $iv, $tag);//OPENSSL_RAW_DATA + OPENSSL_NO_PADDING
echo '<br>DecryptedString: ' . $uncrypt . "\n";
$value = unpack('H*', $uncrypt);
echo '<br>DecryptedString [byte[]]:' . $value[1];
exit;
}
echo '<b>Output for https://stackoverflow.com/questions/62129604/decrypt-aes-128-gcm-encoded-content-with-java-cipher-using-php-openssl</b><br>' . "\n";
echo '' . "\n";
echo 'Start decryption' . "\n";
$receivedData = "P8kU/Gf27LU9qgmLpA8gpfHmdVuW5cC7XeKVIgmb3rgGzyjG0YHX5P/cFfRRoZpUxxTEKzj8";
test_decrypt($receivedData);
?>
This is the decryption output on the webserver:
Output for https://stackoverflow.com/questions/62129604/decrypt-aes-128-gcm-encoded-content-with-java-cipher-using-php-openssl
Start decryption P8kU/Gf27LU9qgmLpA8gpfHmdVuW5cC7XeKVIgmb3rgGzyjG0YHX5P/cFfRRoZpUxxTEKzj8
iv:3fc914fc67f6ecb53daa098ba40f20a5
ciphertext:f1e6755b96e5c0bb5de29522099bdeb806cf28c6d181
tag:d7e4ffdc15f451a19a54c714c42b38fc
DecryptedString: Secret data for TytooF
DecryptedString [byte[]]:536563726574206461746120666f72205479746f6f46
Edit Oct. 10th 2021: The code above is using an IV/nonce with a length of 16 bytes but the recommended IV/nonce length is 12.

Related

Java Password based Encryption decrypt throwing errors

I am trying to write a program that will encrypt a file based on a password the user enters when calling the program. The salt of the password should be included in the file. I believe the program is encrypting the file properly but when I go to decrypt the file the program throws
java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected.
I am not sure as to why this would be the case as I am not generating an IV as such. To call the program it is enc "password" fileToEncrypt.txt destinationFile.enc.
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.util.HexFormat;
import java.util.logging.Logger;
public class FileEncryptor {
private static final Logger LOG = Logger.getLogger(FileEncryptor.class.getSimpleName());
private static final String ALGORITHM = "AES";
private static final String CIPHER = "AES/CBC/PkCS5PADDING";
public static void main(String[] args) {
if (args.length == 4) {
if (args[0].equals("enc") || args[0].equals("dec")) {
try {
if (args[0].equals("enc")) {
SecureRandom sr = new SecureRandom();
PBEKeySpec pbeKeySpec;
PBEParameterSpec pbeParamSpec;
SecretKeyFactory keyFac;
byte[] salt = new byte[16];
sr.nextBytes(salt);
int count = 1000;
pbeParamSpec = new PBEParameterSpec(salt, count);
char[] password = args[1].toCharArray();
pbeKeySpec = new PBEKeySpec(password);
keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
if (encrypt(pbeCipher, args[2], args[3],salt)) {
LOG.info("Encryption finished, saved at " + args[3]);
}
} else {
PBEKeySpec pbeKeySpec;
SecretKeyFactory keyFac;
char[] password = args[1].toCharArray();
pbeKeySpec = new PBEKeySpec(password);
keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
if (decrypt(pbeCipher, pbeKey, args[2], args[3])) {
LOG.info("Decryption complete, open " + args[3]);
}
}
} catch (Exception e) {
exceptionHandle(e);
}
} else {
errorHandle("Please call encrypt or decrypt");
}
} else {
errorHandle("Please call program with 4 arguments");
}
}
/**
* Method to encrypt a file with the Key and IV generated by the program and write the results to a
* new file.
*
* #param cipher cipher generated by the program early
* #param input name of the file to be encrypted
* #param output name of the new file after encryption
* #return boolean if encryption was successful or not.
*/
public static boolean encrypt(Cipher cipher, String input, String output,byte[] salt) {
LOG.info("File to be Encrypted: " + input);
try (FileInputStream in = new FileInputStream(input);
FileOutputStream out = new FileOutputStream(output);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher)) {
out.write(salt);
byte[] buffer = new byte[1024];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
} catch (IOException e) {
exceptionHandle(e);
}
return true;
}
/**
* Method to decrypt a file with the Key inputted by the user and IV stored in the file. Then write the results to a
* new file.
*
* #param cipher cipher generated by the program early based on key and IV provided.
* #param input name of the file to be decrypted
* #param output name of the new file after decryption
* #return boolean if decryption was successful or not
*/
public static boolean decrypt(Cipher cipher,SecretKey pbeKey, String input, String output) {
int count = 1000;
try (FileInputStream in = new FileInputStream(input);
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(output)) {
byte[] salt = new byte[16];
in.read(salt);
byte[] buffer = new byte[1024];
cipher.init(Cipher.DECRYPT_MODE, pbeKey,new PBEParameterSpec(salt, count));
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
} catch (IOException | InvalidAlgorithmParameterException | InvalidKeyException ex) {
ex.printStackTrace();
exceptionHandle(ex);
return false;
}
return true;
}
/**
* Methods prints a message to the console to let the user know what mistakes they made when running the program.
*
* #param message to be printed to the console.
*/
public static void errorHandle(String message) {
System.out.println(message);
}
/**
* Program prints a message to the user to let them know what went wrong in the program based on what exception
* was thrown early in the program and give them clear understanding of what went wrong.
*
* #param e Exception caught be the program
*/
public static void exceptionHandle(Exception e) {
if(e.getClass() == BadPaddingException.class){
System.out.println("hello");
LOG.info("Please input correct key. " + e.getMessage());
}
if (e.getClass() == FileNotFoundException.class) {
LOG.info("Please call enc or dec with an existing file. " + e.getMessage());
}
if (e.getClass() == InvalidKeyException.class) {
LOG.info("Please put correct Algorithm. " + e.getMessage());
}
if (e.getClass() == NoSuchAlgorithmException.class) {
LOG.info("Please put correct Cipher " + e.getMessage());
}
if (e.getClass() == InvalidAlgorithmParameterException.class) {
LOG.info("Please put correct IV length. " + e.getMessage());
}
if (e.getClass() == IllegalArgumentException.class) {
LOG.info("Please check length Key and IV. " + e.getMessage());
}
if (e.getClass() == NoSuchPaddingException.class) {
LOG.info("Please check Padding. " + e.getMessage());
}
}
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString();
}
}

Looking for Java implementation for decrypting a message encrypted using openssl -aes-256-cbc -a -salt command?

I am looking for any sample java code that will decrypt the messages encrypted using "openssl enc -aes-256-cbc) -a -salt" command provided the key is known.
https://pastebin.com/YiwbCAW8
So far i was able to get the following java code that encrypts and also decrypts the message. But i am not able to decrypt the encrypted message using openssl command. Getting "Bad Magic Number" error. Any idea ?
Encrypt the message using the code >
Encrypt("sample text", "test$password") = "i+5zkPPgnDdV7fr/w8uHkw=="
Decrypt("i+5zkPPgnDdV7fr/w8uHkw==", "test$password") = "sample text"
Decrypt the message using openssl >
F:\cipher>echo i+5zkPPgnDdV7fr/w8uHkw== | openssl aes-256-cbc -a -salt -d
enter aes-256-cbc decryption password:
bad magic number
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.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
private static final byte[] SALT = {
(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
(byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
};
private static final int ITERATION_COUNT = 65536;
private static final int KEY_LENGTH = 256;
private Cipher ecipher;
private Cipher dcipher;
AES(String passPhrase) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, secret);
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
}
public String encrypt(String encrypt) throws Exception {
byte[] bytes = encrypt.getBytes("UTF8");
byte[] encrypted = encrypt(bytes);
return Base64.getEncoder().encodeToString(encrypted);
}
public byte[] encrypt(byte[] plain) throws Exception {
return ecipher.doFinal(plain);
}
public String decrypt(String encrypt) throws Exception {
byte[] bytes = Base64.getDecoder().decode(encrypt);
byte[] decrypted = decrypt(bytes);
return new String(decrypted, "UTF8");
}
public byte[] decrypt(byte[] encrypt) throws Exception {
return dcipher.doFinal(encrypt);
}
public static void main(String[] args) throws Exception {
String message = "sample text";
String password = "test$password";
AES encrypter = new AES(password);
String encrypted = encrypter.encrypt(message);
String decrypted = encrypter.decrypt(encrypted);
System.out.println("Encrypt(\"" + message + "\", \"" + password + "\") = \"" + encrypted + "\"");
System.out.println("Decrypt(\"" + encrypted + "\", \"" + password + "\") = \"" + decrypted + "\"");
}
}
You may search stackoverflow for many similar questions.
you have multiple issues in your code:
You use different keys:
In Java you use PBKDF2 to generate an encryption key from the provided password. Openssl uses its EVP_BytesToKey. Search internet for Java implementation. Please note the hash used in the EVP_BytesToKey changed with some openssl version (from MD5 to SHA-1 SHA-256), if someone is having more details, please comment
And you use random IV. you don't pass the IV along the ciphertext, so you may be able to decrypt the ciphertext with the same cipher instance (kkeping the same iv), but lets try your Java code to decrypt your ciphertext other time or with other instance, it won't work. You need to pass IV along the ciphertext (usually it's prepended)
Openssl expect following format:
Salted_<8 byte salt>ciphertext
Salted__<8 byte salt>ciphertext
8 byte salt is a random byte array used to generate the encryption key and IV from the provided password. Try encrypt with openssl with -pparameter, it will print the salt, IV and Key generated so you can check and compare
Using CBC without any integrity check (hmac, ..) may be unsafe in many implementations
Suggestions:
you can find an openssl java library implementing the same required (EVP_BytesToKey)
you can implement EVP_BytesToKey yourself
you can use openssl directly with -K/-ivparameters providing the encryption key and IV (in hex format) instead of password, then openssl expects pure ciphertext (no Salted_ or salt inside the input)
Thanks a lot for the clues. As mentioned, did some search and modified the code from one of the post. I have seen similar code with EVP_BytesToKeys in many places, but took some time to figure out the usage. I am able to decrypt the msg encrypted by openssl.
Trying to search the code for encryption as well. Meanwhile any help of encryption is appreciated as well.
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class AES5 {
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 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 byte[][] getKeyIV(byte[] headerSaltAndCipherText, Cipher aesCBC, String password) {
byte[] salt = Arrays.copyOfRange(headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
byte[][] keyAndIV=null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
keyAndIV = EVP_BytesToKey(KEY_SIZE_BITS / Byte.SIZE, aesCBC.getBlockSize(), md5, salt,
password.getBytes(ASCII), ITERATIONS);
} catch (Exception e) {e.printStackTrace();}
return keyAndIV;
}
// https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes
public static String decrypt(String encryptedMsg, String password) {
String decryptedMsg =null;
byte[] headerSaltAndCipherText = Base64.decodeBase64(encryptedMsg);
byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
try {
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
final byte[][] keyAndIV = getKeyIV(headerSaltAndCipherText, aesCBC, password);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
decryptedMsg = new String(decrypted, ASCII);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
//TODO - Encrypt the msg in same manner as "openssl enc -aes-256-cbc -a -salt"
public static String encrypt(String msg, String password) {
String decryptedMsg =null;
byte[] headerSaltAndCipherText = Base64.decodeBase64(msg);
byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
try {
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
final byte[][] keyAndIV = getKeyIV(headerSaltAndCipherText, aesCBC, password);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
aesCBC.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
decryptedMsg = new String(decrypted, ASCII);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
public static void main(String[] args) {
String msg = "the decrypted message is this";
String password = "pass";
System.out.println(encrypt(msg, password));
String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
System.out.println(decrypt(encryptedMsg, password));
}
}
Also got an improved solution from the following site. Got the code for both encryption and decryption for now...
http://qaru.site/questions/19874/java-equivalent-of-an-openssl-aes-cbc-encryption
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
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;
import org.apache.commons.codec.binary.Base64;
import static java.nio.charset.StandardCharsets.*;
/**
* Mimics the OpenSSL AES Cipher options for encrypting and decrypting messages using a
* shared key (aka password) with symetric ciphers.
*/
public class OpenSslAesQu {
/** OpenSSL magic initial bytes. */
private static final String SALTED_STR = "Salted__";
private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);
public static String encryptAndURLEncode(String password, String clearText) {
String encrypted = null;
try {
encrypted = URLEncoder.encode(encrypt(password, clearText),UTF_8.name());
} catch (Exception e) {e.printStackTrace();}
return encrypted;
}
/**
*
* #param password The password / key to encrypt with.
* #param data The data to encrypt
* #return A base64 encoded string containing the encrypted data.
*/
public static String encrypt(String password, String clearText) {
String encryptedMsg = null;
final byte[] pass = password.getBytes(US_ASCII);
final byte[] salt = (new SecureRandom()).generateSeed(8);
final byte[] inBytes = clearText.getBytes(UTF_8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
try {
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] data = cipher.doFinal(inBytes);
data = array_concat(array_concat(SALTED_MAGIC, salt), data);
//return Base64.getEncoder().encodeToString( data );
encryptedMsg = org.apache.commons.codec.binary.Base64.encodeBase64String(data);
} catch(Exception e) {e.printStackTrace();}
return encryptedMsg;
}
/**
* #see http://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption for what looks like a useful answer. The not-yet-commons-ssl also has an implementation
* #param password
* #param source The encrypted data
*/
public static String decrypt(String password, String source) {
String decryptedMsg = null;
final byte[] pass = password.getBytes(US_ASCII);
//final byte[] inBytes = Base64.getDecoder().decode(source);
final byte[] inBytes = Base64.decodeBase64(source);
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
try {
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
decryptedMsg = new String(clear, UTF_8);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
private static byte[] array_concat(final byte[] a, final byte[] b) {
final byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String msg = "the decrypted message is this";
String password = "pass";
System.out.println(">> "+encrypt(password,msg));
//System.out.println("<< "+decrypt(encrypt(msg, password), password));
String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
String encryptedMsg2 = "U2FsdGVkX1/B6oOznz5+nd7W/qXwXI7G7rhj5o9pjx8MS0TXp9SNxO3AhM9HBJ/z";
System.out.println(decrypt(password,encryptedMsg));
System.out.println(decrypt(password,encryptedMsg2));
System.out.println(decrypt(password,encrypt(password,msg)));
}
}

Encrypting A String With Crypto++ (C++) and Decrypting With AESCrypt (Android/java)

I have been working on this for way too long. At this rate I wont have any hair left. I have a situation where I am trying to encrypt with c++ and decrypt with java which I thought would be easy with a standard like AES, or so I thought. Here is the code
The encrypted c++ does not decrypt correctly in the java code.
C++ using crypto++
byte key[ CryptoPP::AES::MAX_KEYLENGTH ], iv[ CryptoPP::AES::BLOCKSIZE ];
memset( key, 0x00, CryptoPP::AES::MAX_KEYLENGTH );
memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE );
string plaintext = "this is a test string";
string ciphertext;
string decryptedtext;
CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::MAX_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( aesEncryption, iv );
CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink( ciphertext ) );
stfEncryptor.Put( reinterpret_cast<const unsigned char*>( plaintext.c_str() ), plaintext.length() + 1 );
stfEncryptor.MessageEnd();
std::cout << "Cipher Text (" << ciphertext() << " bytes)" << std::endl;
My android/java code -
MainActivity.java
String sEncryptedMsg = "paste encrypted string here from crypto++";
try {
String messageAfterDecrypt = AESCrypt.decrypt(password, sEncryptedMsg);
Log.i("decrypted --> ", messageAfterDecrypt);
}catch (GeneralSecurityException e){
}
Android/java code - AESCrypt.java
import android.util.Base64;
import android.util.Log;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public final class AESCrypt
{
private static final String TAG = "AESCrypt";
//AESCrypt-ObjC uses CBC and PKCS7Padding
private static final String AES_MODE = "AES/CBC/PKCS7Padding";
private static final String CHARSET = "UTF-8";
//AESCrypt-ObjC uses SHA-256 (and so a 256-bit key)
private static final String HASH_ALGORITHM = "SHA-256";
//AESCrypt-ObjC uses blank IV (not the best security, but the aim here is compatibility)
private static final byte[] ivBytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
//togglable log option (please turn off in live!)
public static boolean DEBUG_LOG_ENABLED = false;
/**
* Generates SHA256 hash of the password which is used as key
*
* #param password used to generated key
* #return SHA256 of the password
*/
private static SecretKeySpec generateKey(final String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
final MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
byte[] bytes = password.getBytes("UTF-8");
digest.update(bytes, 0, bytes.length);
byte[] key = digest.digest();
log("SHA-256 key ", key);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
return secretKeySpec;
}
/**
* Encrypt and encode message using 256-bit AES with key generated from password.
*
*
* #param password used to generated key
* #param message the thing you want to encrypt assumed String UTF-8
* #return Base64 encoded CipherText
* #throws GeneralSecurityException if problems occur during encryption
*/
/**
* Decrypt and decode ciphertext using 256-bit AES with key generated from password
*
* #param password used to generated key
* #param base64EncodedCipherText the encrpyted message encoded with base64
* #return message in Plain text (String UTF-8)
* #throws GeneralSecurityException if there's an issue decrypting
*/
public static String decrypt(final String password, String base64EncodedCipherText)
throws GeneralSecurityException {
try {
final SecretKeySpec key = generateKey(password);
log("base64EncodedCipherText", base64EncodedCipherText);
byte[] decodedCipherText = Base64.decode(base64EncodedCipherText, Base64.NO_WRAP);
log("decodedCipherText", decodedCipherText);
byte[] decryptedBytes = decrypt(key, ivBytes, decodedCipherText);
log("decryptedBytes", decryptedBytes);
String message = new String(decryptedBytes, CHARSET);
log("message", message);
return message;
} catch (UnsupportedEncodingException e) {
if (DEBUG_LOG_ENABLED)
Log.e(TAG, "UnsupportedEncodingException ", e);
throw new GeneralSecurityException(e);
}
}
/**
* More flexible AES decrypt that doesn't encode
*
* #param key AES key typically 128, 192 or 256 bit
* #param iv Initiation Vector
* #param decodedCipherText in bytes (assumed it's already been decoded)
* #return Decrypted message cipher text (not encoded)
* #throws GeneralSecurityException if something goes wrong during encryption
*/
public static byte[] decrypt(final SecretKeySpec key, final byte[] iv, final byte[] decodedCipherText)
throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance(AES_MODE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decryptedBytes = cipher.doFinal(decodedCipherText);
log("decryptedBytes", decryptedBytes);
return decryptedBytes;
}
private static void log(String what, byte[] bytes) {
if (DEBUG_LOG_ENABLED)
Log.d(TAG, what + "[" + bytes.length + "] [" + bytesToHex(bytes) + "]");
}
private static void log(String what, String value) {
if (DEBUG_LOG_ENABLED)
Log.d(TAG, what + "[" + value.length() + "] [" + value + "]");
}
/**
* Converts byte array to hexidecimal useful for logging and fault finding
* #param bytes
* #return
*/
private static String bytesToHex(byte[] bytes) {
final char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F'};
char[] hexChars = new char[bytes.length * 2];
int v;
for (int j = 0; j < bytes.length; j++) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private AESCrypt() {
}
Thanks in advance with any help you can provide!

PSKC file decryption using java

I am new to encryption and decryption. I was given a PSKC file and asked for decryption. I was given the password for decryption. The PSKC file doenot have initialization vector value.
I wrote the code trying to decrypt it. But i am unsuccessful in achieving the outcome.
below is the PSKC file example
<?xml version="1.0"?>
<pskc:KeyContainer xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc">
<pskc:EncryptionKey>
<xenc11:DerivedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#">
<xenc11:KeyDerivationMethod Algorithm="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2">
<pkcs5:PBKDF2-params xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#">
<Salt>
<Specified>EW0h0yUcDX72WU9UiKiCwDpXsJg=</Specified>
</Salt>
<IterationCount>128</IterationCount>
<KeyLength>16</KeyLength>
<PRF />
</pkcs5:PBKDF2-params>
</xenc11:KeyDerivationMethod>
<xenc:ReferenceList>
<xenc:DataReference URI="#ED" />
</xenc:ReferenceList>
<xenc11:MasterKeyName>Passphrase1</xenc11:MasterKeyName>
</xenc11:DerivedKey>
</pskc:EncryptionKey>
<pskc:MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1">
<pskc:MACKey>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH6Mlg</xenc:CipherValue>
</xenc:CipherData>
</pskc:MACKey>
</pskc:MACMethod>
<pskc:KeyPackage>
<pskc:DeviceInfo>
<pskc:Manufacturer>Gemalto</pskc:Manufacturer>
<pskc:SerialNo>GAKT000047A5</pskc:SerialNo>
</pskc:DeviceInfo>
<pskc:CryptoModuleInfo>
<pskc:Id>CM_ID_007</pskc:Id>
</pskc:CryptoModuleInfo>
<pskc:Key Id="GAKT000047A5" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:totp">
<pskc:Issuer>Issuer0</pskc:Issuer>
<pskc:AlgorithmParameters>
<pskc:ResponseFormat Encoding="DECIMAL" Length="6" />
</pskc:AlgorithmParameters>
<pskc:Data>
<pskc:Secret>
<pskc:EncryptedValue>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE</xenc:CipherValue>
</xenc:CipherData>
</pskc:EncryptedValue>
<pskc:ValueMAC>lbu+9OcLArnj6mS7KYOKDa4zRU0=</pskc:ValueMAC>
</pskc:Secret>
<pskc:Time>
<pskc:PlainValue>0</pskc:PlainValue>
</pskc:Time>
<pskc:TimeInterval>
<pskc:PlainValue>30</pskc:PlainValue>
</pskc:TimeInterval>
</pskc:Data>
</pskc:Key>
</pskc:KeyPackage>
</pskc:KeyContainer>
below is the java code which i have written for decryption.
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
public class test {
/**
* #param args
*/
public static void main(String[] args) {
test te = new test();
try {
te.decryptSeedValue();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
e.printStackTrace();
}
// TODO Auto-generated method stub
}
public static HashMap decryptSeedValue()throws Exception{
String password = "G?20R+I+3-/UcWIN";
String pbesalt ="EW0h0yUcDX72WU9UiKiCwDpXsJg=";
String iv = "aaaaaaaaaaaaaaaaaaaaaaaa";
int iteration = 128;
String value = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE";
String valueDigest = "lbu+9OcLArnj6mS7KYOKDa4zRU0=";
byte[] cipherText =null;
//some parameters need to decode from Base64 to byte[]
byte[] data = base64Decode(value.getBytes());
//System.out.println("data(hex string) = " + HexBin.encode(data));//debug
byte[] salt = base64Decode(pbesalt.getBytes());
//System.out.println("salt(hex string) = " + HexBin.encode(salt));//debug
byte[] initVec = base64Decode(iv.getBytes());
//System.out.println("iv(hex string) = " + HexBin.encode(initVec));//debug
//perform PBE key generation and AES/CBC/PKCS5Padding decrpyption
HashMap hs = myFunction(data, password, initVec, salt, iteration);
String seedValue = (String)hs.get("DECRYPTED_SEED_VALUE");
byte[] temp = (byte[])hs.get("HASH_OUTPUT");
//System.out.println("hashed output(hex string) = " + HexBin.encode(temp));//debug
//perform Base64 Encode
byte[] out = base64Encode(temp);
String output = new String((out));
System.out.println("output = "+output);
System.out.println("valueD = "+valueDigest);
//System.out.println("hashed output(base64) = " + output);
//compare the result
if(output.equals(valueDigest)){
System.out.println("Hash verification successful for:-->" );
System.out.println("\n");
//hs.put("SEED_VALUE", HexBin.encode(temp));
hs.put("SEED_VALUE", seedValue);
return hs;
}
else{
System.out.println("Hash verification failed for :-->");
return null;
}
}
public static HashMap myFunction(byte[] data, String password, byte[] initVec,
byte[] salt, int iteration) throws Exception{
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
byte[] pBytes = password.getBytes();
generator.init(pBytes, salt, iteration);
int keysize = 128;//fixed at AES key of 16 bytes
int ivsize = initVec.length;
ParametersWithIV params = (ParametersWithIV) generator.generateDerivedParameters(keysize, ivsize);
KeyParameter keyParam = (KeyParameter) params.getParameters();
//System.out.println("derived key = " + HexBin.encode(keyParam.getKey()));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec paramSpec = new IvParameterSpec(initVec);
SecretKeySpec key = new SecretKeySpec(keyParam.getKey(), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
//perform decryption
byte[] secret = cipher.doFinal(data);
//display the 20 bytes secret of the token
//System.out.println("token secret(hex string) = " + HexBin.encode(secret));
//perform HMAC-SHA-1
byte[] output = hmac_sha1(secret, keyParam.getKey());
HashMap hs = new HashMap();
hs.put("ENCRYPTION_KEY", HexBin.encode(keyParam.getKey()));
hs.put("HASH_OUTPUT", output);
hs.put("DECRYPTED_SEED_VALUE", HexBin.encode(secret));
return hs;
}
public static byte[] base64Encode(byte[] passwordBytes) throws NoSuchAlgorithmException {
Base64 base64 = new Base64();
byte[] hashBytes2 = base64.encode(passwordBytes);
return hashBytes2;
}
public static byte[] base64Decode(byte[] passwordBytes) throws NoSuchAlgorithmException {
Base64 base64 = new Base64();
byte[] hashBytes2 = base64.decode(passwordBytes);
return hashBytes2;
}
public static byte[] hmac_sha1(byte[] dataByte, byte[] keyByte) throws Exception{
Mac hmacSha1;
hmacSha1 = Mac.getInstance("HmacSHA1");
SecretKeySpec macKey = new SecretKeySpec(keyByte, "HmacSHA1");
hmacSha1.init(macKey);
byte[] result = hmacSha1.doFinal(dataByte);
return result;
}
/**
* Convert a byte array of 8 bit characters into a String.
*
* #param bytes the array containing the characters
* #param length the number of bytes to process
* #return a String representation of bytes
*/
private static String toString(
byte[] bytes,
int length)
{
char[] chars = new char[length];
for (int i = 0; i != chars.length; i++)
{
chars[i] = (char)(bytes[i] & 0xff);
}
return new String(chars);
}
}
it doesn't throw any exception, but it prints "Hash verification failed for" which is defined in my code when decryption fails.
Can some one please help me out.
As per the pskc standard http://www.rfc-editor.org/rfc/rfc6030.txt the IV is prepended to the ciphervalue. This is aes128, so it'll be the first 16 bytes once it's been base64 decoded.
Adding onto what bcharlton is describing; what you are not doing is check the hmac_sha1 for the encrypted data (which has the iv prepended in encrypted form), using the MACKey described in the xml document.
With AES-128 CBC the initialization vector is explicitly defined, and since there is no verification built into it, it uses HMAC for it.
So given your example the following will work:
public static HashMap decryptSeedValue() throws Exception
{
String password = "G?20R+I+3-/UcWIN";
String pbesalt = "EW0h0yUcDX72WU9UiKiCwDpXsJg=";
String iv = "aaaaaaaaaaaaaaaaaaaaaaaa";
int iteration = 128;
String value = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE";
String valueDigest = "lbu+9OcLArnj6mS7KYOKDa4zRU0=";
//YOU NEED THIS GUY BELOW TO VERIFY
String macKey = "jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH6Mlg";
byte[] cipherText = null;
//some parameters need to decode from Base64 to byte[]
byte[] data = base64Decode(value.getBytes());
//System.out.println("data(hex string) = " + HexBin.encode(data));//debug
byte[] salt = base64Decode(pbesalt.getBytes());
//System.out.println("salt(hex string) = " + HexBin.encode(salt));//debug
byte[] initVec = base64Decode(iv.getBytes());
//System.out.println("iv(hex string) = " + HexBin.encode(initVec));//debug
//perform PBE key generation and AES/CBC/PKCS5Padding decrpyption
HashMap hs = myFunction(data, password, base64Decode(macKey.getBytes()), salt, iteration);
String seedValue = (String) hs.get("DECRYPTED_SEED_VALUE");
byte[] temp = (byte[]) hs.get("HASH_OUTPUT");
//System.out.println("hashed output(hex string) = " + HexBin.encode(temp));//debug
//perform Base64 Encode
byte[] out = base64Encode(temp);
String output = new String((out));
System.out.println("output = " + output);
System.out.println("valueD = " + valueDigest);
//System.out.println("hashed output(base64) = " + output);
//compare the result
if (output.equals(valueDigest)) {
System.out.println("Hash verification successful for:-->");
System.out.println("\n");
//hs.put("SEED_VALUE", HexBin.encode(temp));
hs.put("SEED_VALUE", seedValue);
return hs;
} else {
System.out.println("Hash verification failed for :-->");
return null;
}
}
public static HashMap myFunction(byte[] data, String password, byte[] macData,
byte[] salt, int iteration) throws Exception
{
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
byte[] pBytes = password.getBytes();
generator.init(pBytes, salt, iteration);
byte[] iv = new byte[16];
int ivsize = iv.length;
byte[] encryptedData = new byte[data.length - ivsize];
System.arraycopy(data, 0, iv, 0, iv.length);
System.arraycopy(data, ivsize, encryptedData, 0, encryptedData.length);
byte[] maciv = new byte[16];
byte[] encryptedMac = new byte[macData.length - maciv.length];
System.arraycopy(macData, 0, maciv, 0, maciv.length);
System.arraycopy(macData, maciv.length, encryptedMac, 0, encryptedMac.length);
int keysize = 128;//fixed at AES key of 16 bytes
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iteration, keysize);
SecretKey tmp = factory.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decryptedData = dcipher.doFinal(encryptedData);
// decryptedData is your token value!
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(maciv));
byte[] decryptedMac = dcipher.doFinal(encryptedMac);
//display the 20 bytes secret of the token
//System.out.println("token secret(hex string) = " + HexBin.encode(secret));
//perform HMAC-SHA-1
//Use the decrypted MAC key here for hashing!
byte[] output = hmac_sha1(data, decryptedMac);
HashMap hs = new HashMap();
hs.put("ENCRYPTION_KEY", password);
hs.put("HASH_OUTPUT", output);
hs.put("DECRYPTED_SEED_VALUE", HexBin.encode(decryptedData));
return hs;
}
Keep in mind that as https://www.rfc-editor.org/rfc/rfc6030#section-6.2 describes, a different iv can be used for the MAC and the token key.

rsa with digital signature coding in java

I was running a code for rsa with digital signature on netbeans but got the following error :
Exception caught
java.io.FileNotFoundException: \org\owasp\crypto\testkeystore.ks (The system cannot find the path specified)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.(FileInputStream.java:120)
at java.io.FileInputStream.(FileInputStream.java:79)
at org.owasp.crypto.PublicKeyCryptography.main(PublicKeyCryptography.java:52)
BUILD SUCCESSFUL (total time: 0 seconds)
package org.owasp.crypto;
import java.security.*;
import java.security.cert.*;
import javax.crypto.*;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
public class PublicKeyCryptography {
public static void main(String[] args) {
SymmetricEncrypt encryptUtil = new SymmetricEncrypt();
String strDataToEncrypt = "Hello World";
byte[] byteDataToTransmit = strDataToEncrypt.getBytes();
// Generating a SecretKey for Symmetric Encryption
SecretKey senderSecretKey = SymmetricEncrypt.getSecret();
//1. Encrypt the data using a Symmetric Key
byte[] byteCipherText = encryptUtil.encryptData(byteDataToTransmit,senderSecretKey,"AES");
String strCipherText = new BASE64Encoder().encode(byteCipherText);
//2. Encrypt the Symmetric key using the Receivers public key
try{
// 2.1 Specify the Keystore where the Receivers certificate has been imported
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
char [] password = "testpwd".toCharArray();
java.io.FileInputStream fis = new java.io.FileInputStream("/org/owasp/crypto/testkeystore.ks");
ks.load(fis, password);
fis.close();
// 2.2 Creating an X509 Certificate of the Receiver
X509Certificate recvcert ;
MessageDigest md = MessageDigest.getInstance("MD5");
recvcert = (X509Certificate)ks.getCertificate("testrecv");
// 2.3 Getting the Receivers public Key from the Certificate
PublicKey pubKeyReceiver = recvcert.getPublicKey();
// 2.4 Encrypting the SecretKey with the Receivers public Key
byte[] byteEncryptWithPublicKey = encryptUtil.encryptData(senderSecretKey.getEncoded(),pubKeyReceiver,"RSA/ECB/PKCS1Padding");
String strSenbyteEncryptWithPublicKey = new BASE64Encoder().encode(byteEncryptWithPublicKey);
// 3. Create a Message Digest of the Data to be transmitted
md.update(byteDataToTransmit);
byte byteMDofDataToTransmit[] = md.digest();
String strMDofDataToTransmit = new String();
for (int i = 0; i < byteMDofDataToTransmit.length; i++){
strMDofDataToTransmit = strMDofDataToTransmit + Integer.toHexString((int)byteMDofDataToTransmit[i] & 0xFF) ;
}
// 3.1 Message to be Signed = Encrypted Secret Key + MAC of the data to be transmitted
String strMsgToSign = strSenbyteEncryptWithPublicKey + "|" + strMDofDataToTransmit;
// 4. Sign the message
// 4.1 Get the private key of the Sender from the keystore by providing the password set for the private key while creating the keys using keytool
char[] keypassword = "send123".toCharArray();
Key myKey = ks.getKey("testsender", keypassword);
PrivateKey myPrivateKey = (PrivateKey)myKey;
// 4.2 Sign the message
Signature mySign = Signature.getInstance("MD5withRSA");
mySign.initSign(myPrivateKey);
mySign.update(strMsgToSign.getBytes());
byte[] byteSignedData = mySign.sign();
// 5. The Values byteSignedData (the signature) and strMsgToSign (the data which was signed) can be sent across to the receiver
// 6.Validate the Signature
// 6.1 Extracting the Senders public Key from his certificate
X509Certificate sendercert ;
sendercert = (X509Certificate)ks.getCertificate("testsender");
PublicKey pubKeySender = sendercert.getPublicKey();
// 6.2 Verifying the Signature
Signature myVerifySign = Signature.getInstance("MD5withRSA");
myVerifySign.initVerify(pubKeySender);
myVerifySign.update(strMsgToSign.getBytes());
boolean verifySign = myVerifySign.verify(byteSignedData);
if (verifySign == false)
{
System.out.println(" Error in validating Signature ");
}
else
System.out.println(" Successfully validated Signature ");
// 7. Decrypt the message using Recv private Key to get the Symmetric Key
char[] recvpassword = "recv123".toCharArray();
Key recvKey = ks.getKey("testrecv", recvpassword);
PrivateKey recvPrivateKey = (PrivateKey)recvKey;
// Parsing the MessageDigest and the encrypted value
String strRecvSignedData = new String (byteSignedData);
String[] strRecvSignedDataArray = new String [10];
strRecvSignedDataArray = strMsgToSign.split("|");
int intindexofsep = strMsgToSign.indexOf("|");
String strEncryptWithPublicKey = strMsgToSign.substring(0,intindexofsep);
String strHashOfData = strMsgToSign.substring(intindexofsep+1);
// Decrypting to get the symmetric key
byte[] bytestrEncryptWithPublicKey = new BASE64Decoder().decodeBuffer(strEncryptWithPublicKey);
byte[] byteDecryptWithPrivateKey = encryptUtil.decryptData(byteEncryptWithPublicKey,recvPrivateKey,"RSA/ECB/PKCS1Padding");
// 8. Decrypt the data using the Symmetric Key
javax.crypto.spec.SecretKeySpec secretKeySpecDecrypted = new javax.crypto.spec.SecretKeySpec(byteDecryptWithPrivateKey,"AES");
byte[] byteDecryptText = encryptUtil.decryptData(byteCipherText,secretKeySpecDecrypted,"AES");
String strDecryptedText = new String(byteDecryptText);
System.out.println(" Decrypted data is " +strDecryptedText);
// 9. Compute MessageDigest of data + Signed message
MessageDigest recvmd = MessageDigest.getInstance("MD5");
recvmd.update(byteDecryptText);
byte byteHashOfRecvSignedData[] = recvmd.digest();
String strHashOfRecvSignedData = new String();
for (int i = 0; i < byteHashOfRecvSignedData.length; i++){
strHashOfRecvSignedData = strHashOfRecvSignedData + Integer.toHexString((int)byteHashOfRecvSignedData[i] & 0xFF) ;
}
// 10. Validate if the Message Digest of the Decrypted Text matches the Message Digest of the Original Message
if (!strHashOfRecvSignedData.equals(strHashOfData))
{
System.out.println(" Message has been tampered ");
}
}
catch(Exception exp)
{
System.out.println(" Exception caught " + exp);
exp.printStackTrace();
}
}
}
package org.owasp.crypto;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import sun.misc.BASE64Encoder;
/**
* #author Joe Prasanna Kumar
* This program provides the following cryptographic functionalities
* 1. Encryption using AES
* 2. Decryption using AES
* High Level Algorithm :
* 1. Generate a DES key (specify the Key size during this phase)
* 2. Create the Cipher
* 3. To Encrypt : Initialize the Cipher for Encryption
* 4. To Decrypt : Initialize the Cipher for Decryption
*/
public class SymmetricEncrypt {
String strDataToEncrypt = new String();
String strCipherText = new String();
String strDecryptedText = new String();
static KeyGenerator keyGen;
private static String strHexVal = "0123456789abcdef";
public static SecretKey getSecret(){
/**
* Step 1. Generate an AES key using KeyGenerator
* Initialize the keysize to 128
*
*/
try{
keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
}
catch(Exception exp)
{
System.out.println(" Exception inside constructor " +exp);
}
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
/**
* Step2. Create a Cipher by specifying the following parameters
* a. Algorithm name - here it is AES
*/
public byte[] encryptData(byte[] byteDataToEncrypt, Key secretKey, String Algorithm) {
byte[] byteCipherText = new byte[200];
try {
Cipher aesCipher = Cipher.getInstance(Algorithm);
/**
* Step 3. Initialize the Cipher for Encryption
*/
if(Algorithm.equals("AES")){
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey,aesCipher.getParameters());
}
else if(Algorithm.equals("RSA/ECB/PKCS1Padding")){
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey);
}
/**
* Step 4. Encrypt the Data
* 1. Declare / Initialize the Data. Here the data is of type String
* 2. Convert the Input Text to Bytes
* 3. Encrypt the bytes using doFinal method
*/
byteCipherText = aesCipher.doFinal(byteDataToEncrypt);
strCipherText = new BASE64Encoder().encode(byteCipherText);
}
catch (NoSuchAlgorithmException noSuchAlgo)
{
System.out.println(" No Such Algorithm exists " + noSuchAlgo);
}
catch (NoSuchPaddingException noSuchPad)
{
System.out.println(" No Such Padding exists " + noSuchPad);
}
catch (InvalidKeyException invalidKey)
{
System.out.println(" Invalid Key " + invalidKey);
}
catch (BadPaddingException badPadding)
{
System.out.println(" Bad Padding " + badPadding);
}
catch (IllegalBlockSizeException illegalBlockSize)
{
System.out.println(" Illegal Block Size " + illegalBlockSize);
illegalBlockSize.printStackTrace();
}
catch (Exception exp)
{
exp.printStackTrace();
}
return byteCipherText;
}
/**
* Step 5. Decrypt the Data
* 1. Initialize the Cipher for Decryption
* 2. Decrypt the cipher bytes using doFinal method
*/
public byte[] decryptData(byte[] byteCipherText, Key secretKey, String Algorithm) {
byte[] byteDecryptedText = new byte[200];
try{
Cipher aesCipher = Cipher.getInstance(Algorithm);
if(Algorithm.equals("AES")){
aesCipher.init(Cipher.DECRYPT_MODE,secretKey,aesCipher.getParameters());
}
else if(Algorithm.equals("RSA/ECB/PKCS1Padding")){
aesCipher.init(Cipher.DECRYPT_MODE,secretKey);
}
byteDecryptedText = aesCipher.doFinal(byteCipherText);
strDecryptedText = new String(byteDecryptedText);
}
catch (NoSuchAlgorithmException noSuchAlgo)
{
System.out.println(" No Such Algorithm exists " + noSuchAlgo);
}
catch (NoSuchPaddingException noSuchPad)
{
System.out.println(" No Such Padding exists " + noSuchPad);
}
catch (InvalidKeyException invalidKey)
{
System.out.println(" Invalid Key " + invalidKey);
invalidKey.printStackTrace();
}
catch (BadPaddingException badPadding)
{
System.out.println(" Bad Padding " + badPadding);
badPadding.printStackTrace();
}
catch (IllegalBlockSizeException illegalBlockSize)
{
System.out.println(" Illegal Block Size " + illegalBlockSize);
illegalBlockSize.printStackTrace();
}
catch (InvalidAlgorithmParameterException invalidParam)
{
System.out.println(" Invalid Parameter " + invalidParam);
}
return byteDecryptedText;
}
public static byte[] convertStringToByteArray(String strInput) {
strInput = strInput.toLowerCase();
byte[] byteConverted = new byte[(strInput.length() + 1) / 2];
int j = 0;
int interimVal;
int nibble = -1;
for (int i = 0; i < strInput.length(); ++i) {
interimVal = strHexVal.indexOf(strInput.charAt(i));
if (interimVal >= 0) {
if (nibble < 0) {
nibble = interimVal;
} else {
byteConverted[j++] = (byte) ((nibble << 4) + interimVal);
nibble = -1;
}
}
}
if (nibble >= 0) {
byteConverted[j++] = (byte) (nibble << 4);
}
if (j < byteConverted.length) {
byte[] byteTemp = new byte[j];
System.arraycopy(byteConverted, 0, byteTemp, 0, j);
byteConverted = byteTemp;
}
return byteConverted;
}
public static String convertByteArrayToString(byte[] block) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < block.length; ++i) {
buf.append(strHexVal.charAt((block[i] >>> 4) & 0xf));
buf.append(strHexVal.charAt(block[i] & 0xf));
}
return buf.toString();
}
}
Your exception is caused because you've passed a non-existent file path to the FileInputStream constructor:
... = FileInputStream("/org/owasp/crypto/testkeystore.ks")
Make sure the path you provide is a valid relative or absolute path.

Categories

Resources