3

I have an encrypted mp4 using Rijndael and I am decrypting in C# in the following manner.

System.Security.Cryptography.Rijndael crypt = System.Security.Cryptography.Rijndael.Create();

crypt.Key = convertedSecureString;

byte[] initializationVectorLength = new byte[sizeof(int)];
CryptoStream cryptostream = new CryptoStream(inputStream, crypt.CreateDecryptor(), CryptoStreamMode.Read);
byte[] buffer = new byte[1024];
int len;
while ((len = cryptostream.Read(buffer, 0, buffer.Length)) > 0)
{
    outputStream.Write(buffer, 0, len);
    buffer = new byte[1024];
}

outputStream.Flush();
cryptostream.Close();
inputStream.Close();

Now I need to convert this code to Java/Android equivalent. I am not sure where to start frankly. I am confused by so many options - some say use Bouncy Castle, some say Apache Commons, some the native Java lib. How do I do this. And what do I do about CryptoStream etc?

UPDATE

I am using the following the code in C# for assigning the key

byte[] convertedSecureString = new byte[this.key.Length];
IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(this.key);

for (int i = 0, j = 0; i < this.key.Length * UnicodeByteLength; i = i + UnicodeByteLength, j++)
{
    convertedSecureString[j] = System.Runtime.InteropServices.Marshal.ReadByte(ptr, i);
}

try
{
    crypt.Key = convertedSecureString;
}

where key is secure. I have the equivalent unsecure key in Java. How do i convert this piece of code to Java

UPDATE

  Rfc2898DeriveBytes newKey = new Rfc2898DeriveBytes(crypt.Key.ToString(), crypt.IV);
                Array.Copy(newKey.GetBytes((int)crypt.KeySize / 8), crypt.Key, (int)crypt.KeySize / 8);
                Array.Copy(newKey.GetBytes((int)crypt.BlockSize / 8), crypt.IV, (int)crypt.BlockSize / 8);

I am using this in C# to mod the key using Rfc 2898 and derive the bytes - I cant find an equivalent in Java - I found here Java equivalent of C#'s Rfc2898DerivedBytes in the second comment - but what values do I give for iterator and dklen?

Community
  • 1
  • 1
Vrashabh Irde
  • 14,129
  • 6
  • 51
  • 103

2 Answers2

6

You need to get Cipher object. Here is one way of getting it, using byte[] aesKey, and byte[] iv (initialization vector, must always be 16 bytes for AES).

// aesKey is 128, 196 or 256-bit key (8, 12 or 16 byte array)
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");

// initialization vector
IvParameterSpec ivSpec = new IvParameterSpec(iv);

// create and initialize cipher object
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

Once you have Cipher object in decrypt mode, you can feed it with encrypted data using update methods, and it will return you plain-text data. When you are done, you must call one of doFinal methods to get final block.

Alternatively, you can create CipherInputStream using your Cipher object, and original input stream that supplies encrypted data. You read data from CipherInputStream, which in turn reads data from original input stream, decrypts it, and returns you the plain-text data.

For encrypting, you need to pass Cipher.ENCRYPT_MODE into Cipher.init method, and use CipherOutputStream instead.

Update: full example, more or less equivalent to original C# code:

// Algorithm, mode and padding must match encryption.
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");

// If you have Bouncycastle library installed, you can use
// Rijndael/CBC/PKCS7PADDING directly.
Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7PADDING");

// convertedSecureString and initVector must be byte[] with correct length
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(convertedSecureString, "AES"),
    new IvParameterSpec(initVector));

CipherInputStream cryptoStream = new CipherInputStream(inputStream, cipher);
byte[] buffer = new byte[1024];
int len = cryptoStream.read(buffer, 0, buffer.length);
while (len > 0) {
    outputStream.write(buffer, 0, len);
    len = cryptoStream.read(buffer, 0, buffer.length);
}

outputStream.flush();
cryptoStream.close();
// no need to close inputStream here, as cryptoStream will close it

By default, Java doesn't support Rijndael algorithm (AES may or may not work, see the other answer) and PKCS7 padding. You will need to install Bouncycastle extension for that and then just use Cipher.getInstance("Rijndael/CBC/PKCS7PADDING");. Why CBC and PKCS7 Padding? Those seem to be defaults for System.Security.Cryptography.Rijndael class. If I got that wrong, use correct mode and padding for your situation.

Peter Štibraný
  • 32,463
  • 16
  • 90
  • 116
  • Thanks! But I am still not sure how to proceed - is what are you showing me in your comment above the equivalent to what I am doing in the C# code. I am not sure how to convert the encrypted data file to a stream, decrypt the bits and write it back to a file again – Vrashabh Irde Apr 11 '12 at 05:27
  • @Vrashabh: I've updated my answer with equivalent code in Java. – Peter Štibraný Apr 11 '12 at 07:26
  • Thanks for your help! I will try it and do a little more research and come back with either questions or to mark as right answer :) – Vrashabh Irde Apr 11 '12 at 08:09
  • I have edited the question to include an update - from your example I am generating the key everytime using the marshalling code in C# from a securestring. So SecureKey->BSTR->Marshall to 32 bytes and assign to key. I have the unsecure encoded key in Java = how do I do the same operation to have a 32 byte key for the decryption? – Vrashabh Irde Apr 11 '12 at 12:18
  • @Vrashabh: there is no such thing as "SecureString" in Java. What exactly do you have when you say "unsecure encoded key", maybe I can help? What type is it? Instance of String class? If so, you need to convert it to byte array using `String.getBytes("UTF-8")` (or some other encoding). When you have byte array, you can create a `SecretKeySpec` as shown in the code. – Peter Štibraný Apr 11 '12 at 18:56
  • Yes i am trying UTF-16LE because of C#,JAVA difference- but now encountering an NoSuchAlgorithmException even before that - at "Rijndael/CBC/PKCS7PADDING" - I tried with both installing and then removing bouncy castle - it gives me the same error – Vrashabh Irde Apr 12 '12 at 06:07
  • @Vrashabh: I've just tried it here. What has worked for me was specifying "Rijndael" only, without mode and padding (with Bouncycastle installed). That creates Cipher with Rijndael algorithm, using ECB mode and PKCS7 padding. I suppose ECB is not what you want as you have initialization vector (ECB doesn't need it). It seems to me that you will need to use Bouncycastle API instead of standard Java API to configure Rijndael with your configuration. You can also try to specify "AES" instead of Rijndael. AES is essentially Rijndael with some restrictions for key sizes and block size. – Peter Štibraný Apr 12 '12 at 08:05
  • I have updated the question - can you please tell me about the equivalents for RFC2898 if you are aware it – Vrashabh Irde Apr 13 '12 at 11:04
  • @Vrashabh: I've created custom implementation of "Password-based Key Derivation Function 2" (PBKDF2) function for our purposes. According to the documentation of C# Rfc2898DeriveBytes class, default iteration count is 1000 (also recommended value by RFC). `dkLen` is number of bytes that PBKDF2 function generates. In your case, `dkLen` should be `(int)crypt.KeySize / 8 + (int)crypt.BlockSize / 8`, because you're using this many bytes. Unlike `Rfc2898DeriveBytes.GetBytes` method, `PBKDF2.deriveKey` from that answer will give you all bytes in one byte array and you need to split it on your own. – Peter Štibraný Apr 13 '12 at 11:33
  • Using PBKDF2 for 1000 takes a really really long time - the link I ve mentioned - does the number of iterations matter? – Vrashabh Irde Apr 14 '12 at 06:24
  • Or is there a faster bouncy castle version I could use - because the time the above implementation takes is enormous and I dont have enough time to implement my own PBKDF2 – Vrashabh Irde Apr 14 '12 at 06:26
  • Higher the iteration count is, more time attacker needs to verify if password is correct or not. If 1000 is too much to handle, feel free to reduce it, but make sure that you use same iteration count everywhere. I would not use this techinque for generating Initialization vector -- that would reduce number of bytes that PBKDF2 needs to generate. It also generates same IV each time, that kind of defeats the point of IV. – Peter Štibraný Apr 14 '12 at 09:57
  • Sorry to bother you - I ve gotten it to work completely (thank you!), but now this `int len = cryptoStream.read(buffer, 0, buffer.length); while (len > 0) { outputStream.write(buffer, 0, len); len = cryptoStream.read(buffer, 0, buffer.length); }` takes a huge amount of time to run. I am reading the encrypted file from sdcard and putting the decrypted one in the /data/app path. Is there any way I can use some other mechanism like mappedbytebuffer to speed this op up? – Vrashabh Irde Apr 16 '12 at 09:27
  • @Vrashabh: I don't think you can speed it up by much, but you can try to do some profiling to see where the bottlenecks are. Maybe it is something you can fix. – Peter Štibraný Apr 16 '12 at 09:42
1

Don't worry about the implementation in C#. Go through the below link for encrypting/decrypting for Java Language.RjIndeal Implementation in Java

Community
  • 1
  • 1
Phani
  • 5,319
  • 6
  • 35
  • 43