1

Well this is actually a two-parter...

First I need to

  1. read the contents of the file
  2. crypt them into a byte[]
  3. write the byte[] in a file or whatever...

Then the result from #2 or #3 will go into another project. I'm trying to protect our PEM/DER keys.

For decryption, I need to

  1. read the contents of the crypted file as a byte[]
  2. decrypt them into a byte[]
  3. write the decrypted data to a file OR use it instead of a file

Now, I have some basic crypting code

        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    keyGenerator.init(128); // 192 and 256 bits may not be available

    SecretKey secretKey = keyGenerator.generateKey();

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");


    // By initializing the cipher in CBC mode, an "initialization vector" has been randomly
    // generated. This initialization vector will be necessary to decrypt the encrypted data.
    // It is safe to store the initialization vector in plain text for later use. You can obtain
    // it's bytes by calling iv.getIV().
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
        //      IvParameterSpec iv = new IvParameterSpec(IV); //used for the hardcoded one

        byte[] encryptedData = cipher.doFinal(data);

and decrypting one as well

    cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
    byte[] decryptedData = cipher.doFinal(encryptedData);
    System.out.println("decrypted: " + new String(decryptedData));

and the question is:

Given a use-case scenario where one would rarely encrypt something and would distribute crypted keys that are to be decrypted at runtime, what do I need to save apart from the cyphertext?

I know I need to save the IV, but when I did decryption wasn't quite good - which leads me to believe that I need to save the secretKey as well.

Could anyone give me any tips, pointers or general security hints to a better solution? If I need to save the key, the IV and the encrypted data, where should I store them? Maybe hardcode the key and store the IV along the encrypted data? Maybe hardcode both the IV and the key and just store encrypted data in the files?

This isn't about theoretical safety, think of this as the biggest nuissance and inconvenience you can cause to someone that is trying to steal your keys. We all know there's no way I can perfectly hide them.

I pretty much need what this guy started with Decrypting an encrypted file and executing in Java

However if there's a better way of feeding secure data into a PemKeyReader, i'm all ears.

Community
  • 1
  • 1
Shark
  • 6,513
  • 3
  • 28
  • 50
  • Have you considered password-based encryption ? – Rostislav Matl Aug 21 '12 at 10:59
  • That would imply storing a plaintext password somewhere... not really, but I'm listening. Ideally I'd like to use one file to crypt the other since they go in pair anyway. – Shark Aug 21 '12 at 11:02

2 Answers2

1

Sharing the key and encrypting something are two completely different things. How to share keys

Having said this, AES with 128bit is fairly strong encryption algorithm than 3DES So what you can do is keep PKI infrastructure in place to exchange AES keys and then Encrypt and Decrypt using them.

Why not RSA? RSA needs to be minimum 512 bit to consider it as strongest and if you increase more bits then it increases time required for encryption and decryption.

SO AES is fast and safe.

Use SecretKeySpec to create key from byte[]

public static void main(String[] args) throws Exception
{
    // Initialise secret key with predefined byte array [] like below. I
    // have used simple string to array method to generate 16 byte array.
    // AES Key must be minimum 16 bytes.
    // Now you can put this byte array some where is .SO file.
    // Generate new Key using this byte []
    // Then you can generate a key using device specific information at
    // first boot up.
    // Use second key to encrypt data and first key to encrypt the second
    // key
    // I Hope it clears all the doubts
    SecretKey key = new SecretKeySpec("ABCDEFGHIJKLMNOP".getBytes(), "AES");
    System.out.println(Arrays.toString(key.getEncoded()));
    // Initialise Cipher with AES Algorithm
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    // Set The Encrypt Mode
    cipher.init(Cipher.ENCRYPT_MODE, key);
    // Encrypt some bytes
    byte[] encrypted = cipher.doFinal("ABCDEFGH".getBytes());
    // Print it to vefiry
    System.out.println(Arrays.toString(encrypted));

    // Get The IV
    byte[] iv = cipher.getIV();
    System.out.println(iv.length);
    // Now why storing you can create structure like [16 IV][Encrypted Data]
    // And while decrypting you can read first [16] bytes IV and then
    // decrypt remaining bytes

    //byte[] iv = new byte[16];
    // System.arraycopy(encrypted, 0, iv, 0, 16)
    //Copy remaining bytes to decrypt


    // set cipher to decrypt mode

    cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(iv));

    // decrypt it
    byte[] decrypted = cipher.doFinal(encrypted);
    System.out.println(new String(decrypted));

}

Now write an algorithm which will generate byte[] from some random data like device name, user name, random seed etc.

You can add more protection to algorithm source code by writing that algorithm in C and create.SO file and get byte [] using Native calls.

What are the advantages of doing all this?

  1. Event if your so is hacked it will need real time environment to run create key out of it.
  2. Even if some one does crack it the damage will be limited i.e. 1 device
  3. Hacker will have to repeat same with each device which is highly impossible to do.
Amit Deshpande
  • 19,001
  • 4
  • 46
  • 72
  • I'm not 'sharing' keys; I'm distributing them with the production code. This all has to do with how to keep them safe in the APK. – Shark Aug 21 '12 at 11:35
  • 1
    If you distribute any key with the application it easy to crack vulnerable to attacks and it is not safe. The best case is device algorithm that will generate key based on some factors like Device Type, User Name, Model No etc and then use it for encryption also that file needs to be obfuscated so that no one will come to know the implementation easily – Amit Deshpande Aug 21 '12 at 11:45
  • So you are saying that the only secure thing to hardcode is the crypted data? Why not put everything into the .SO file? – Shark Aug 21 '12 at 12:32
  • Yes you can. But with java there is always a problem to deliver keys with code because of decompilation. – Amit Deshpande Aug 21 '12 at 12:40
  • If someone is determined enough, I know I cannot save the keys from him. But I might make it as inconvenient as possible to attempt such a thing. – Shark Aug 21 '12 at 12:46
  • Yes. But if data is highly sensitive then you just can not say to the user that it got hacked. So if it is less or moderate sensitive then in such case also it should be made as unreachable as you can – Amit Deshpande Aug 21 '12 at 12:53
  • Ok so how do I ensure that the newly generated keys will always decrypt the file? I suspect that it will have to be like hashCode() where it always returns the same output for the same input? Any tips on that one? The less I have to "store" in the APK/code the better, that's why i'm asking about this generateKey() method :) – Shark Aug 21 '12 at 12:59
  • 1
    AES is symmetric algorithm what it means is that you should decrypt with the same key from which you have encrypted. So I would advice keep default key encrypt data deliver application, on the first launch change the key to some random key write key and encrypted data somewhere. Also always encrypt key with default application key before writing to some location. Again this algorithm is vulnerable to attacks but as damage will be limited as per device key is changed. – Amit Deshpande Aug 21 '12 at 13:20
  • So would i need to subclass/implement the SecretKeySpec interface then to accomodate generation of similar keys each time? We're not really hitting the 'key' issue here :) – Shark Aug 21 '12 at 14:19
  • Great! I'm somewhat having trouble figuring out how to get the key that was used for encrypting. I realize that if i'm to generate it, it needs to be consistent - i hope your snippets will help :) – Shark Aug 21 '12 at 14:44
  • yes :) even though you're missing the part where the key has to be reinitialized before decrypting. – Shark Aug 22 '12 at 08:48
1

The I/O aspect of your question is best addressed by reading the "Byte Streams" and "Buffered Streams" sections of the Oracle Java tutorial. You can accumulate the bytes in memory by writing them to a ByteArrayOutputStream, and then using the toByteArray() method to get the bytes as a byte[].

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Hm, I like this as this allows me to hardcode a byte[]. What about hardcoding the IV and the secret key alongside it? Would that be safe? – Shark Aug 21 '12 at 11:34