4

Before you mark this as a duplicate, please read the full question.

I've looked through countless questions here about this problem, and every answer said to install JCE. However, if I want to send the program to someone else, another computer, virtually anything off the development computer, they have to install JCE too.

Is there a way I can use a smaller keysize without having to install anything?

My encryption method;

public static String encrypt(String in) throws NoSuchAlgorithmException,
   NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
   IllegalBlockSizeException, BadPaddingException, IOException {

    String out = " ";

    // generate a key
    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    keygen.init(128);
    byte[] key = keygen.generateKey().getEncoded();
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

    // build the initialization vector
    SecureRandom random = new SecureRandom();
    byte iv[] = new byte[16]; //generate random 16 byte IV. AES is always 16bytes
    random.nextBytes(iv);
    IvParameterSpec ivspec = new IvParameterSpec(iv);

    saveKey(key, iv); //<-- save to file

    // initialize the cipher for encrypt mode
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);

    byte[] encrypted = cipher.doFinal(in.getBytes());

    out = asHex(encrypted);

    return out;
}

And my decrypt method:

public static String decrypt(String in) throws NoSuchAlgorithmException,
  NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
  IllegalBlockSizeException, BadPaddingException, IOException, KeyFileNotFoundException, UnknownKeyException {

    String out = " ";

    byte[] key = readKey("key").clone(); //<--from file
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

    byte[] iv = readKey("iv"); //<-- from file
    IvParameterSpec ivspec = new IvParameterSpec(iv);

    //initialize the cipher for decryption
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);

    // decrypt the message
    byte[] decrypted = cipher.doFinal(in.getBytes());

    out = asHex(decrypted);

    return out;
}

My saveKey() method:

private static void saveKey(byte[] key, byte[] iv) throws FileNotFoundException, IOException {

    File keyFile = new File(Logging.getCurrentDir() + "\\cikey.key");

    keys.setProperty("key", asHex(key));
    keys.setProperty("iv", asHex(iv));

    keys.store(new FileOutputStream(keyFile.getAbsolutePath(), false), null);
}

My readKey() method:

 private static byte[] readKey(String request) throws KeyFileNotFoundException, UnknownKeyException, FileNotFoundException, IOException {

    File keyFile = new File(Logging.getCurrentDir() + "\\cikey.key");
    byte[] storage;

    keys.load(new FileInputStream(keyFile));

    if (!keyFile.exists())
        throw new KeyFileNotFoundException("Key file not located.");

    if (keys.containsKey(request) == false)
        throw new UnknownKeyException("Key not found.");
    else
        storage = keys.getProperty(request).getBytes();

    return storage;
}

asHex() method (transferring array to String):

public static String asHex(byte buf[]) {

    StringBuilder strbuf = new StringBuilder(buf.length * 2);

    for (int i = 0; i < buf.length; i++) {
        if (((int) buf[i] & 0xff) < 0x10)
            strbuf.append("0");

        strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
    }
    return strbuf.toString();
}
Aaron
  • 992
  • 3
  • 15
  • 33
  • Its not clear why key size is relavent here. JCE is [bundled with the jre](http://stackoverflow.com/questions/15248199/does-java-cryptography-extensionsjce-come-bundled-with-the-jre), or is the issue that you need to use AES 256 (which requires the unlimited jurisdiction policy files)? Can you clarify please. – Syon Aug 29 '13 at 22:17
  • @Syon The problem has been narrowed down to "probably" the way I'm storing the keys. See Jk1's answer. – Aaron Aug 29 '13 at 22:20

1 Answers1

3

Is there a way I can use a smaller keysize without having to install anything?

You can't use AES with keys sizes smaller than 128 bit, but there are other ciphers available: DES, Blowfish, etc. They aren't as secure as AES, but still can do the trick if your application (as most apps do) does not worth complicated hacking effort. Here's an example for 56 bit DES:

 public static String encrypt(String in) throws Exception {
    String out = " ";
    // generate a key
    KeyGenerator keygen = KeyGenerator.getInstance("DES");
    keygen.init(56);
    byte[] key = keygen.generateKey().getEncoded();
    SecretKeySpec skeySpec = new SecretKeySpec(key, "DES");

    // build the initialization vector
    SecureRandom random = new SecureRandom();
    byte iv[] = new byte[8]; //generate random 8 byte IV. 
    random.nextBytes(iv);
    IvParameterSpec ivspec = new IvParameterSpec(iv);
    // initialize the cipher for encrypt mode
    Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);

    byte[] encrypted = cipher.doFinal(in.getBytes());

    out = asHex(encrypted);

    return out;
}

There is also a problem with storing and reading the keys in the code. You're storing them as hex, but reading as symbols from default platform encoding. Here's an example how to make both operations uniform:

private static void saveKey(byte[] key, byte[] iv) throws IOException {
    File keyFile = new File("C:/cikey.key");
    keys.setProperty("key", toHexString(key));
    keys.setProperty("iv", toHexString(iv));
    keys.store(new FileOutputStream(keyFile.getAbsolutePath(), false), null);
}

private static byte[] readKey(String request) throws IOException {
    File keyFile = new File("C:/cikey.key");
    keys.load(new FileInputStream(keyFile));
    return toByteArray(keys.getProperty(request));
}

public static String toHexString(byte[] array) {
    return DatatypeConverter.printHexBinary(array);
}

public static byte[] toByteArray(String s) {
    return DatatypeConverter.parseHexBinary(s);
}
Jk1
  • 11,233
  • 9
  • 54
  • 64
  • I've changed everything to how you have it, including some items in my `decrypt()` method to match the `DES` as you've used. However, I now get there error: `java.security.InvalidKeyException: Invalid key length: 16 bytes` using: `cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);` **EDIT:** if it helps, I've edited my question to provide my `readKey()` method. – Aaron Aug 29 '13 at 22:01
  • Why you're using a key of 16 bytes for decrypting? That is not a legal key size for DES. You should use either 7 byte (56 bit) for key as in my example above, or 5 byte (40 bit) key. Given you are only getting an error on decryption (encryption is fine) I suppose you have a bug in saving\restoring keys. – Jk1 Aug 29 '13 at 22:06
  • I've edited my question with my read/saving method. When I tried storing the keys using `toString()` instead of a custom method, the size was shortened to 11 instead of 16. It might be in that method, but I don't really know too much about how it works to mess with it. – Aaron Aug 29 '13 at 22:14
  • Thanks, now it's clear where the problem is. Please refer to the updated answer for explanation. – Jk1 Aug 29 '13 at 22:25
  • Fixed the problem -- now I have `javax.crypto.BadPaddingException: Given final block not properly padded`. Error source is `byte[] decrypted = cipher.doFinal(in.getBytes());` – Aaron Aug 29 '13 at 22:34
  • Ok, now we need to take a look on how encrypted data is stored and read. Bad padding exception on decryption typicaly indicates damaged encrypted data. – Jk1 Aug 29 '13 at 22:38
  • And here we have the same error: data is stored as hex, but restore using getBytes(...). Try the same fix for data as you did for the key. – Jk1 Aug 29 '13 at 22:44
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/36494/discussion-between-aaron-and-jk1) – Aaron Aug 29 '13 at 23:11
  • DES should not be used for **anything**. The whole 56-bit keyspace of DES can be searched through in 20 hours with current hardware. Consider Moore's law and it's practically equivalent to double ROT13. There are much better alternatives available. – ntoskrnl Aug 30 '13 at 04:58
  • @ntoskrnl It doesn't matter what I'm using since it's not a commercial application. And secondly, it can be used when the application is not worth the hacking effort (as mine certainly isn't). – Aaron Aug 30 '13 at 14:02
  • @Aaron If your application doesn't need security, why do you bother with encryption in the first place? – ntoskrnl Aug 30 '13 at 15:36
  • @Aaron, just in case you're not looking into a chat, here's the corrected code: pastebin.com/vXcHezW3 – Jk1 Aug 30 '13 at 17:11
  • @ntoskrnl because when I write an application for my own use and I want to secure my data from family / friends reading it, I might want to encrypt it. – Aaron Aug 30 '13 at 23:51
  • @Aaron In that case you could probably get away with something like Base64. Either way, I don't think using AES takes any more effort than using DES – just replace all instances of "DES" with "AES" and you will have uncrackable "military grade" encryption. – ntoskrnl Aug 31 '13 at 08:42