Chiddix

Rfc2898DeriveBytes

Jul 17th, 2012
677
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import java.io.UnsupportedEncodingException;
  2. import java.security.InvalidKeyException;
  3. import java.security.NoSuchAlgorithmException;
  4.  
  5. import javax.crypto.Mac;
  6. import javax.crypto.spec.SecretKeySpec;
  7.  
  8. /**
  9.  * RFC 2898 password derivation compatible with .NET Rfc2898DeriveBytes class.
  10.  */
  11. public class Rfc2898DeriveBytes {
  12.  
  13.     private Mac _hmacSha1;
  14.     private byte[] _salt;
  15.     private int _iterationCount;
  16.  
  17.     private byte[] _buffer = new byte[20];
  18.     private int _bufferStartIndex = 0;
  19.     private int _bufferEndIndex = 0;
  20.     private int _block = 1;
  21.  
  22.     /**
  23.      * Creates new instance.
  24.      *
  25.      * @param password
  26.      *            The password used to derive the key.
  27.      * @param salt
  28.      *            The key salt used to derive the key.
  29.      * @param iterations
  30.      *            The number of iterations for the operation.
  31.      * @throws NoSuchAlgorithmException
  32.      *             HmacSHA1 algorithm cannot be found.
  33.      * @throws InvalidKeyException
  34.      *             Salt must be 8 bytes or more. -or- Password cannot be null.
  35.      */
  36.     public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations) throws NoSuchAlgorithmException, InvalidKeyException {
  37.         if ((salt == null) || (salt.length < 8)) {
  38.             throw new InvalidKeyException("Salt must be 8 bytes or more.");
  39.         }
  40.         if (password == null) {
  41.             throw new InvalidKeyException("Password cannot be null.");
  42.         }
  43.         this._salt = salt;
  44.         this._iterationCount = iterations;
  45.         this._hmacSha1 = Mac.getInstance("HmacSHA1");
  46.         this._hmacSha1.init(new SecretKeySpec(password, "HmacSHA1"));
  47.     }
  48.  
  49.     /**
  50.      * Creates new instance.
  51.      *
  52.      * @param password
  53.      *            The password used to derive the key.
  54.      * @param salt
  55.      *            The key salt used to derive the key.
  56.      * @param iterations
  57.      *            The number of iterations for the operation.
  58.      * @throws NoSuchAlgorithmException
  59.      *             HmacSHA1 algorithm cannot be found.
  60.      * @throws InvalidKeyException
  61.      *             Salt must be 8 bytes or more. -or- Password cannot be null.
  62.      * @throws UnsupportedEncodingException
  63.      *             UTF-8 encoding is not supported.
  64.      */
  65.     public Rfc2898DeriveBytes(String password, byte[] salt, int iterations) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
  66.         this(password.getBytes("UTF8"), salt, iterations);
  67.     }
  68.  
  69.     /**
  70.      * Creates new instance.
  71.      *
  72.      * @param password
  73.      *            The password used to derive the key.
  74.      * @param salt
  75.      *            The key salt used to derive the key.
  76.      * @throws NoSuchAlgorithmException
  77.      *             HmacSHA1 algorithm cannot be found.
  78.      * @throws InvalidKeyException
  79.      *             Salt must be 8 bytes or more. -or- Password cannot be null.
  80.      * @throws UnsupportedEncodingException
  81.      *             UTF-8 encoding is not supported.
  82.      */
  83.     public Rfc2898DeriveBytes(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
  84.         this(password, salt, 0x3e8);
  85.     }
  86.  
  87.     /**
  88.      * Returns a pseudo-random key from a password, salt and iteration count.
  89.      *
  90.      * @param count
  91.      *            Number of bytes to return.
  92.      * @return Byte array.
  93.      */
  94.     public byte[] getBytes(int count) {
  95.         byte[] result = new byte[count];
  96.         int resultOffset = 0;
  97.         int bufferCount = this._bufferEndIndex - this._bufferStartIndex;
  98.  
  99.         if (bufferCount > 0) { // if there is some data in buffer
  100.             if (count < bufferCount) { // if there is enough data in buffer
  101.                 System.arraycopy(this._buffer, this._bufferStartIndex, result, 0, count);
  102.                 this._bufferStartIndex += count;
  103.                 return result;
  104.             }
  105.             System.arraycopy(this._buffer, this._bufferStartIndex, result, 0, bufferCount);
  106.             this._bufferStartIndex = this._bufferEndIndex = 0;
  107.             resultOffset += bufferCount;
  108.         }
  109.  
  110.         while (resultOffset < count) {
  111.             int needCount = count - resultOffset;
  112.             this._buffer = this.func();
  113.             if (needCount > 20) { // we one (or more) additional passes
  114.                 System.arraycopy(this._buffer, 0, result, resultOffset, 20);
  115.                 resultOffset += 20;
  116.             } else {
  117.                 System.arraycopy(this._buffer, 0, result, resultOffset, needCount);
  118.                 this._bufferStartIndex = needCount;
  119.                 this._bufferEndIndex = 20;
  120.                 return result;
  121.             }
  122.         }
  123.         return result;
  124.     }
  125.  
  126.     private byte[] func() {
  127.         this._hmacSha1.update(this._salt, 0, this._salt.length);
  128.         byte[] tempHash = this._hmacSha1.doFinal(getBytesFromInt(this._block));
  129.  
  130.         this._hmacSha1.reset();
  131.         byte[] finalHash = tempHash;
  132.         for (int i = 2; i <= this._iterationCount; i++) {
  133.             tempHash = this._hmacSha1.doFinal(tempHash);
  134.             for (int j = 0; j < 20; j++) {
  135.                 finalHash[j] = (byte) (finalHash[j] ^ tempHash[j]);
  136.             }
  137.         }
  138.         if (this._block == 2147483647) {
  139.             this._block = -2147483648;
  140.         } else {
  141.             this._block += 1;
  142.         }
  143.  
  144.         return finalHash;
  145.     }
  146.  
  147.     private static byte[] getBytesFromInt(int i) {
  148.         return new byte[] { (byte) (i >>> 24), (byte) (i >>> 16), (byte) (i >>> 8), (byte) i };
  149.     }
  150.  
  151. }
RAW Paste Data