4

I am integrating with a .Net application which uses Symmetric encryption. My application is on Java. Following is the .Net code which was shared by the team which uses the Symmetric encryption;

public static string SymmetricEncrypt<T>(string value, string PrivateKey, string SALT_STRING) where T : SymmetricAlgorithm, new()
    {

 PasswordDeriveBytes rgb = 
 new PasswordDeriveBytes(PrivateKey ,      
Encoding.Unicode.GetBytes(SALT_STRING));

        SymmetricAlgorithm algorithm = new T();

        byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
        byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);

        ICryptoTransform transform = algorithm.CreateEncryptor(rgbKey, rgbIV);

        using (MemoryStream buffer = new MemoryStream())
        {
  using (CryptoStream stream = 
                new CryptoStream(buffer, transform, CryptoStreamMode.Write))
            {
using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
                {
                    writer.Write(value);
                }
            }

            return Convert.ToBase64String(buffer.ToArray());
        }
    }

Looking at the .Net documentation, I could see that SymmetricEncrypt class by default uses Rijndael encryption. And also I have found a class similar to "PasswordDeriveBytes" implementation in bouncy castle which implements the "PKCS5S1" algorithm for key generation using the salt with an iteration count of 100. But still I am unable to generate the exact encryption to what is required by the .Net application. Following code is what i have tried so far;

 import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import mtnsa.sorb.handler.PKCS5Test.PasswordDeriveBytes;

import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Base64;

public class EncryptionHelper {



    public static void main(String[] args)throws Exception {
        encrypt("FTTH","HhN01vcEEtMmwdNFliM8QYg0Y89xzBOJJG7BHARC7g", "002400000480000094000000060200000024000052534131000400000100010085525e9438e9fae122f71ec7124" +
                "443bf2f9f57f5f3760b3704df168493004b9ef68413f500d54fa9fa3869b42b1e2365204826e54b618d56e7e575f2" +
                "7f675f0eae3ea8458a8ee1e92dc3f4bfc34fbe23851afa9d2c28fc8cd5b124f60a03a06bfb598bc3acbd8c4380ae" +
                "f02cc58bdf955d140390f740a7e115c59e3b3b5758ca");
    }

    public static String encrypt(final String valueToEncrpt, final String saltString, final String privateKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
        String encrptedValue = null;

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

        byte[] password = privateKey.getBytes();
        byte[] salt = saltString.getBytes();

        PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
        generator.init(password, salt, 100);

        byte[] keyArr = ((KeyParameter)generator.generateDerivedParameters(128)).getKey();
        byte[] IvArr = ((KeyParameter)generator.generateDerivedParameters(128)).getKey();



        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyArr,"AES"),new IvParameterSpec(IvArr));
        byte[]test  = cipher.doFinal(valueToEncrpt.getBytes());

        System.out.println(new String(Base64.encode(test)));

        return encrptedValue;
    }
}

In the Java code i have given the sample SALT key and the PRIVATE key used for testing purposes. But still I am unable to get the exact value as generated by the .Net application. Following is an example plain text and the encrypted value which i got from the .Net application which i have failed to replicate in my Java code.

Plain text value : FTTH

Encrypted value : MjgmbdT3Vg6RW/7K1BjQ/Q==

Any help on this is much appreciated as i am currently out of ideas.

dinukadev
  • 2,279
  • 17
  • 22
  • How did you call the .NET encryption function ? What is the algorithm & key size ? – Eledra Nguyen May 18 '15 at 08:03
  • @EledraNguyen i did not call the .Net function. I was given the .Net code saying this is what they use in the .Net side. But i need to send the encryption values from my Java component. Algorithm is AES used from the .Net side as confirmed. – dinukadev May 18 '15 at 09:41
  • 1
    The problem is that the encrypt function in .NET does not specify the key length. Let say I call like this: SymmetricEncrypt("test", "test"), that means I am using AES 256, CBC, not 128 like in your Java code. Also, since they use PasswordDeriveBytes, you should read this: http://stackoverflow.com/questions/9231754/c-sharp-passwordderivebytes-confusion. I would like to try and help, but cannot without the specific call in .NET – Eledra Nguyen May 18 '15 at 09:50
  • PS: It is marked as Obsolete in the .NET document anyway – Eledra Nguyen May 18 '15 at 09:55
  • @EledraNguyen i just checked with the .Net component team and they use AesManaged as the implementation. The keysize was 32 and the block size was 16. – dinukadev May 18 '15 at 10:41
  • 1
    Then it will definitely not work in the conventional way. You can find the answer here: http://stackoverflow.com/questions/21480202/encryption-result-in-java-and-net-are-not-same However, in the long run, it is still better for the .NET team to switch to the newer https://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx – Eledra Nguyen May 18 '15 at 11:37
  • 1
    See it from the bright sight: as long as you don't send the IV you should still be secure. If you call `GetBytes` twice and over 20 bytes then the MS implementation of `PasswordDeriveBytes` *may repeat previously generated bytes*. There is also a difference by calling `GetBytes` once for `x + y` bytes and calling it twice as in `GetBytes(x)` *and* `GetBytes(y)`. Nice huh? Switch to `RFC2898DeriveBytes` now and don't forget to use only characters with a code point < 128 if you want to have Java compatibility. – Maarten Bodewes May 18 '15 at 14:05
  • 1
    You use `byte[] password = privateKey.getBytes();` Better not to do that, you need to specify the byte encoding used in the string: `byte[] password = privateKey.getBytes("utf-16");` or whatever. IIRC C# uses UTF-16. Similarly for `byte[] salt = saltString.getBytes();`. Your compiler should flag `String.getBytes()` as deprecated. – rossum May 18 '15 at 18:26
  • Thank you everyone for the valuable comments. I have requested the .Net module authors to change their implementation to use RFC2898DeriveBytes. – dinukadev May 20 '15 at 05:05

1 Answers1

1

Thank you everyone for the valuable comments. I have requested the .Net module authors to change their implementation to use RFC2898DeriveBytes

dinukadev
  • 2,279
  • 17
  • 22