PBKDF2与Java中的bouncycastle [英] PBKDF2 with bouncycastle in Java

查看:333
本文介绍了PBKDF2与Java中的bouncycastle的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将密码安全地存储在数据库中,为此我选择存储使用PBKDF2函数生成的哈希。我想使用充气城堡库来做这个,但我不知道为什么我不能通过使用JCE接口让它工作...
问题是在3种不同模式下生成散列:

1.使用由sun提供的PBKDF2WithHmacSHA1密钥工厂

2.直接使用充气城堡api

3.通过JCE使用充气城堡

产生2个不同的值:一个与前两个相同,一个与第三个相同。



这是我的代码:

  //模式1 

SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2WithHmacSHA1);
KeySpec keyspec = new PBEKeySpec(password.toCharArray(),salt,1000,128);
Key key = factory.generateSecret(keyspec);
System.out.println(key.getClass()。getName());
System.out.println(Arrays.toString(key.getEncoded()));

//模式2

PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes((password)。toCharArray()),salt,1000);
KeyParameter params =(KeyParameter)generator.generateDerivedParameters(128);
System.out.println(Arrays.toString(params.getKey()));

//模式3

SecretKeyFactory factorybc = SecretKeyFactory.getInstance(PBEWITHHMACSHA1,BC);
KeySpec keyspecbc = new PBEKeySpec(password.toCharArray(),salt,1000,128);
Key keybc = factorybc.generateSecret(keyspecbc);
System.out.println(keybc.getClass()。getName());
System.out.println(Arrays.toString(keybc.getEncoded()));
System.out.println(keybc.getAlgorithm());

我知道PBKDF2是使用HMAC SHA1实现的,所以这就是我在最后一个方法中选择算法的原因我从弹性城堡java docs中获取的PBEWITHHMACSHA1。



输出如下:

  com.sun.crypto.provider.SunJCE_ae 
[-53,29,113,-110,-25,76,115,-127,-64,74,-63 ,102,75,81,-21,74]
[-53,29,113,-110,-25,76,115,-127,-64,74,-63,102,75,81 ,-21,74]
org.bouncycastle.jce.provider.JCEPBEKey
[14,-47,-87,-16,-117,-31,91,-121,90,-68 ,-82,-31,-27,5,-93,-67,30,-34,-64,-40]
PBEwithHmacSHA

任何想法?

解决方案

简而言之,差异的原因是模式#1和#2中的PBKDF2算法使用PKCS#5 v2方案2(PKCS5S2)进行迭代密钥生成,但模式#3中PBEWITHHMACSHA1的BouncyCastle提供程序使用PKCS#12 v1(PKCS12)算法inst元首。这些是完全不同的密钥生成算法,因此您会得到不同的结果。



有关原因的详细信息以及为何获得不同大小的结果将在下面说明。



首先,当您构建JCE KeySpec时,keyLength参数仅向提供者表达您想要的密钥大小的首选项。来自 API文档


注意:这用于表示变量密钥大小密码的密钥长度偏好。实际密钥大小取决于每个提供商的实现。


JCEPBEKey的来源,所以你应该期望从使用JCE API的任何使用SHA-1的BC提供者那里获得160位密钥。



你可以通过以下方式确认:以编程方式访问测试代码中返回的 keybc 变量的 getKeySize()方法:

  Key keybc = factorybc.generateSecret(keyspecbc); 
// ... ...
方法getKeySize = JCEPBEKey.class.getDeclaredMethod(getKeySize);
getKeySize.setAccessible(true);
System.out.println(getKeySize.invoke(keybc)); //打印'160'

现在,要了解PBEWITHHMACSHA1提供商对应的内容,您可以在中找到以下内容BouncyCastleProvider的来源

  put(SecretKeyFactory.PBEWITHHMACSHA1,
org.bouncycastle。 jce.provider.JCESecretKeyFactory $ PBEWithSHA);

并执行JCESecretKeyFactory.PBEWithSHA 如下所示:

 公共静态类PBEWithSHA 
扩展PBEKeyFactory
{
public PBEWithSHA()
{
super(PBEwithHmacSHA ,null,false,PKCS12,SHA1,160,0);
}
}

您可以看到上面这个密钥工厂使用PKCS用于迭代密钥生成的#12 v1( PKCS12 )算法。但是,要用于密码散列的PBKDF2算法使用PKCS#5 v2方案2( PKCS5S2 )。这就是为什么你会得到不同的结果。



我快速浏览了在 BouncyCastleProvider 中注册的JCE提供商,但是根本看不到使用PKCS5S2的任何密钥生成算法,更不用说也使用HMAC-SHA-1的那个。



所以我猜你会遇到使用Sun实现(上面的模式#1)和在其他JVM上丢失可移植性,或直接使用Bouncy Castle类(上面的模式#2)并在运行时需要BC库。 / p>

无论哪种方式,您都应该切换到160位密钥,这样就不会不必要地截断生成的SHA-1哈希。


I'm trying to securely store a password in a database and for that I chose to store its hash generated using the PBKDF2 function. I want to do this using the bouncy castle library but I don't know why I cannot get it to work by using the JCE interface... The problem is that generating the hash in 3 different modes:
1. using the PBKDF2WithHmacSHA1 secret key factory provided by sun
2. using the bouncy castle api directly
3. using the bouncy castle through JCE
results in 2 distinct values: one common to the first two and one for the third.

Here is my code:

    //Mode 1

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    KeySpec keyspec = new PBEKeySpec("password".toCharArray(), salt, 1000, 128);
    Key key = factory.generateSecret(keyspec);
    System.out.println(key.getClass().getName());
    System.out.println(Arrays.toString(key.getEncoded()));

    //Mode 2

    PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
    generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(("password").toCharArray()), salt, 1000);
    KeyParameter params = (KeyParameter)generator.generateDerivedParameters(128);
    System.out.println(Arrays.toString(params.getKey()));

    //Mode 3

    SecretKeyFactory factorybc = SecretKeyFactory.getInstance("PBEWITHHMACSHA1", "BC");
    KeySpec keyspecbc = new PBEKeySpec("password".toCharArray(), salt, 1000, 128);
    Key keybc = factorybc.generateSecret(keyspecbc);
    System.out.println(keybc.getClass().getName());
    System.out.println(Arrays.toString(keybc.getEncoded()));
    System.out.println(keybc.getAlgorithm());

I know that PBKDF2 is implemented using HMAC SHA1 so that is why i chose as algorithm in the last method the "PBEWITHHMACSHA1" which i took from the bouncy castle java docs.

The output is the following:

com.sun.crypto.provider.SunJCE_ae
[-53, 29, 113, -110, -25, 76, 115, -127, -64, 74, -63, 102, 75, 81, -21, 74]
[-53, 29, 113, -110, -25, 76, 115, -127, -64, 74, -63, 102, 75, 81, -21, 74]
org.bouncycastle.jce.provider.JCEPBEKey
[14, -47, -87, -16, -117, -31, 91, -121, 90, -68, -82, -31, -27, 5, -93, -67, 30, -34, -64, -40]
PBEwithHmacSHA

Any ideas?

解决方案

In short, the reason for the difference is that PBKDF2 algorithm in modes #1 and #2 uses PKCS #5 v2 scheme 2 (PKCS5S2) for iterative key generation, but the BouncyCastle provider for "PBEWITHHMACSHA1" in mode #3 uses the PKCS #12 v1 (PKCS12) algorithm instead. These are completely different key-generation algorithms, so you get different results.

More detail on why this is so and why you get different sized results is explained below.

First, when you're constructing a JCE KeySpec, the keyLength parameter only expresses "a preference" to the provider what key size you want. From the API docs:

Note: this is used to indicate the preference on key length for variable-key-size ciphers. The actual key size depends on each provider's implementation.

The Bouncy Castle providers don't appear to respect this parameter, judging from the source of JCEPBEKey, so you should expect to get a 160-bit key back from any BC provider which uses SHA-1 when using the JCE API.

You can confirm this by programmatically accessing the getKeySize() method on the returned keybc variable in your test code:

Key keybc = factorybc.generateSecret(keyspecbc);
// ...
Method getKeySize = JCEPBEKey.class.getDeclaredMethod("getKeySize");
getKeySize.setAccessible(true);
System.out.println(getKeySize.invoke(keybc)); // prints '160'

Now, to understand what the "PBEWITHHMACSHA1" provider corresponds to, you can find the following in the source for BouncyCastleProvider:

put("SecretKeyFactory.PBEWITHHMACSHA1", 
    "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA");

And the implementation of JCESecretKeyFactory.PBEWithSHA looks like this:

public static class PBEWithSHA
    extends PBEKeyFactory
{
    public PBEWithSHA()
    {
        super("PBEwithHmacSHA", null, false, PKCS12, SHA1, 160, 0);
    }
}

You can see above that this key factory uses the PKCS #12 v1 (PKCS12) algorithm for iterative key generation. But the PBKDF2 algorithm that you want to use for password hashing uses PKCS #5 v2 scheme 2 (PKCS5S2) instead. This is why you're getting different results.

I had a quick look through the JCE providers registered in BouncyCastleProvider, but couldn't see any key generation algorithms that used PKCS5S2 at all, let alone one which also uses it with HMAC-SHA-1.

So I guess you're stuck with either using the Sun implementation (mode #1 above) and losing portability on other JVMs, or using the Bouncy Castle classes directly (mode #2 above) and requiring the BC library at runtime.

Either way, you should probably switch to 160-bit keys, so you aren't truncating the generated SHA-1 hash unnecessarily.

这篇关于PBKDF2与Java中的bouncycastle的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆