I have a 3des encrypted hex string which I get from a server. When I decrypt it with Java ECB/NoPadding, I get the expected value, but when I try to decrypt it with PHP I am getting a different value. This is the PHP code:
<?php
$key = "428982f4658cfeb679bae2bf85ebcde9";
$input = "9CB39217A434F6339EF27C503B561342";
$encrypted_data = bin2hex(
mcrypt_ecb(MCRYPT_3DES, pack("H*", $key), pack("H*", $input), MCRYPT_DECRYPT)
);
?>
and part of the java code starting with main:
String input = "F414E86894D996F8658F9327C8EC786404192012174729";
String key = "05aa58f38a0883d1439750a2495d36ad";
byte2hex(doCryto("F414E86894D996F8658F9327C8EC7864", "05aa58f38a0883d1439750a2495d36ad", Cipher.DECRYPT_MODE));
public static byte[] doCryto(String data, String key, int CipherMode) throws Exception {
byte result[];
try {
byte data1[] = hex2byte(data);
Key key1 = formDESKey(key);
transformation = key1.getAlgorithm() + "/ECB/NoPadding";
Cipher c1 = Cipher.getInstance(transformation);
c1.init(CipherMode, key1);
result = c1.doFinal(data1);
} catch (Exception e) {
throw new Exception(e);
}
return result;
}
public static Key formDESKey(String key) throws Exception {
short len = (short) ((key.length() / 2) * 8);
return formDESKey(len, hex2byte(key));
}
public static Key formDESKey(short keyLength, byte clearKeyBytes[]) throws Exception {
Key key = null;
switch (keyLength) {
case 64: // '#'
key = new SecretKeySpec(clearKeyBytes, "DES");
break;
case 128:
clearKeyBytes = concat(clearKeyBytes, 0, 128/8, clearKeyBytes, 0, getBytesLength((short) 64));
// fall through
case 192:
key = new SecretKeySpec(clearKeyBytes, "DESede");
break;
}
if (key == null) {
throw new Exception("Unsupported DES key length: " + keyLength + " bits");
} else {
return key;
}
}
private static byte[] concat(byte array1[], int beginIndex1, int length1, byte array2[], int beginIndex2, int length2) {
byte concatArray[] = new byte[length1 + length2];
System.arraycopy(array1, beginIndex1, concatArray, 0, length1);
System.arraycopy(array2, beginIndex2, concatArray, length1, length2);
return concatArray;
}
Java results in: 31303031464646464646464646464646
While PHP results in: 411C689D687F2F118EA05BF234F4E582
I don't know why PHP is getting a different value. Any help would be appreciated.
Thanks.
Related
Recently i've post a question about this topic but the thing turns a little weird when i tried to decrypt an AES String that was encoded on UTF8
In the following lines i read the String AES encrypted wich returns the following String: RnLObq9hdUDGp9S2pxC1qjQXekuf9g6i/5bQfKilYn4=
public static final String AesKey256 ="ZzRtNDNuY3J5cHRrM3kuLi4=";
//This reads the String and stores on res.getContents()
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult res= IntentIntegrator.parseActivityResult(requestCode, resultCode,data);
if(res!=null){
if (res.getContents() == null) {
Toast.makeText(this,"Captura cancelada",Toast.LENGTH_SHORT).show();
}else{
try {
String cadena= decrypt(res.getContents());
out.setText(cadena);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//provide the res.getContents() to decrypt method, wich its the String i've recently read
private String decrypt(String cadena)throws Exception{
SecretKeySpec keySpec= generateKey(AesKey256); //HERE
Cipher c= Cipher.getInstance(AES_MODE);
c.init(Cipher.DECRYPT_MODE,keySpec);
byte[] decodedValue= Base64.decode(cadena, Base64.DEFAULT);
byte[] decValue= c.doFinal(decodedValue);/* c.doFinal(decodedValue);*/
String decryptedValue= new String((decValue), "UTF-8");
return decryptedValue;
}
private SecretKeySpec generateKey(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
final MessageDigest digest= MessageDigest.getInstance("SHA-256");
byte[] bytes= password.getBytes("UTF-8");
digest.update(bytes,0,bytes.length);
byte[] key= digest.digest();
SecretKeySpec secretKeySpec= new SecretKeySpec(key, "AES");
return secretKeySpec;
}
I am only using the decryption method but it still returning this characters:
i've spent hours looking for a solution, but nothing works for now... hope someone can give me a hand!
Best Regards!
EDIT
THIS IS HOW IT WAS ENCRYPTED IN C#
private const string AesIV256 = "IVFBWjJXU1gjRURDNFJGVg==";
private const string AesKey256 = "ZzRtNDNuY3J5cHRrM3kuLi4=";
public static string Encrypt(string text)
{
var sToEncrypt = text;
var rj = new RijndaelManaged()
{
Padding = PaddingMode.Zeros,
Mode = CipherMode.ECB,
KeySize = 256,
BlockSize = 256,
};
var key = Convert.FromBase64String(AesKey256);
var IV = Convert.FromBase64String(AesIV256);
var encryptor = rj.CreateEncryptor(key, IV);
var msEncrypt = new MemoryStream();
var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
var toEncrypt = Encoding.UTF8.GetBytes(sToEncrypt);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
var encrypted = msEncrypt.ToArray();
return (Convert.ToBase64String(encrypted));
}
Made it!
After a long time of searching thanks to all you guys who answered this post i used the bouncy castle library and i've decrypt the desired String by doing the following:
public static String decrypt(String valueToDecrypt) throws Exception {
AESCrypt enc = new AESCrypt();
return new String(enc.decryptInternal(valueToDecrypt)).trim();
}
private byte[] decryptInternal(String code) throws Exception {
if (code == null || code.length() == 0) {
throw new Exception("Empty string");
}
byte[] decrypted = null;
try {
byte[] key= SecretKey.getBytes("UTF-8");
PaddedBufferedBlockCipher c = new PaddedBufferedBlockCipher(new RijndaelEngine(256), new ZeroBytePadding());
CipherParameters params= new KeyParameter(key);`
// false because its going to decrypt
c.init(false,params);
decrypted= GetData(c,(Base64.decode(code,Base64.DEFAULT));
} catch (Exception e) {
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
private static byte[] GetData(PaddedBufferedBlockCipher cipher, byte[] data) throws InvalidCipherTextException
{
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] cipherArray = new byte[actualLength];
for (int x = 0; x < actualLength; x++) {
cipherArray[x] = outBuf[x];
}
return cipherArray;
}
I searched a lot but I haven't found a good solution how to solve this. I have an app which has to decrypt a long hex string with AES 256.
In order to test it, I created a test method which encrypts a long text into a hex and then convert it back and decrypt it.
If I run this method, I always get the following error: Given final block not properly padded. I receive this error in the decryption method.
The test method looks like so:
#Test
public void testEncAndDecRequestWithHexString() throws UnsupportedEncodingException {
CryptoHelper cryptoHelper = new CryptoHelper("AES256");
String paramStr = "ABCB28BCEE5947B8AECE3386871EC0DF&{D5CA99D2-506B-4864-8971-E87821D6B105}&7523429";
//encrypt the param string
byte[] paramByteEnc = cryptoHelper.encryptBytesToBytes(paramStr.getBytes("ASCII"), PARAM_KEY, PARAM_IV);
//convert it to hex
String encryptedHexStr = cryptoHelper.byteArrayToHexStr(paramByteEnc);
//convert it back to a byte array
byte[] encryptedHexBytes = cryptoHelper.hexStrToByteArray(encryptedHexStr);
// decrypt it
byte[] paramByteDecrypted = cryptoHelper.decryptBytesToBytes(encryptedHexBytes, encryptedHexBytes.length, PARAM_KEY, PARAM_IV);
String decryptedStr = new String(paramByteDecrypted);
assertEquals("ABCB28BCEE5947B8AECE3386871EC0DF&{D5CA99D2-506B-4864-8971-E87821D6B105}&7523429", decryptedStr);
}
The CryptHelper class has following methods:
#Override
public byte[] encryptBytesToBytes(byte[] plainData, byte[] key, byte[] iv) {
try {
initCipher(Cipher.ENCRYPT_MODE, key, iv);
return aesCipher.doFinal(plainData);
} catch (IllegalBlockSizeException | BadPaddingException e) {
log.severe(e.getMessage());
}
return null;
}
#Override
public byte[] decryptBytesToBytes(byte[] encryptedBytes, int length,
byte[] key, byte[] iv) {
try {
initCipher(Cipher.DECRYPT_MODE, key, iv);
return aesCipher.doFinal(encryptedBytes, 0, length);
} catch (IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return null;
}
private void initCipher(int mode, byte[] keyBytes, byte[] ivBytes) {
try {
// create shared secret and init cipher mode
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCipher.init(mode == Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(ivBytes));
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
}
public String byteArrayToHexStr(byte[] encrypted) {
StringBuilder hex = new StringBuilder();
for (byte b : encrypted) {
hex.append(String.format("%02X", b));
}
return new String(hex.toString());
}
public byte[] hexStrToByteArray(String hex) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hex.length() - 1; i += 2) {
String output = hex.substring(i, (i + 2));
int decimal = Integer.parseInt(output, 16);
sb.append((char) decimal);
}
String temp = sb.toString();
return temp.getBytes();
}
I used the same key and initialization vector for the decryption process so the problem is not the wrong key or initialization vector. I am also sure that every function here is doing their job correctly. If you don't use the functions hexStrToByteArray() and byteArrayToHexStr() and just use the encrypted byte for decrypting, it works no problem. I think there is a encoding/decoding problem but I have no idea how to handle it in java. If I use getBytes("UTF-8") and new String(byte[], "UTF-8") I get an IllegalBlockSizeException.
I hope you can help me finding out if I am on the right way and what I did wrong.
This is a clear indication that you shouldn't write library functions if they have already been defined. Use a hex codec from Bouncy Castle, Guava or Apache codec instead (until Oracle finally sees the light and provides one in a java.util package).
If you do implement it yourself, please don't mistake characters for bytes:
public byte[] hexStrToByteArray(String hex) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(hex.length() / 2);
for (int i = 0; i < hex.length(); i += 2) {
String output = hex.substring(i, i + 2);
int decimal = Integer.parseInt(output, 16);
baos.write(decimal);
}
return baos.toByteArray();
}
In the code below I'm initializing the security key from an external class. Is it necessary to use the security key length of 16? Any possibility to use the smaller length byte?
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class MCrypt {
private String iv = "fedcba9876543210";
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey ;
public MCrypt(String s)
{
SecretKey=s;
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] data)
{
if (data==null)
{
return null;
}
int len = data.length;
String str = "";
for (int i=0; i<len; i++) {
if ((data[i]&0xFF)<16)
str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
else
str = str + java.lang.Integer.toHexString(data[i]&0xFF);
}
return str;
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
}
Your key must match one of the permitted lengths for the AES algorithm: 16, 24 or 32 bytes. You cannot use a key smaller than 16 bytes.
Some other comments on your code:
Use camelCase for all variables (e.g. secretKey).
Avoid using a fixed IV. Generate a random IV each time and store it with the ciphertext.
Consider using a padding mode (e.g. AES/CBC/PKCS5Padding) otherwise your input must always be a multiple of 16 bytes.
You are calling getBytes() on a string without supplying a charset. This could result in different results on different platforms. Always specify a character set.
In addition to Duncan's suggested improvements:
Never ever use a password/String directly as a key!
If you want to create a key based on a password use a "Password based key derivation function" - e.g. the standard is PBKDF2 (see for example this question: PBKDF2 with bouncycastle in Java).
I'm following this tutorial to use 3DES encryption, and i needed to make some changes in cipher settings, so here's my code:
public class TripleDES {
public static int MAX_KEY_LENGTH = DESedeKeySpec.DES_EDE_KEY_LEN;
private static String ENCRYPTION_KEY_TYPE = "DESede";
private static String ENCRYPTION_ALGORITHM = "DESede/ECB/PKCS7Padding";
private final SecretKeySpec keySpec;
private final static String LOG = "TripleDES";
public TripleDES(String passphrase) {
byte[] key;
try {
// get bytes representation of the password
key = passphrase.getBytes("UTF8");
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e);
}
key = padKeyToLength(key, MAX_KEY_LENGTH);
key = addParity(key);
keySpec = new SecretKeySpec(key, ENCRYPTION_KEY_TYPE);
}
// !!! - see post below
private byte[] padKeyToLength(byte[] key, int len) {
byte[] newKey = new byte[len];
System.arraycopy(key, 0, newKey, 0, Math.min(key.length, len));
return newKey;
}
// standard stuff
public byte[] encrypt(String message) throws GeneralSecurityException, UnsupportedEncodingException {
byte[] unencrypted = message.getBytes("UTF8");
return doCipher(unencrypted, Cipher.ENCRYPT_MODE);
}
public byte[] decrypt(byte[] encrypted) throws GeneralSecurityException {
return doCipher(encrypted, Cipher.DECRYPT_MODE);
}
private byte[] doCipher(byte[] original, int mode)
throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
// IV = 0 is yet another issue, we'll ignore it here
// IvParameterSpec iv = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 });
cipher.init(mode, keySpec); //, iv);
return cipher.doFinal(original);
}
// Takes a 7-byte quantity and returns a valid 8-byte DES key.
// The input and output bytes are big-endian, where the most significant
// byte is in element 0.
public static byte[] addParity(byte[] in) {
byte[] result = new byte[8];
// Keeps track of the bit position in the result
int resultIx = 1;
// Used to keep track of the number of 1 bits in each 7-bit chunk
int bitCount = 0;
// Process each of the 56 bits
for (int i = 0; i < 56; i++) {
// Get the bit at bit position i
boolean bit = (in[6 - i / 8] & (1 << (i % 8))) > 0;
// If set, set the corresponding bit in the result
if (bit) {
result[7 - resultIx / 8] |= (1 << (resultIx % 8)) & 0xFF;
bitCount++;
}
// Set the parity bit after every 7 bits
if ((i + 1) % 7 == 0) {
if (bitCount % 2 == 0) {
// Set low-order bit (parity bit) if bit count is even
result[7 - resultIx / 8] |= 1;
}
resultIx++;
bitCount = 0;
}
resultIx++;
}
Log.d(LOG, "result: " + result);
return result;
}
}
But i'm getting InvalidKeyException on this line:
cipher.init(mode, keySpec);
LogCat:
W/System.err(758): java.security.InvalidKeyException: src.length=8 srcPos=8 dst.length=8 dstPos=0 length=8
W/System.err(758): at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(JCEBlockCipher.java:584)
W/System.err(758): at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(JCEBlockCipher.java:631)
W/System.err(758): at javax.crypto.Cipher.init(Cipher.java:511)
W/System.err(758): at javax.crypto.Cipher.init(Cipher.java:471)
I'm new on encrytion so i probably overlook something but i cannot find what it is. Any help is appreciated...
Triple DES requires a 24 byte key, not an 8 byte one.
I've found solution by changing these lines:
try {
// get bytes representation of the password
key = passphrase.getBytes("UTF8");
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e);
}
key = padKeyToLength(key, MAX_KEY_LENGTH);
key = addParity(key);
keySpec = new SecretKeySpec(key, ENCRYPTION_KEY_TYPE);
into these:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] keyBytes = GetKeyAsBytes(key);
keySpec = new SecretKeySpec(keyBytes, "DESede");
while GetKeyAsBytes method is this:
public byte[] GetKeyAsBytes(String key) {
byte[] keyBytes = new byte[24]; // a Triple DES key is a byte[24] array
for (int i = 0; i < key.length() && i < keyBytes.length; i++)
keyBytes[i] = (byte) key.charAt(i);
return keyBytes;
}
I am trying to write an encryption algorithm using RSA in Java, I am getting a
"javax.crypto.BadPaddingException: Data must start with zero"; I do not know what is this exception for.
This is the example I used here
and Here is my code; please help.
public byte[] getEncryptedValue(byte[] bytes, PublicKey key) {
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
return blockCipher(bytes, Cipher.ENCRYPT_MODE);
} catch (Exception ex) {
Logger.getLogger(SecurityUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public byte[] getDecryptedValue(byte[] bytes, PrivateKey key) {
try {
cipher.init(Cipher.DECRYPT_MODE, key);
return blockCipher(bytes, Cipher.DECRYPT_MODE);
} catch (Exception ex) {
Logger.getLogger(SecurityUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
private byte[] append(byte[] prefix, byte[] suffix) {
byte[] toReturn = new byte[prefix.length + suffix.length];
System.arraycopy(prefix, 0, toReturn, 0, prefix.length);
System.arraycopy(suffix, 0, toReturn, prefix.length, suffix.length);
return toReturn;
}
private byte[] blockCipher(byte[] bytes, int mode) throws IllegalBlockSizeException, BadPaddingException {
byte[] scrambled = new byte[0];
byte[] toReturn = new byte[0];blocks (because of RSA)
int length = (mode == Cipher.ENCRYPT_MODE) ? 100 : 128;
int n = 0;
byte[] buffer = new byte[length];
for (int i = 0; i < bytes.length; i++) {
if ((i > 0) && (i % length == 0)) {
n = 0;
scrambled = cipher.doFinal(buffer);
toReturn = append(toReturn, scrambled);
}
buffer[i % length] = bytes[i];
n++;
}
***scrambled = cipher.doFinal(buffer, 0, n);*** <-- the exception is caught here
toReturn = append(toReturn, scrambled);
return toReturn;
}
The problem could be the data sent over the network using sockets may be corrupted due to some encoding problems. I had the same problem while developing a simple client/server chat program that encrypts/decrypts using asymmetric key the messages between the server and client and vise versa, instead of sending the message as a string, I sent it as a byte array which is the encrypted message.
check if keys are matching
check if data returned by getEncryptedValue are the same that you pass to getDecryptedValue
check corectness of loop in blockCipher method