java PBKDF2 जावा में बाउंससाइकल के साथ




bouncycastle jce (3)

मैं एक डेटाबेस में पासवर्ड को सुरक्षित रूप से संग्रहीत करने की कोशिश कर रहा हूं और इसके लिए मैंने PBKDF2 फ़ंक्शन का उपयोग करके अपने हैश को स्टोर करने के लिए चुना है। मैं उछालभरी महल पुस्तकालय का उपयोग करना चाहता हूं, लेकिन मुझे नहीं पता कि मैं जेसीई इंटरफेस का उपयोग करके काम करने के लिए इसे क्यों नहीं प्राप्त कर सकता ... समस्या यह है कि 3 अलग-अलग मोड में हैश उत्पन्न करना:
1. सूरज द्वारा प्रदत्त PBKDF2WithHmacSHA1 गुप्त कुंजी कारखाने का उपयोग करना
2. उछालभरी महल एपीआई का सीधे उपयोग करना
3. JCE के माध्यम से उछालभरी महल का उपयोग करना
2 अलग-अलग मूल्यों में परिणाम: पहले दो के लिए एक सामान्य और तीसरे के लिए एक।

यहाँ मेरा कोड है:

    //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());

मुझे पता है कि PBKDF2 को HMAC SHA1 का उपयोग करके कार्यान्वित किया गया है, इसीलिए मैंने अंतिम विधि "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

कोई विचार?


संक्षेप में, अंतर का कारण यह है कि PBKDF2 एल्गोरिथ्म मोड # 1 और # 2 में PKCS # 5 v2 योजना 2 (PKCS5S2) का उपयोग पुनरावृत्ति कुंजी के लिए करता है, लेकिन मोड में # PBEWITHHMACSHA1 के लिए BouncyCastle प्रदाता # 3 PKCS # का उपयोग करता है। इसके बजाय 12 v1 (PKCS12) एल्गोरिथ्म। ये पूरी तरह से अलग-अलग कुंजी-पीढ़ी के एल्गोरिदम हैं, इसलिए आपको अलग-अलग परिणाम मिलते हैं।

ऐसा क्यों है और क्यों आपको अलग-अलग आकार के परिणाम मिलते हैं, इस बारे में विस्तार से नीचे बताया गया है।

सबसे पहले, जब आप एक JCE KeySpec का निर्माण कर रहे होते हैं, तो KeyLength पैरामीटर केवल प्रदाता को "वरीयता" व्यक्त करता है कि कुंजी आकार क्या चाहते हैं। एपीआई डॉक्स से :

नोट: यह चर-की-आकार साइफर के लिए महत्वपूर्ण लंबाई पर वरीयता को इंगित करने के लिए उपयोग किया जाता है। वास्तविक कुंजी आकार प्रत्येक प्रदाता के कार्यान्वयन पर निर्भर करता है।

बाउंसी कैसल प्रदाता JCEPBEKey के स्रोत से देखते हुए, इस पैरामीटर का सम्मान नहीं करते हैं, इसलिए आपको किसी भी BC प्रदाता से 160-बिट कुंजी वापस पाने की उम्मीद करनी चाहिए जो JCE API का उपयोग करते समय SHA-1 का उपयोग करता है।

आप अपने परीक्षण कोड में दिए गए keybc वैरिएबल पर getKeySize() पद्धति को प्रोग्रामेटिक रूप से एक्सेस करके इसकी पुष्टि कर सकते हैं:

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

अब, यह समझने के लिए कि "PBEWITHHMACSHA1" प्रदाता किससे मेल खाता है, आप BouncyCastleProvider के लिए स्रोत में निम्नलिखित पा सकते हैं:

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

और JCESecretKeyFactory.PBEWithSHA का कार्यान्वयन इस तरह दिखता है:

public static class PBEWithSHA
    extends 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 के साथ उपयोग करते हैं।

इसलिए मुझे लगता है कि आप या तो सूर्य कार्यान्वयन (मोड # 1 ऊपर) का उपयोग कर रहे हैं और अन्य JVM पर पोर्टेबिलिटी खो रहे हैं, या बाउंसी कैसल क्लासेस सीधे (मोड # 2 ऊपर) का उपयोग कर रहे हैं और रनटाइम पर बीसी लाइब्रेरी की आवश्यकता है।

किसी भी तरह से, आपको संभवतः 160-बिट कुंजियों पर स्विच करना चाहिए, इसलिए आप उत्पन्न SHA-1 हैश को अनावश्यक रूप से काट नहीं रहे हैं।


मुझे बीसी क्रिप्टो-ओनली विधि (वास्तव में बीसी के सेमी पैकेज से) मिली, जो यूटीएफ -8 आधारित पासवर्ड एन्कोडिंग का उत्पादन करने के लिए काम करती है। इस तरह मैं केडीएफ आउटपुट उत्पन्न कर सकता हूं जो संगत है

http://packages.python.org/passlib/lib/passlib.hash.cta_pbkdf2_sha1.html#passlib.hash.cta_pbkdf2_sha1

private byte[] calculatePasswordDigest(char[] pass, byte[] salt, int iterations)
    throws PasswordProtectionException
{
    try
    {
        /* JCE Version (does not work as BC uses PKCS12 encoding)
        SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWITHHMACSHA1","BC");
        PBEKeySpec ks = new PBEKeySpec(pass, salt, iterations,160);
        SecretKey digest = kf.generateSecret(ks);
        return digest.getEncoded();
        */
        PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator();
        gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(pass), salt, iterations);
        byte[] derivedKey = ((KeyParameter)gen.generateDerivedParameters(160)).getKey();
        return derivedKey;
    }
    catch(Exception e)
    {
        LOG.error("Failed to strengthen the password with PBKDF2.",e);
        throw new PasswordProtectionException();
    }
}

PBKDF2WithHmacSHA1 पहले से ही BouncyCastle 1.60 में समर्थित है

https://www.bouncycastle.org/specifications.html //www.b Councilycastle.org/specifications.html पासवर्ड हैशिंग और PBE

ओपनजेडके रनटाइम एनवायरनमेंट 18.9 (बिल्ड 11.0.1 + 13) के साथ टेस्ट पास किया गया:

    Security.addProvider(new BouncyCastleProvider());

    String password = "xrS7AJk+V6L8J?B%";
    SecureRandom rnd = new SecureRandom();
    int saltLength = 16;
    int keyLength = 128;
    int iterationCount = 10000;

    byte[] salt = new byte[saltLength];
    rnd.nextBytes(salt);

//SunJCE
    SecretKeyFactory factorySun = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1", "SunJCE");
    KeySpec keyspecSun = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength);
    SecretKey keySun = factorySun.generateSecret(keyspecSun);
    System.out.println(keySun.getClass().getName());
    System.out.println(Hex.toHexString(keySun.getEncoded()));

//BouncyCastle  
    SecretKeyFactory factoryBC = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1", "BC");
    KeySpec keyspecBC = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength);
    SecretKey keyBC = factoryBC.generateSecret(keyspecBC);
    System.out.println(keyBC.getClass().getName());
    System.out.println(Hex.toHexString(keyBC.getEncoded()));

    Assert.assertArrayEquals(keySun.getEncoded(), keyBC.getEncoded());

आउटपुट है:

com.sun.crypto.provider.PBKDF2KeyImpl
e9b01389fa91a6172ed6e95e1e1a2611
org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey
e9b01389fa91a6172ed6e95e1e1a2611




pbkdf2