Invalid key size in Java symmetric encryption program - java

This is my basic symmetric encryption program. As im learning now I want to know how this is working. And I have the following Exception:
Exception in thread "main" java.security.InvalidKeyException: Illegal
key size or default parameters at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..) at
javax.crypto.Cipher.init(DashoA13*..) at
sample.MainClass.main(MainClass.java:24)
This is my program:
public class MainClass {
public static void main(String[] args) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] input = " www.java2s.com ".getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding", "BC");
System.out.println("input text : " + new String(input));
// encryption pass
byte[] cipherText = new byte[input.length];
cipher.init(Cipher.ENCRYPT_MODE, key);
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
System.out.println("cipher text: " + new String(cipherText) + " bytes: " + ctLength);
// decryption pass
byte[] plainText = new byte[ctLength];
cipher.init(Cipher.DECRYPT_MODE, key);
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
ptLength += cipher.doFinal(plainText, ptLength);
System.out.println("plain text : " + new String(plainText) + " bytes: " + ptLength);
}
}
Can you please tell me how to solve this problem?

This does indeed seem like a duplicate of Java Security: Illegal key size or default parameters? . You probably need to replace your local_policy.jar and US_export_policy.jar with the Unlimited Security versions. You can download them here: http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
These files should be in your jre/lib/security/ folder. Also, for me I had a ton of different JREs in different locations, and the one I was using was inside my jdk folder path. It took me the longest time to figure this crap out

Related

Can't get AES cipher working on Android

I can't get a basic AES encryption/decryption round-tripping unit test to pass on Android. Probably something I'm doing, but would greatly appreciate the guidance:
public class EncryptionManager {
private static final byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
private static final byte[] ivBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
public byte[] encrypt(byte[] plaintext) throws Exception {
Cipher cipher = getCipher(true);
return cipher.doFinal(plaintext);
}
public byte[] decrypt(byte [] ciphertext) throws Exception {
Cipher cipher = getCipher(false);
return cipher.doFinal(ciphertext);
}
private static Cipher getCipher(boolean encrypt) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE,
secretKeySpec, ivParameterSpec);
return cipher;
}
}
The failing unit test looks like this:
public class EncryptionManagerTest extends TestCase {
public void testShouldPbeRoundtrip() throws Exception {
String unencryptedStr = "foobargum";
byte[] unencrypted = unencryptedStr.getBytes();
EncryptionManager encryptionManager = new EncryptionManager();
byte[] encrypted = encryptionManager.encrypt(unencrypted);
String encryptedStr = new String(encrypted);
byte[] decrypted = encryptionManager.decrypt(encrypted);
String decryptedStr = new String(encrypted);
// assert
assertFalse("encryption not done",
unencryptedStr.equalsIgnoreCase(encryptedStr));
assertEquals("decryption didn't work", unencryptedStr,
decryptedStr);
}
}
Error: "decryption didn't work expected:<[foobargum]> but was:<[�3���jw� �|*�]>"
String encryptedStr = new String(encrypted); is a mistake. Encryption produces byte sequences that are not likely to be a valid String for a given charset, in particular not for the UTF-8 charset that is the android default. In converting the byte array encrypted to a String object invalid byte sequences are silently replaced with valid characters in an unrecoverable manner.
I think you have a copy-paste error:
String decryptedStr = new String(encrypted);

How to use Java Card crypto sample?

I'm trying to make run example from IBM website.
I wrote this method:
public static byte[] cipher(byte[] inputData) {
Cipher cipher
= Cipher.getInstance(
Cipher.ALG_DES_CBC_NOPAD, true);
DESKey desKey = (DESKey) KeyBuilder.buildKey(
KeyBuilder.TYPE_DES,
KeyBuilder.LENGTH_DES,
false);
byte[] keyBytes = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04};
desKey.setKey(keyBytes, (short) 0);
cipher.init(desKey, Cipher.MODE_ENCRYPT);
byte[] outputData = new byte[8];
cipher.doFinal(inputData, (short) 0, (short) inputData.length, outputData, (short) 0);
return outputData;
}
And call this method cipher("test".getBytes());. When I call this servlet server gives me Internal server error and javacard.security.CryptoException.
I tried ALG_DES_CBC_ISO9797_M1, ALG_DES_CBC_ISO9797_M2 (and others) and got the same exception.
How to make run simple example of cipher on Java Card Connected?
UPDATE
As #vojta said, key must be 8 bytes long. So it must be something like this:
byte[] keyBytes = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04};
I don't know why, but it works only if replace
Cipher cipher = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, true);
with
Cipher cipher = Cipher.getInstance(Cipher.ALG_DES_CBC_ISO9797_M2, false);
I could not find anything about it in documentation.
These lines seem to be wrong:
byte[] keyBytes = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04};
desKey.setKey(keyBytes, (short) 0);
DES key should be longer than 4 bytes, right? Standard DES key is 8 bytes long (with strength of 56 bits).
In addition to #vojta's answer, the input data should be block aligned.
Your input data "test".getBytes() have length 4 which is not valid for Cipher.ALG_DES_CBC_NOPAD (but valid for Cipher.ALG_DES_CBC_ISO9797_M2).
Strange is that this should cause CryptoException.ILLEGAL_USE reason (which is 5 opposed to 3 you are getting)...

AES256 on Java vs PHP

Quick one that's thus far been evading me (long night). I'm comparing AES256 in PHP vs Java and noticing discrepancies. Please for simplicity ignore the ascii key and the null IV, those will be replaced in production. But I need to get past this first and can't figure out where I am erring:
PHP:
echo base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
"1234567890ABCDEF1234567890ABCDEF",
"This is a test",
MCRYPT_MODE_CBC,
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
)
);
Java
byte[] key = "1234567890ABCDEF1234567890ABCDEF".getBytes("UTF-8");
byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec newKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
byte[] results = cipher.doFinal("This is a test".getBytes("UTF-8"));
return Base64.encodeToString(results,Base64.DEFAULT);
PHP output: 0KwK+eubMErzDaPU1+mwTQ==
Java output: DEKGJDo3JPtk48tPgCVN3Q==
Not quite what I was expecting o_O !
I've also tried MCRYPT_MODE_CBC, MCRYPT_MODE_CFB, MCRYPT_MODE_ECB, MCRYPT_MODE_NOFB, etc.. none of them produced the Java string.
PHP pads the input bytes with \0 to make it a multiple of the block size. The equivalent in Java would be this (assuming the string you want to encrypt is in data):
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] inputBytes = data.getBytes();
int byteLength = inputBytes.length;
if (byteLength % blockSize != 0) {
byteLength = byteLength + (blockSize - (byteLength % blockSize));
}
byte[] paddedBytes = new byte[byteLength];
System.arraycopy(inputBytes, 0, paddedBytes, 0, inputBytes.length);
cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
byte[] results = cipher.doFinal(paddedBytes);
As a warning to this - zero-based padding is not desired. There's no way to determine the difference between \0 characters at the end of your string, and the actual padding. It's better to use PKCS5Padding instead, but you will get different results in PHP. Ask yourself if you NEED the encryption cross-platform like this.

why decryption fails for certain data only

I used sample code provided on this site by user. I need to encrypt password and store in file for future use. When user try to use system, I fetch password from file, decrypt it and use it for further processing. So I can't use different key during each encryption/decryption request. So I use fixed byte[] to store key instead of calling KeyGenerator.generateKey(). Below is full code.
public class App
{
static byte[] seckey=null;
static
{
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
// Generate the secret key specs.
// SecretKey skey = kgen.generateKey();
// seckey = skey.getEncoded();
// above won't work as can't generate new secret key for decrypt. Have to use same key for encrypt and decrypt
// seckey = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
seckey = new byte[]{(byte)172,(byte)236,(byte)125,(byte)222,(byte)188,(byte)33,(byte)210,(byte)4,(byte)202,(byte)31,(byte)188,(byte)152,(byte)220,(byte)104,(byte)62,(byte)64};
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
}
public static void main( String[] args )
{
String password = encrypt("A123456"); //working
System.out.println(password);
System.out.println(decrypt(password));
String password = encrypt("A*501717"); //NOT working
System.out.println(password);
System.out.println(decrypt(password));
}
public static String encrypt(String passwd)
{
SecretKeySpec key = new SecretKeySpec(seckey, "AES");
byte[] output;
try
{
Cipher cipher = Cipher.getInstance("AES");
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
output = cipher.doFinal(passwd.getBytes());
} catch (Exception e)
{
System.out.println("Unable to encrypt password.");
output = "".getBytes();
}
return new String(output);
}
public static String decrypt(String passwd)
{
if (!StringUtils.isNotBlank(passwd))
return "";
SecretKeySpec key = new SecretKeySpec(seckey, "AES");
byte[] output;
try
{
Cipher cipher = Cipher.getInstance("AES");
// decryption pass
cipher.init(Cipher.DECRYPT_MODE, key);
output = cipher.doFinal(passwd.getBytes());
} catch (Exception e)
{
System.out.println("Unable to decrypt password");
output = "".getBytes();
}
return new String(output);
}
}
The issue is, it works for most of the time but for certain character sequence it is faling to decrypt. e.g. presently not working for A123456. Then I changed secret key to below
seckey = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
after that it worked for A123456 but then failed for A*3qwe (which worked with earlier secret key. So I am totally not able to understand why is it not working for only some data?
Could someone help me please where am I doing wrong?
I resolved this issue by using Base64Encoder. Basically encrypted data can have some characters not possible to store in normal txt file and so decryption will fail. So I used Base64Encoder to encode encrypted string. This encoded characters are normal ascii characters and can be easily stored in txt file. Reverse thing is required to get original data i.e. first decode and then decrypt the string stored in file.

3DES/DES encryption using the JCE - generating an acceptable key

I'm working on a project that requires 3DES encryption in Java. The issue is that I've been (and will continue to be) supplied with a 128-bit hex key like "0123456789ABCDEF0123456789ABCDEF". Conversion to bytes is no issue. What is the issue, however, is that the Java Cryptographic Extensions API will choke on this key, saying it is invalid. I gather that the MSB of each byte is merely a parity bit, so the JCE expects me to remove those (or so I think). In .NET, however, I can specify the key as supplied, and it quietly handles the encryption/decryption with no complaints.
Is there any way I can generate the kind of key the JCE expects from the kind of key I'm supplied?
I've found that the JCE allows you specify an 8-byte key for DES encryption, so I tried implementing 3DES as DES EDE using half of the supplied key. However, I'm still getting inconsistent results with .NET.
Here's the Java code:
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class Main{
public static void main(String[] args) throws Exception {
byte [] plain = "I eat fish every day".getBytes("utf-8");
byte [] keyBytes = new byte [] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
};
byte [] key2Bytes = new byte [] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0 }; // actual keys replaced with dummies.
SecretKey keySpec = new SecretKeySpec(keyBytes, "DES");
SecretKey keySpec2 = new SecretKeySpec(key2Bytes, "DES");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher e_cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
e_cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec2, iv);
byte [] cipherText = e_cipher.doFinal(plain);
cipherText = cipher.doFinal(cipherText);
cipherText = e_cipher.doFinal(cipherText);
System.out.println("Ciphertext: " + new sun.misc.BASE64Encoder().encode(cipherText));
}
}
and here's the .NET code:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace EncryptionDemo
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
// TODO: Implement Functionality Here
var plainBytes = Encoding.UTF8.GetBytes("I eat fish every day");
var keyBytes = new byte [] { 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
var tripleDES = TripleDESCryptoServiceProvider.Create();
var transform = tripleDES.CreateEncryptor(keyBytes, new byte [8]);
var memStream = new MemoryStream();
var cStream = new CryptoStream(memStream, transform, CryptoStreamMode.Write);
cStream.Write(plainBytes, 0, plainBytes.Length);
cStream.FlushFinalBlock();
//memStream.Position = 0;
var cipherBytes = memStream.ToArray();
Console.WriteLine("Ciphertext: " + Convert.ToBase64String(cipherBytes));
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
Both produce different outputs (some characters in the Base64 string are the same)
3DES keys are 192 bits long.
How are you creating the SecretKey instance? What error message to you get?
The Java code in your question is using DES, not "Triple DES". The algorithm name should be "DESede/CBC/PKCS5Padding". The code in your answer probably works because you got the algorithm right, not because you switched providers. The SunJCE provider in Java 6 will accept 128-bit keys (and use keying option 2). I am not sure about older versions.
the Sun provider doesn't accept 16-byte 3DES keys, but the BouncyCastle provider does. I just tried it out and it works like a charm - it produces the same output as the .NET code!
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class Main{
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte [] plain = "I eat fish every day".getBytes("utf-8");
byte [] keyBytes = new byte [] { (byte) 0xC1, (byte) 0x57, (byte) 0x45, (byte) 0x08,
(byte) 0x85, (byte) 0x02, (byte) 0xB0, (byte) 0xD3,
(byte) 0xA2, (byte) 0xEF, (byte) 0x68, (byte) 0x43,
(byte) 0x5E, (byte) 0xE6, (byte) 0xD0, (byte) 0x75 };
SecretKey keySpec = new SecretKeySpec(keyBytes, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher e_cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding", "BC");
e_cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte [] cipherText = e_cipher.doFinal(plain);
System.out.println("Ciphertext: " + new sun.misc.BASE64Encoder().encode(cipherText));
}
}
In the jPOS project, the problem is worked around by always using either single-length (8-byte) or triple-length (24-byte) keys. Let's say your clear double-length key (in bytes) is AAAAAAAA BBBBBBBB. All code in the jPOS project I've seen so far that uses the JCE appends the first 8 bytes again to the clear key, so it becomes a triple-length key as such: AAAAAAAA BBBBBBBB AAAAAAAA. It seems the Sun provider does accept this material for creating a SecreKeySpec, as it is 192 bits long, as #erickson mentioned.

Categories

Resources