在Java 8中使用PBKDF2WithHmacSHA1生成派生密钥在高并发性下是不确定的

在Java 8中使用PBKDF2WithHmacSHA1生成派生密钥在高并发性下是不确定的,java,Java,在Java 8(Linux)中使用PBKDF2WithHmacSHA1生成Java密码派生的密钥,在高并发性下调用时,似乎会随机返回归零的byte[]密钥。我无法用Java7重现这一点。由于我已经为我的用例找到了一个解决方案,并且我不能再进一步研究这个问题,所以我询问那里的Java专家是否可以: 请检查下面的代码 验证再现性 解释可能的原因 下面的示例将以一种非常不确定的方式在Linux x64上使用Java 8(可能需要执行几次才能失败): 导入java.security.Key; 导入j

在Java 8(Linux)中使用
PBKDF2WithHmacSHA1
生成Java密码派生的密钥,在高并发性下调用时,似乎会随机返回归零的
byte[]
密钥。我无法用Java7重现这一点。由于我已经为我的用例找到了一个解决方案,并且我不能再进一步研究这个问题,所以我询问那里的Java专家是否可以:

  • 请检查下面的代码
  • 验证再现性
  • 解释可能的原因
下面的示例将以一种非常不确定的方式在Linux x64上使用Java 8(可能需要执行几次才能失败):

导入java.security.Key;
导入java.security.NoSuchAlgorithmException;
导入java.security.spec.InvalidKeySpecException;
导入java.util.ArrayList;
导入java.util.array;
导入java.util.concurrent.Callable;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.concurrent.Future;
导入javax.crypto.SecretKey;
导入javax.crypto.SecretKeyFactory;
导入javax.crypto.spec.PBEKeySpec;
导入javax.crypto.spec.SecretKeySpec;
导入javax.xml.bind.DatatypeConverter;
公共班机{
私有静态最终字符串PASSWORD=“swordfish”;
private static final Callable CONCURRENT=new Callable(){
@凌驾
公共布尔调用()引发异常{
final Key key1=deriveKeyFromPassword(PASSWORD.toCharArray());
最后一个字节[]key1bytes=key1.getEncoded();
对于(int i=0;i<10000;i++){
final Key key2=deriveKeyFromPassword(PASSWORD.toCharArray());
最后一个字节[]key2bytes=key2.getEncoded();
如果(!array.equals(key1bytes,key2bytes)){
抛出新异常(“键不匹配!\n”
+key1:“+DatatypeConverter.printHexBinary(key1.getEncoded())+”\n
+“key2:”+DatatypeConverter.printHexBinary(key2.getEncoded())+“\n”);
}
}
返回true;
}
};
公共静态密钥deriveKeyFromPassword(最终字符[]密码){
试一试{
最终SecretKeyFactory=SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA1”);
最终PBEKeySpec spec=新的PBEKeySpec(密码,新字节[8],5256);
最终保密密钥机密=工厂生成密钥(规范);
返回新的SecretKeySpec(secret.getEncoded(),“AES”);
}捕获(NoSuchAlgorithmException | InvalidKeySpece异常){
抛出新的运行时异常(e);
}
}
公共静态void main(最终字符串[]args)引发异常{
final ExecutorService executor=Executors.newCachedThreadPool();
试一试{
int迭代次数=1;
while(true){
最终ArrayList结果=新建ArrayList();
对于(int i=0;i<10;i++){
结果.添加(执行者.提交(并发));
}
对于(最终未来结果:结果){
result.get();
}
System.out.println((迭代次数*10*10000*2)+“派生键”);
迭代++;
}
}捕获(最终异常e){
e、 printStackTrace();
系统出口(1);
}
}
}
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<Boolean> CONCURRENT = new Callable<Boolean>() {
        @Override
        public Boolean call() throws Exception {
            final Key key1 = deriveKeyFromPassword(PASSWORD.toCharArray());
            final byte[] key1bytes = key1.getEncoded();
            for (int i = 0; i < 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<Future<Boolean>> results = new ArrayList<Future<Boolean>>();
                for (int i = 0; i < 10; i++) {
                    results.add(executor.submit(CONCURRENT));
                }
                for (final Future<Boolean> result : results) {
                    result.get();
                }
                System.out.println((iterations * 10 * 10000 * 2) + " keys derived");
                iterations++;
            }
        } catch (final Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}