1

There is a server which encrypt password on C# (method encryptCSharp), android app receives encrypted password, salt, passPhrase and initVector. I have to decrypt that password in Java. Server-guy sent me an encrypt method in C# and I need to created encryptJava and decryptJava methods which works the same in Java as in C#. To create PasswordDeriveBytes which absent in Java I use an example from here Encryption Diff Between Java and C#

So, my question is, what's going wrong with my Java methods? Both didn't work

Update: I made some changes in snippets and now all works!!

I invoke that methods:

String encryptedText = encryptJava("12345", "100", "@.erf.net34", "@gugnet@gugnet77");//it works!!
String decryptedText = decryptJava(encryptedText, "100", "@.erf.net34", "@gugnet@gugnet77");//it doesn't work!!

Here are Java methods that I used and C# method from server.

C# (which I can't change)

public static String encryptCSharp(String plainText, String saltValue, String passPhrase, String initVector) {
    String hashAlgorithm = "SHA1";
    int passwordIterations = 1;
    int keySize = 256;
    // Convert strings into byte arrays.
    // Let us assume that strings only contain ASCII codes.
    // If strings include Unicode characters, use Unicode, UTF7, or UTF8
    // encoding.
    byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

    // Convert our plaintext into a byte array.
    // Let us assume that plaintext contains UTF8-encoded characters.
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

    // First, we must create a password, from which the key will be derived.
    // This password will be generated from the specified passphrase and
    // salt value. The password will be created using the specified hash
    // algorithm. Password creation can be done in several iterations.
    PasswordDeriveBytes password = new PasswordDeriveBytes(
            passPhrase,
            saltValueBytes,
            hashAlgorithm,
            passwordIterations);

    // Use the password to generate pseudo-random bytes for the encryption
    // key. Specify the size of the key in bytes (instead of bits).
    byte[] keyBytes = password.GetBytes(keySize / 8);

    // Create uninitialized Rijndael encryption object.
    RijndaelManaged symmetricKey = new RijndaelManaged();

    // It is reasonable to set encryption mode to Cipher Block Chaining
    // (CBC). Use default options for other symmetric key parameters.
    symmetricKey.Mode = CipherMode.CBC;

    // Generate encryptor from the existing key bytes and initialization
    // vector. Key size will be defined based on the number of the key
    // bytes.
    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
            keyBytes,
            initVectorBytes);

    // Define memory stream which will be used to hold encrypted data.
    MemoryStream memoryStream = new MemoryStream();

    // Define cryptographic stream (always use Write mode for encryption).
    CryptoStream cryptoStream = new CryptoStream(memoryStream,
            encryptor,
            CryptoStreamMode.Write);
    // Start encrypting.
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

    // Finish encrypting.
    cryptoStream.FlushFinalBlock();

    // Convert our encrypted data from a memory stream into a byte array.
    byte[] cipherTextBytes = memoryStream.ToArray();

    // Close both streams.
    memoryStream.Close();
    cryptoStream.Close();

    // Convert encrypted data into a base64-encoded string.

    String cipherText = Convert.ToBase64String(cipherTextBytes);

    // Return encrypted string.
    return cipherText;
}

Java to encrypt message, it works and I got the same result as method in C#

private String encryptJava(String plainText, String saltValue, String passPhrase, String initVector) {//working!!!
    String result = "";

    byte[] initVectorBytes = initVector.getBytes(US_ASCII);
    byte[] saltValueBytes = saltValue.getBytes(US_ASCII);
    byte[] plainTextBytes = plainText.getBytes(UTF_8);

    Cipher cipher;
    try {
        final com.gmail.example.PasswordDeriveBytes password = new com.gmail.example.PasswordDeriveBytes(passPhrase, saltValueBytes);
        final byte[] keyBytes = password.getBytes(256 / Byte.SIZE);
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");

        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        try {
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initVectorBytes));
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }

        final byte[] ct = cipher.doFinal(plainTextBytes);
        result = Base64.encodeToString(ct, Base64.DEFAULT);//**added this line!** 
        //result = new String(ct, "US-ASCII");**-- deleted this line!** 
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    }
    return result;
}

Java method that emulate the same method in C#

public class PasswordDeriveBytes {

    private final MessageDigest hash;

    private final byte[] firstToLastDigest;
    private final byte[] outputBuffer;

    private int position = 0;

    public PasswordDeriveBytes(String password, byte[] salt) {
        try {
            this.hash = MessageDigest.getInstance("SHA-1");

            this.hash.update(password.getBytes(UTF_8));
            this.hash.update(salt);
            this.firstToLastDigest = this.hash.digest();

            final int iterations = 1;//**changed from 100**
            for (int i = 1; i < iterations - 1; i++) {
                hash.update(firstToLastDigest);
                hash.digest(firstToLastDigest, 0, firstToLastDigest.length);
            }

            this.outputBuffer = hash.digest(firstToLastDigest);

        } catch (NoSuchAlgorithmException | DigestException e) {
            throw new IllegalStateException("SHA-1 digest should always be available", e);
        }
    }

    public byte[] getBytes(int requested) {
        if (requested < 1) {
            throw new IllegalArgumentException(
                    "You should at least request 1 byte");
        }

        byte[] result = new byte[requested];

        int generated = 0;

        try {
            while (generated < requested) {
                final int outputOffset = position % outputBuffer.length;
                if (outputOffset == 0 && position != 0) {
                    final String counter = String.valueOf(position / outputBuffer.length);
                    hash.update(counter.getBytes(US_ASCII));
                    hash.update(firstToLastDigest);
                    hash.digest(outputBuffer, 0, outputBuffer.length);
                }

                final int left = outputBuffer.length - outputOffset;
                final int required = requested - generated;
                final int copy = Math.min(left, required);

                System.arraycopy(outputBuffer, outputOffset, result, generated, copy);

                generated += copy;
                position += copy;
            }
        } catch (final DigestException e) {
            throw new IllegalStateException(e);
        }
        return result;
    }
}

and finally Java method that doesn't work and try to understand what I do wrong

private String decryptJava(String encryptedText, String saltValue, String passPhrase, String initVector) {
    String result = "";
    byte[] initVectorBytes = initVector.getBytes(US_ASCII);
    byte[] saltValueBytes = saltValue.getBytes(US_ASCII);
    byte[] encryptedTexttBytes = Base64.decode(encryptedText, Base64.DEFAULT);
    Cipher cipher;
    try {
        final PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes);
        final byte[] keyBytes = password.getBytes(256 / Byte.SIZE);
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");

        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        try {
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(initVectorBytes));
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        final byte[] ct = cipher.doFinal(encryptedTexttBytes);
        //result = Base64.encodeToString(ct, Base64.DEFAULT); - **deleted this line**
        try {
            result = new String(ct, "US-ASCII");//** added this line**
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    }
    return result;
}
0

2 Answers 2

1

With PasswordDeriveBytes never ever ever ask for more bytes than the underlying hash function. This function implements PBKDF1 which cannot output more than that number of bits (160 for SHA-1 in your example).

The Microsoft implementation allows more output, but the implementation is broken to the extreme (it may even repeat output!). Use Rfc2898DeriveBytes instead which implements PBKDF2, also available in Java. Use a larger hash, PBKDF2 can generate more than the output of the hash as bytes, but only at the cost of security.

Sign up to request clarification or add additional context in comments.

2 Comments

I am not experienced in cryptography, so I try to understand your answer. Do you mean that the guy who works with microsoft server has to change method from PasswordDeriveBytes to Rfc2898DeriveBytes?
Yes, that would certainly be advisable. PasswordDeriveBytes has been superseeded by Rfc2898DeriveBytes. To my knowledge nobody has fully described the extended algorithm that the earlier one uses (it probably includes some horrendous buffer overruns).
1

Finally I found a solution for encoding and deconding method: (so that to not increase the amount of code here, I made changes in snippets above)

in encryptJava method I changed one line,

in PasswordDeriveBytes I changed iterations from 100 to 1

in decryptJava method I added one line and deleted one line.

2 Comments

Glad you got it working, but please note that by using PasswordDeriveBytes and 1 as iteration count, you actually did not make it secure, which I hope is what you are after.
@MaartenBodewes, server encrypt method is out of my reach but I will show your notes to server guy to fix that. Even though I found myself the answer on this particular question, it would be impassible to solve it without your method for Java - PasswordDeriveBytes. Thanks a lot for your answers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.