all files in ~/Cipher/nsdl/crypto can be found here
java files compiled with gcj, see compile.sh
nmint#nqmk-mint ~/Cipher/nsdl/crypto $ echo test | ./cryptTest encrypt deadbeefdeadbeefdeadbeefdeadbeef deadbeef Blowfish CBC > test
null
Exception in thread "main" java.lang.IllegalStateException: cipher is not for encrypting or decrypting
at javax.crypto.Cipher.update(libgcj.so.81)
at javax.crypto.CipherOutputStream.write(libgcj.so.81)
at nsdl.crypto.BlockCrypt.encrypt(cryptTest)
at nsdl.crypto.cryptTest.main(cryptTest)
BlockCrypt.java:
package nsdl.crypto;
import java.io.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class BlockCrypt {
Cipher ecipher;
Cipher dcipher;
byte[] keyBytes;
byte[] ivBytes;
SecretKey key;
AlgorithmParameterSpec iv;
byte[] buf = new byte[1024];
BlockCrypt(String keyStr, String ivStr, String algorithm, String mode) {
try {
ecipher = Cipher.getInstance(algorithm + "/" + mode + "/PKCS5Padding");
dcipher = Cipher.getInstance(algorithm + "/" + mode + "/PKCS5Padding");
keyBytes = hexStringToByteArray(keyStr);
ivBytes = hexStringToByteArray(ivStr);
key = new SecretKeySpec(keyBytes, algorithm);
iv = new IvParameterSpec(ivBytes);
ecipher.init(Cipher.ENCRYPT_MODE, key, iv);
dcipher.init(Cipher.DECRYPT_MODE, key, iv);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
public void encrypt(InputStream in, OutputStream out) {
try {
// out: where the plaintext goes to become encrypted
out = new CipherOutputStream(out, ecipher);
// in: where the plaintext comes from
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
public void decrypt(InputStream in, OutputStream out) {
try {
// in: where the plaintext come from, decrypted on-the-fly
in = new CipherInputStream(in, dcipher);
// out: where the plaintext goes
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.flush();
out.close();
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
cryptTest.java:
package nsdl.crypto;
import nsdl.crypto.BlockCrypt;
public class cryptTest {
public static void main (String args[]) {
if (args.length != 5) {
System.err.println("Usage: cryptTest (encrypt|decrypt) key iv algorithm mode");
System.err.println("Takes input from STDIN. Output goes to STDOUT.");
} else {
String operation = args[0];
String key = args[1];
String iv = args[2];
String algorithm = args[3];
String mode = args[4];
BlockCrypt blockCrypt = new BlockCrypt(key, iv, algorithm, mode);
if (operation.equalsIgnoreCase("encrypt")) {
blockCrypt.encrypt(System.in, System.out);
} else if (operation.equalsIgnoreCase("decrypt")) {
blockCrypt.decrypt(System.in, System.out);
} else {
System.err.println("Invalid operation. Use (encrypt|decrypt).");
}
}
}
}
The Cipher, ecipher, is not initialized, and it throws an IllegalStateException when you try to use it as if it were initialized in ENCRYPT_MODE.
Note your catch block in the constructor of BlockCrypt. It is catching an exception with no message, and printing "null" to System.err. Rather than aborting execution—perhaps by throwing an exception from the constructor—you keep sailing.
Replacing System.err.println(e.getMessage()) with e.printStackTrace() or at least System.err.println(e) should give you more detail. My guess is that ecipher.init() is throwing an exception because you're providing a 32-bit IV instead of 64 bits.
Perhaps looking at the source for javax.crypto.Cipher helps this make sense? I couldn't really figure it out, even finding the error message in the source. Good luck!
Related
I would like to encrypt and decrypt files. While decrypting I wish to skip first few bytes of the file and decrypt only the rest.
Here contains three files
input_file.txt - input text file for encryption
output_file.txt - encrypted input_file.txt file
decrypted_file.txt - decrypted output_file.txt file
Sample code:
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileReadWrite {
public static byte[] getAESKey() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[32];
secureRandom.nextBytes(bytes);
return bytes;
}
/**
* Method used to generate a random new iv
*
* #return Randomly generated iv
*/
public static byte[] getAESIV() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);
return bytes;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
FileInputStream fin = new FileInputStream("/Users/emp/Research/Streams/input_file.txt");
FileOutputStream fout = new FileOutputStream("/Users/emp/Research/Streams/output_file.txt");
SecretKeySpec keySpec = null;
IvParameterSpec ivSpec = null;
Cipher ecipher = null;
Cipher dcipher = null;
byte[] keyBytes = getAESKey();
byte[] ivBytes = getAESIV();
// Creating keyspec and ivspec for generating cipher
keySpec = new SecretKeySpec(keyBytes,"AES");
ivSpec = new IvParameterSpec(ivBytes);
try {
ecipher = Cipher.getInstance("AES/CTR/NoPadding");
ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
} catch (Exception e) {
System.out.println("Thus the exception occured during cipher generation is ::: "+e);
}
CipherOutputStream cout = new CipherOutputStream(fout, ecipher);
try {
int count = 0;
int BUFFER_SIZE = 1024;
byte[] bytearray = new byte[BUFFER_SIZE];
while((count = fin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
//fout.write(bytearray, 0, count);
cout.write(bytearray, 0, count);
}
} catch(Exception ex) {
System.out.println("Thus the exception occured is ::: "+ex);
} finally {
fin.close();
fout.close();
cout.close();
}
fin = new FileInputStream("/Users/emp/Research/Streams/output_file.txt");
fout = new FileOutputStream("/Users/emp/Research/Streams/decrypted_file.txt");
try {
dcipher = Cipher.getInstance("AES/CTR/NoPadding");
dcipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
} catch (Exception e) {
System.out.println("Thus the exception occured during cipher generation is ::: "+e);
}
//fin.skip(1024);
CipherInputStream cin = new CipherInputStream(fin, dcipher);
try {
int count = 0;
int BUFFER_SIZE = 1024;
byte[] bytearray = new byte[BUFFER_SIZE];
**//cin.read(bytearray, 0, 30);**
while((count = cin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
//fout.write(bytearray, 0, count);
fout.write(bytearray, 0, count);
}
} catch(Exception ex) {
System.out.println("Thus the exception occured is ::: "+ex);
} finally {
fin.close();
cin.close();
fout.close();
}
System.out.println("File read write completed successfully !!! ");
}
}
Tried:
Cipher input stream skip - cin.skip(no_of_bytes) - this doesn't work - this decrypts the entire file
File input stream skip
fin.skip(no_of_bytes); CipherInputStream cin = new
CipherInputStream(fin, cipher);
This does not decrypt the file. The output file looks like encrypted.
Dummy read - Reading and ignoring from cipher input stream - this works
//cin.read(bytearray, 0, 30);
Please clarify me the following:
What skip and seek input stream does internally?
Why my cipher input stream seek doesn't work?
Why cipher input stream requires all the bytes to be read from the stream (I assume so) for decrypting? How does it work?
With reference to the article,
Random access InputStream using AES CTR mode in android
After updating the iv with offset value the decryption works fine.
Here is the updated code
package Streams;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class FileReadWrite {
private static final int AES_BLOCK_SIZE = 16;
public static final void jumpToOffset(final Cipher c, final SecretKeySpec aesKey, final IvParameterSpec iv, final long offset) {
if (!c.getAlgorithm().toUpperCase().startsWith("AES/CTR")) {
throw new IllegalArgumentException("Invalid algorithm, only AES/CTR mode supported");
}
if (offset < 0) {
throw new IllegalArgumentException("Invalid offset");
}
final int skip = (int) (offset % AES_BLOCK_SIZE);
final IvParameterSpec calculatedIVForOffset = calculateIVForOffset(iv, offset - skip);
try {
c.init(Cipher.ENCRYPT_MODE, aesKey, calculatedIVForOffset);
final byte[] skipBuffer = new byte[skip];
c.update(skipBuffer, 0, skip, skipBuffer);
Arrays.fill(skipBuffer, (byte) 0);
} catch (ShortBufferException | InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalStateException(e);
}
}
private static IvParameterSpec calculateIVForOffset(final IvParameterSpec iv, final long blockOffset) {
final BigInteger ivBI = new BigInteger(1, iv.getIV());
final BigInteger ivForOffsetBI = ivBI.add(BigInteger.valueOf(blockOffset / AES_BLOCK_SIZE));
final byte[] ivForOffsetBA = ivForOffsetBI.toByteArray();
final IvParameterSpec ivForOffset;
if (ivForOffsetBA.length >= AES_BLOCK_SIZE) {
ivForOffset = new IvParameterSpec(ivForOffsetBA, ivForOffsetBA.length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
} else {
final byte[] ivForOffsetBASized = new byte[AES_BLOCK_SIZE];
System.arraycopy(ivForOffsetBA, 0, ivForOffsetBASized, AES_BLOCK_SIZE - ivForOffsetBA.length, ivForOffsetBA.length);
ivForOffset = new IvParameterSpec(ivForOffsetBASized);
}
return ivForOffset;
}
public static byte[] getAESKey() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[32];
secureRandom.nextBytes(bytes);
return bytes;
}
/**
* Method used to generate a random new iv
*
* #return Randomly generated iv
*/
public static byte[] getAESIV() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);
return bytes;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
FileInputStream fin = new FileInputStream("/Users/emp/Research/Streams/input_file.txt");
FileOutputStream fout = new FileOutputStream("/Users/emp/Research/Streams/output_file.txt");
SecretKeySpec keySpec = null;
IvParameterSpec ivSpec = null;
Cipher ecipher = null;
Cipher dcipher = null;
byte[] keyBytes = getAESKey();
byte[] ivBytes = getAESIV();
// Creating keyspec and ivspec for generating cipher
keySpec = new SecretKeySpec(keyBytes,"AES");
ivSpec = new IvParameterSpec(ivBytes);
try {
ecipher = Cipher.getInstance("AES/CTR/NoPadding");
ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
} catch (Exception e) {
System.out.println("Thus the exception occured during cipher generation is ::: "+e);
}
CipherOutputStream cout = new CipherOutputStream(fout, ecipher);
try {
int count = 0;
int BUFFER_SIZE = 1024;
byte[] bytearray = new byte[BUFFER_SIZE];
while((count = fin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
//fout.write(bytearray, 0, count);
cout.write(bytearray, 0, count);
}
} catch(Exception ex) {
System.out.println("Thus the exception occured is ::: "+ex);
} finally {
fin.close();
fout.close();
cout.close();
}
fin = new FileInputStream("/Users/emp/Research/Streams/output_file.txt");
fout = new FileOutputStream("/Users/emp/Research/Streams/decrypted_file.txt");
try {
dcipher = Cipher.getInstance("AES/CTR/NoPadding");
dcipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
} catch (Exception e) {
System.out.println("Thus the exception occured during cipher generation is ::: "+e);
}
fin.skip(1024);
jumpToOffset(dcipher, keySpec, ivSpec, 1024);
CipherInputStream cin = new CipherInputStream(fin, dcipher);
//cin.skip(1024);
try {
int count = 0;
int BUFFER_SIZE = 1024;
byte[] bytearray = new byte[BUFFER_SIZE];
//cin.read(bytearray, 0, 30);
while((count = cin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
//fout.write(bytearray, 0, count);
fout.write(bytearray, 0, count);
}
} catch(Exception ex) {
System.out.println("Thus the exception occured is ::: "+ex);
} finally {
fin.close();
cin.close();
fout.close();
}
System.out.println("File read write completed successfully !!! ");
}
}
I have an API documentation that requires encrypting a key to authenticate,
I managed to build and compile their sample code, but the results on windows are different than linux.
When I run and test from Windows, all seems to be correct and works with the API.
That same test on Linux outputs a different result. I need it working on Linux since that's the main server.
I am using & running the same jar file on both environments.
This is the key I am trying to encrypt (it's a dynamic key):
2136230$486B91E1BEA5D082BA3601CD803585CE$20140409$20140409$$ABCDEFGH$Reserved$CTC
This is the correct output on Windows (it's obviously quite longer):
F7BE2B7E0CEAD9D09135FCF2A8AEB11E2937D26B33CCBC9B8132A29A3534040C9737B2A8E3F271A9DF6454696CF890F7886223AE9C86F81EF58E41AEAA3D34A80F7089154E64F4FD36E75C25A7C2DA7FF03D21F57DA950F5
This is the wrong output from Linux:
F66D4CE1238B30EE54ABC74966D7AC3064FEA3ADFB9D37548E41509CE4FED9CB1D146651B491F2433169999A85F73DAF9ACD07A090DF3D85477BE4201ADC9E1A0181EA7CB763050A
What is causing this and how to correct it ?
This is the source code of the program to use as we received from the API company:
public class DESUtil
{
private static final String Algorithm = "DESede/ECB/PKCS5Padding";// DESede/ECB/PKCS5Padding;DESede
private static final String DESede = "DESede";
public static byte[] encrypt(byte[] keybyte, byte[] src)
throws NoSuchAlgorithmException, NoSuchPaddingException, Exception
{
SecretKey deskey = new SecretKeySpec(keybyte, DESede);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.ENCRYPT_MODE, deskey);
return c1.doFinal(src);
}
public static byte[] decrypt(byte[] keybyte, byte[] src)
throws NoSuchAlgorithmException, NoSuchPaddingException, Exception
{
SecretKey deskey = new SecretKeySpec(keybyte, DESede);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.DECRYPT_MODE, deskey);
return c1.doFinal(src);
}
public static String byte2hex(byte[] b)
{
StringBuffer hs = new StringBuffer();
String stmp = "";
for (int n = 0; n <b.length; n++)
{
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1)
hs.append("0").append(stmp);
else
hs.append(stmp);
}
return hs.toString().toUpperCase(Locale.getDefault());
}
public static byte[] hex2byte(String hexStr)
{
if (hexStr.length() % 2 != 0)
{
AppLogger.error("hex2bytes's hexStr length is not even.");
return null;
}
byte[] toBytes = new byte[hexStr.length() / 2];
for (int i = 0, j = 0; i <hexStr.length(); j++, i = i + 2)
{
int tmpa = Integer.decode(
"0X" + hexStr.charAt(i) + hexStr.charAt(i + 1)).intValue();
toBytes[j] = (byte) (tmpa & 0XFF);
}
return toBytes;
}
public static void main(String[] args)
{
Security.addProvider(new com.sun.crypto.provider.SunJCE());
final byte[] rawKey = "db90e7eb".getBytes();
final byte[] keyBytes = new byte[24];
for (int i = 0; i <rawKey.length; i++)
{
keyBytes[i] = rawKey[i];
}
for (int i = rawKey.length; i <keyBytes.length; i++)
{
keyBytes[i] = (byte)0;
}
String szSrc = "20926330$AD75B1697FB5EB6345B2D412124030D2$10086$10086$10.164.111$ABCDEFGH$Reserved$CTC";
System.out.println("string before encrypt:" + szSrc);
byte[] encoded = null;
try
{
encoded = encrypt(keyBytes, szSrc.getBytes());
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("string after encrypt::" + byte2hex(encoded));
byte[] srcBytes = null;
try
{
srcBytes = decrypt(keyBytes, encoded);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("string before decode: :" + (new String(srcBytes)));
}
}
Almost certainly your use of szSrc.getBytes() which uses the platform's default character encoding.
Try szSrc.getBytes("ISO-8859-1") as a starter if it's working on Windows, but if this string comes from an external service you should determine the encoding scheme dynamically (eg. if it comes through a Servlet use httpRequest.getCharacterEncoding()).
I am working on AES algorithm and I get this Exception:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at org.enterprisepower.io.MyEncryptTools.decrypt(MyEncryptTools.java:41)
at org.enterprisepower.io.IOUtils.copyStream(IOUtils.java:132)
at org.enterprisepower.net.portforward.Processor$Copier.run(Processor.java:99)
at java.lang.Thread.run(Unknown Source)
the exception happen in the decryption part.I encrypt a message in myClient program and send cipherMessage to myServer program.After server recieve the cipherMessage it throws the above exception, but in client I can decrypt the exact same cipherMessage.(I check this with printing the bytes in both side...)
These are my codes:
//It's decrypt method for both client and server
public byte[] decrypt(byte[] encryptedData, int length) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] enc = new byte[length];
for (int i = 0; i < length; i++) {
enc[i] = encryptedData[i];
}
byte[] decordedValue= Base64.decodeBase64(enc);//org.apache.commons.codec.binary.*;
byte[] decValue = c.doFinal(decordedValue);
return decValue;
}
//Client side
public static void copyStream(InputStream in, OutputStream out,
boolean closeOnFinish, boolean encrypt, String password) throws Exception {
MyEncryptTools mit;
try {
mit = new MyEncryptTools(password);
byte[] buf = new byte[BUF_SIZE];
byte[] enbuf ;
int count;
try {
if (encrypt) {
while (((count = in.read(buf)) != -1) && alive) {
enbuf = mit.encrypt(buf,count);
out.write(enbuf, 0, enbuf.length);
}
} else {
buf = new byte[172];
while (((count = in.read(buf)) != -1) && alive) {
enbuf = mit.decrypt(buf, count);
out.write(enbuf, 0, enbuf.length);
}
}
} finally {
if (closeOnFinish)
close(in);
if (closeOnFinish)
close(out);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//Server side
public static void copyStream(InputStream in, OutputStream out,
boolean closeOnFinish, boolean encrypt, String user, String password) throws Exception {
MyEncryptTools mit;
try {
mit = new MyEncryptTools(password);
byte[] buf = new byte[BUF_SIZE];
byte[] enbuf ;
int count;
try {
if (encrypt) {
System.out.println("Encrypt;");
while (((count = in.read(buf)) != -1)) {
enbuf = mit.encrypt(buf,count);
out.write(enbuf, 0, enbuf.length);
}
} else {
buf = new byte[172];
while (((count = in.read(buf)) != -1)) {
enbuf = mit.decrypt(buf, count);
out.write(enbuf, 0, enbuf.length);
}
}
} finally {
if (closeOnFinish)
close(in);
if (closeOnFinish)
close(out);
}
} catch (Exception e) {
e.printStackTrace();
}
}
I should mention that I use an array of 117 byte for encrypting and an array of 172 for decrypting.
Use the wonderful CipherInputStream and forget about buffers length.
Your error message says: "Given final block not properly padded". To fix the error specify PKCS7 padding for both encryption or decryption. Alternatively switch to CTR mode, which does not require padding.
I'm trying to encrypt data between my android application and a PHP webservice.
I found the next piece of code in this website: http://schneimi.wordpress.com/2008/11/25/aes-128bit-encryption-between-java-and-php/
But when I try to decrypt I get the Exception of the title "data not block size aligned"
This are the method in my MCrypt class
public String encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
Cipher cipher;
byte[] encrypted = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return new String( encrypted );
}
public String decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
Cipher cipher;
byte[] decrypted = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return new String( decrypted );
}
private static byte[] hexToBytes(String hex) {
String HEXINDEX = "0123456789abcdef";
int l = hex.length() / 2;
byte data[] = new byte[l];
int j = 0;
for (int i = 0; i < l; i++) {
char c = hex.charAt(j++);
int n, b;
n = HEXINDEX.indexOf(c);
b = (n & 0xf) << 4;
c = hex.charAt(j++);
n = HEXINDEX.indexOf(c);
b += (n & 0xf);
data[i] = (byte) b;
}
return data;
}
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;
}
And this is how I'm using it in my activity to test:
String encrypted = mcrypt.encrypt(jsonUser.toString());
String decrypted = mcrypt.decrypt(encrypted);
the encrypt method works fine, but the second throws an exception.
At last! I made it work! Thanks for all your suggestion. I would like to share the code just in case somebody get stuck like me:
JAVA
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";//Dummy iv (CHANGE IT!)
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)
public MCrypt()
{
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;
}
}
HOW TO USE IT (JAVA)
mcrypt = new MCrypt();
/* Encrypt */
String encrypted = MCrypt.bytesToHex( mcrypt.encrypt("Text to Encrypt") );
/* Decrypt */
String decrypted = new String( mcrypt.decrypt( encrypted ) );
====================================================
PHP
<?php
class MCrypt
{
private $iv = 'fedcba9876543210'; #Same as in JAVA
private $key = '0123456789abcdef'; #Same as in JAVA
function __construct()
{
}
function encrypt($str) {
//$key = $this->hex2bin($key);
$iv = $this->iv;
$td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);
mcrypt_generic_init($td, $this->key, $iv);
$encrypted = mcrypt_generic($td, $str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return bin2hex($encrypted);
}
function decrypt($code) {
//$key = $this->hex2bin($key);
$code = $this->hex2bin($code);
$iv = $this->iv;
$td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);
mcrypt_generic_init($td, $this->key, $iv);
$decrypted = mdecrypt_generic($td, $code);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return utf8_encode(trim($decrypted));
}
protected function hex2bin($hexdata) {
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
}
HOW TO USE IT (PHP)
<?php
$mcrypt = new MCrypt();
#Encrypt
$encrypted = $mcrypt->encrypt("Text to encrypt");
#Decrypt
$decrypted = $mcrypt->decrypt($encrypted);
I'm guessing your keyspec and ivspec are not valid for decryption. I've typically transformed them into PublicKey and PrivateKey instances and then use the private key to decrypt.
I looked at the comments in the other answer. I ran into a similar problem trying to encrypt a large block of text using open SSL in php (on both sides). I imagine the same issue would come up in Java.
If you have a 1024 bit RSA key, you must split the incoming text into 117 byte chunks (a char is a byte) and encrypt each (you can concatenate them together). On the other end, you must split the encrypted data into 128 byte chunks and decrypt each. This should give you your original message.
Also note that http may not play friendly with the non-ASCII encrypted data. I base64 encode/decode it before and after transmission (plus you have to worry about additional urlencoding for the base64 change, but it is easy).
I'm not sure of your AES key length, but if it's 1024 bits the chunk length is probably the same. If it's not, you will have to divide the bits by 8 to find the byte chunk length coming out. I'm actually not sure how to get it coming in, unfortunately (maybe multiply by 117/128 ?)
Here's some php code:
class Crypto {
public function encrypt($key, $data) {
$crypto = '';
foreach (str_split($data, 117) as $chunk) {
openssl_public_encrypt($chunk, $encrypted, $key);
$crypto .= $encrypted;
}
return $crypto;
}
//Decrypt omitted. Basically the same, change 117 to 128.
/**##+
* Update data for HTTP transmission and retrieval
* Must be used on encrypted data, but also useful for any binary data
* (e.g. zip files).
*/
public function base64_encode($value) {
return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
}
//String length must be padded for decoding for some reason
public function base64_decode($value) {
return base64_decode(str_pad(strtr($value, '-_', '+/')
, strlen($value) % 4, '=', STR_PAD_RIGHT));
}
/**##-*/
}
I use DES algorithm to encrypt/decrypt my text. And it works perfect with latin texts.
But when I start encrypt/decrypt cyrillic text, decrypted one is shown as ????? ???????? in my TextField form and in console. How can i fix it?
After Joachim Sauer advice I changed inputBytes = textToEnrypt.getBytes(); to inputBytes = textToEnrypt.getBytes("UTF-8"); and now I have javax.crypto.IllegalBlockSizeException. Help me, please...
package crypting;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.microedition.midlet.*;
public class Encryptor extends MIDlet {
String buffer;
public void startApp() {
String keyString = "testtest";
// encrypt("Text for encrypting", keyString);
encrypt("Привет", keyString);
decrypt(buffer, keyString);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void encrypt(String textToEnrypt, String keyString) {
Cipher cipher;
try {
cipher = Cipher.getInstance("DES");
} catch (Exception ex) {
System.out.println(ex.toString());
return;
}
byte[] keyData = keyString.getBytes();
SecretKeySpec key = new SecretKeySpec(keyData, 0, keyData.length, "DES");
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
} catch (Exception ex) {
System.out.println(ex.toString());
return;
}
int cypheredBytes = 0;
byte[] inputBytes;
try {
inputBytes = textToEnrypt.getBytes("UTF-8");
// inputBytes = textToEnrypt.getBytes();
} catch (Exception ex) {
System.out.println(ex.toString());
return;
}
byte[] outputBytes = new byte[100];
try {
cypheredBytes = cipher.doFinal(inputBytes, 0, inputBytes.length,
outputBytes, 0);
} catch (Exception ex) {
System.out.println(ex.toString());
return;
}
String str = new String(outputBytes, 0, cypheredBytes);
buffer = str;
System.out.println("Encrypted string = " + str);
}
public void decrypt(String textToDecrypt, String keyString) {
Cipher cipher;
try {
cipher = Cipher.getInstance("DES");
} catch (Exception ex) {
System.out.println(ex.toString());
return;
}
byte[] keyData = keyString.getBytes();
SecretKeySpec key = new SecretKeySpec(keyData, 0, keyData.length, "DES");
try {
cipher.init(Cipher.DECRYPT_MODE, key);
} catch (Exception ex) {
System.out.println("2. " + ex.toString());
return;
}
int cypheredBytes = 0;
byte[] inputBytes;
try {
inputBytes = textToDecrypt.getBytes("UTF-8");
// inputBytes = textToDecrypt.getBytes();
} catch (Exception ex) {
System.out.println("3. " + ex.toString());
return;
}
byte[] outputBytes = new byte[100];
try {
cypheredBytes = cipher.doFinal(inputBytes, 0, inputBytes.length,
outputBytes, 0);
} catch (Exception ex) {
System.out.println("4. " + ex.toString());
return;
}
String str = new String(outputBytes, 0, cypheredBytes);
System.out.println("Decrypted string = " + str);
}
}
I was able to make it work with the following changes.
Change the return type of encrypt method, from void to byte[]:
static public byte[] encrypt(String textToEnrypt, String keyString)
throws Exception
{
//at the end
//write this down:
byte[] newResponse = new byte[cypheredBytes];
for(int i=0;i < cypheredBytes;i++)
{
newResponse[i] = outputBytes[i];
}
return newResponse;
}
Instead of:
String str = new String(outputBytes, 0, cypheredBytes);
buffer = str;
System.out.println("Encrypted string = " + str);
My guess (and I can't do more without you showing some code) is that you're using getBytes() without a parameter and construct the String from the byte[] without a parameter as well. That means using the platform default encoding and if that can't represent cyrillic characters, then that's what you get.
It's better to use UTF-8 for both transformations, this way you can represent every Unicode character.
DES uses a 64-bit (8-byte) block size. You have to make sure that the data you're encrypting is a multiple of 8 bytes, i.e. pad it out with zero bytes or whatever to ensure that it is. Otherwise you'll get an IllegalBlockSizeException. You'll need to do this after you convert the string to UTF-8, of course....
One problem is the line
String str = new String(outputBytes, 0, cypheredBytes);
in your encrypt method. You cannot use String as a container for binary data. Encrypted data should not be converted to a String unless it is a requirement, and then you need to use an appropriate codec such as base64.