title: Derived key generation using PBKDF2WithHmacSHA1 in Java 8 is not deterministic under high concurrency
body: <p>Java password derived key generation using <code>PBKDF2WithHmacSHA1</code> in Java 8 (Linux) seems to randomly return zeroed <code>byte[]</code> keys when invoked under high concurrency. I have <strong>not</strong> been able to reproduce this with Java 7.  Since I have found a work-around for my use case, and I cannot invest in researching this any further, I ask the Java experts out there if they might:</p>
  
  <ul>
  <li>Sanity check the code below</li>
  <li>Verify the reproducibility</li>
  <li>Explain what might be causing this</li>
  </ul>
  
  <p>The example below will explode using Java 8 on Linux x64 in a very non-deterministic fashion (it may need to be executed a few times before it fails):</p>
  
  <pre><code>import java.security.Key;
  import java.security.NoSuchAlgorithmException;
  import java.security.spec.InvalidKeySpecException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.concurrent.Callable;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  import java.util.concurrent.Future;
  
  import javax.crypto.SecretKey;
  import javax.crypto.SecretKeyFactory;
  import javax.crypto.spec.PBEKeySpec;
  import javax.crypto.spec.SecretKeySpec;
  import javax.xml.bind.DatatypeConverter;
  
  public class Main {
      private static final String PASSWORD = "swordfish";
  
      private static final Callable&lt;Boolean&gt; CONCURRENT = new Callable&lt;Boolean&gt;() {
          @Override
          public Boolean call() throws Exception {
              final Key key1 = deriveKeyFromPassword(PASSWORD.toCharArray());
              final byte[] key1bytes = key1.getEncoded();
              for (int i = 0; i &lt; 10000; i++) {
                  final Key key2 = deriveKeyFromPassword(PASSWORD.toCharArray());
                  final byte[] key2bytes = key2.getEncoded();
                  if (!Arrays.equals(key1bytes, key2bytes)) {
                      throw new Exception("Keys do not match!\n"
                              + "key1: " + DatatypeConverter.printHexBinary(key1.getEncoded()) + "\n"
                              + "key2: " + DatatypeConverter.printHexBinary(key2.getEncoded()) + "\n");
                  }
              }
              return true;
          }
      };
  
      public static Key deriveKeyFromPassword(final char[] password) {
          try {
              final SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
              final PBEKeySpec spec = new PBEKeySpec(password, new byte[8], 5, 256);
              final SecretKey secret = factory.generateSecret(spec);
              return new SecretKeySpec(secret.getEncoded(), "AES");
          } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
              throw new RuntimeException(e);
          }
      }
  
      public static void main(final String[] args) throws Exception {
          final ExecutorService executor = Executors.newCachedThreadPool();
          try {
              int iterations = 1;
              while (true) {
                  final ArrayList&lt;Future&lt;Boolean&gt;&gt; results = new ArrayList&lt;Future&lt;Boolean&gt;&gt;();
                  for (int i = 0; i &lt; 10; i++) {
                      results.add(executor.submit(CONCURRENT));
                  }
                  for (final Future&lt;Boolean&gt; result : results) {
                      result.get();
                  }
                  System.out.println((iterations * 10 * 10000 * 2) + " keys derived");
                  iterations++;
              }
          } catch (final Exception e) {
              e.printStackTrace();
              System.exit(1);
          }
      }
  }
  </code></pre>