Java Rfc2898DeriveBytes

I was creating Java port for one program of mine and I stumbled across little issue. Although C# and Java seem quite different, you can almost always rely on one-for-one feature compatibility. Of course I stumbled across one class that was not implemented in Java. That class was Rfc2898DeriveBytes (.NET PBKDF2 implementation).

To be totally correct, I did found quite a few classes that do implement RFC 2898 but they did not give same result as one I used in .NET. While those implementations were also correct ones, they did not ensure compatibility with my existing code.

.NET Reflector comes here as great debugging tool. Quick peek just discovered that core of .NET Rfc2898DeriveBytes class is HMAC SHA-1 algorithm. GetBytes method has some basic buffer management (data gets generated 20 bytes at time) and call to omnipotent Func method. It is in this method that real crypto-magic happens.

Fortunately, building blocks for this functionality is available to Java. Although syntax is somewhat different, general idea is same. Whole getBytes method needed only changes related to array copying. In .NET we would use Buffer.BlockCopy and in Java this translates perfectly to System.arraycopy. Really hard...

Crypto-core is hidden in function named "func". Notable spot here is incrementing block counter. In C# this is unsigned int but in Java there is no such thing. That is reason for one extra check.

if (this._block == 2147483647) {
this._block = -2147483648;
} else {
this._block += 1;
}

With these few changes done our Java implementation of Rfc2898DeriveBytes was done. Source code can be downloaded here.

6 thoughts to “Java Rfc2898DeriveBytes”

  1. Thanks for sharing this. I’ve been trying to achieve exactly the same. We have a server written in .Net and a client written in Java.

  2. The class Rfc2898DeriveBytes has been improved to work with any HMac algorithm. A field hLen is introduced and it is set based on the hash algo supplied with an overloaded constructor. This we have tested with HMac512 test vectors from PHP Crypt Lib and failed for 6 cases out of 100 listed out.


    import java.io.UnsupportedEncodingException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;

    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;

    /**
    * Imported from Derived Bytes and
    * code here
    *
    * @author Madhu Krishna G
    * @version $Revision: $, $Date: $, $Author: $
    * @since Jun 29, 2012
    */
    public class Rfc2898DeriveBytes
    {

    private Mac _hmacShaAlgo;
    private byte[] _salt;
    private int _iterationCount;

    private int _hLen = 20; //for HMACSHA1
    private byte[] _buffer = new byte[_hLen];
    private int _bufferStartIndex = 0;
    private int _bufferEndIndex = 0;
    private int _block = 1;

    /**
    * Creates new instance.
    * @param password The password used to derive the key.
    * @param salt The key salt used to derive the key.
    * @param iterations The number of iterations for the operation.
    * @throws java.security.NoSuchAlgorithmException HmacSHA1 algorithm cannot be found.
    * @throws java.security.InvalidKeyException Salt must be 8 bytes or more. -or- Password cannot be null.
    */
    public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations) throws NoSuchAlgorithmException, InvalidKeyException {
    this("HmacSHA1", password, salt, iterations);
    }

    public Rfc2898DeriveBytes(String hashAlgo, byte[] password, byte[] salt, int iterations) throws NoSuchAlgorithmException, InvalidKeyException {
    // if ((salt == null) || (salt.length 0) { //if there is some data in buffer
    if (count < bufferCount) { //if there is enough data in buffer
    System.arraycopy(this._buffer, this._bufferStartIndex, result, 0, count);
    this._bufferStartIndex += count;
    return result;
    }
    System.arraycopy(this._buffer, this._bufferStartIndex, result, 0, bufferCount);
    this._bufferStartIndex = this._bufferEndIndex = 0;
    resultOffset += bufferCount;
    }

    while (resultOffset _hLen) { //we one (or more) additional passes
    System.arraycopy(this._buffer, 0, result, resultOffset, _hLen);
    resultOffset += _hLen;
    } else {
    System.arraycopy(this._buffer, 0, result, resultOffset, needCount);
    this._bufferStartIndex = needCount;
    this._bufferEndIndex = _hLen;
    return result;
    }
    }
    return result;
    }

    private byte[] func() {
    this._hmacShaAlgo.update(this._salt, 0, this._salt.length);
    byte[] tempHash = this._hmacShaAlgo.doFinal(getBytesFromInt(this._block));

    this._hmacShaAlgo.reset();
    byte[] finalHash = tempHash;
    for (int i = 2; i <= this._iterationCount; i++) {
    tempHash = this._hmacShaAlgo.doFinal(tempHash);
    for (int j = 0; j >> 24), (byte)(i >>> 16), (byte)(i >>> 8), (byte)i };
    }

    }

    1. Can you please verify this source code? Something is wrong with it, it appears some part of the original source is missing.

  3. This is brilliant and is helping me to generate the IV.

    However, can you tell me how do I get from the keyBytes to a SecretKeySpec so I can encrypt?

    Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterationCount);
    keyBytes = key.getBytes(16);
    ivBytes = key.getBytes(16);

    SecretKeySpec sks1 = new SecretKeySpec(??????, “AES”);

    cipher.init(Cipher.ENCRYPT_MODE, sks1,ivSpec );
    encryptedBytes = cipher.doFinal(bytesToEncrypt);

Leave a Reply

Your email address will not be published. Required fields are marked *