All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.rtner.security.auth.spi.PBKDF2Engine.bak Maven / Gradle / Ivy

There is a newer version: 1.1.4
Show newest version
/*
 * A free Java implementation of Password Based Key Derivation Function 2 as
 * defined by RFC 2898. Copyright 2007, 2014, Matthias Gärtner
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package de.rtner.security.auth.spi;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;

/**
 * This Password Based Key Derivation Function 2 implementation.
 * 
* Request for Comments: 2898 PKCS #5: Password-Based Cryptography Specification *

* Version 2.0 * *

* PBKDF2 (P, S, c, dkLen) * *

* Options: *

    *
  • PRF underlying pseudorandom function (hLen denotes the length in octets * of the pseudorandom function output). PRF is pluggable.
  • *
* *

* Input: *

    *
  • P password, an octet string
  • *
  • S salt, an octet string
  • *
  • c iteration count, a positive integer
  • *
  • dkLen intended length in octets of the derived key, a positive integer, * at most (2^32 - 1) * hLen
  • *
* *

* Output: *

    *
  • DK derived key, a dkLen-octet string
  • *
* * @see RFC 2898 * @author Matthias Gärtner */ public class PBKDF2Engine implements PBKDF2 { protected PBKDF2Parameters parameters; protected PRF prf; /** * Constructor for PBKDF2 implementation object. PBKDF2 parameters must be * passed later. */ public PBKDF2Engine() { this.parameters = null; prf = null; } /** * Constructor for PBKDF2 implementation object. PBKDF2 parameters are * passed so that this implementation knows iteration count, method to use * and String encoding. * * @param parameters * Data holder for iteration count, method to use et cetera. */ public PBKDF2Engine(PBKDF2Parameters parameters) { this.parameters = parameters; prf = null; } /** * Constructor for PBKDF2 implementation object. PBKDF2 parameters are * passed so that this implementation knows iteration count, method to use * and String encoding. * * @param parameters * Data holder for iteration count, method to use et cetera. * @param prf * Supply customer Pseudo Random Function. */ public PBKDF2Engine(PBKDF2Parameters parameters, PRF prf) { this.parameters = parameters; this.prf = prf; } public byte[] deriveKey(String inputPassword) { return deriveKey(inputPassword, 0); } public byte[] deriveKey(String inputPassword, int dkLen) { byte[] r = null; byte P[] = null; String charset = parameters.getHashCharset(); if (inputPassword == null) { inputPassword = ""; } try { if (charset == null) { P = inputPassword.getBytes(); } else { P = inputPassword.getBytes(charset); } } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } assertPRF(P); if (dkLen == 0) { dkLen = prf.getHLen(); } r = PBKDF2(prf, parameters.getSalt(), parameters.getIterationCount(), dkLen); return r; } public boolean verifyKey(String inputPassword) { byte[] referenceKey = getParameters().getDerivedKey(); if (referenceKey == null || referenceKey.length == 0) { return false; } byte[] inputKey = deriveKey(inputPassword, referenceKey.length); if (inputKey == null || inputKey.length != referenceKey.length) { return false; } for (int i = 0; i < inputKey.length; i++) { if (inputKey[i] != referenceKey[i]) { return false; } } return true; } /** * Factory method. Default implementation is (H)MAC-based. To be overridden * in derived classes. * * @param P * User-supplied candidate password as array of bytes. */ protected void assertPRF(byte[] P) { if (prf == null) { prf = new MacBasedPRF(parameters.getHashAlgorithm()); } prf.init(P); } public PRF getPseudoRandomFunction() { return prf; } /** * Core Password Based Key Derivation Function 2. * * @see RFC 2898 5.2 * @param prf * Pseudo Random Function (i.e. HmacSHA1) * @param S * Salt as array of bytes. null means no salt. * @param c * Iteration count (see RFC 2898 4.2) * @param dkLen * desired length of derived key. * @return internal byte array */ protected byte[] PBKDF2(PRF prf, byte[] S, int c, int dkLen) { if (S == null) { S = new byte[0]; } int hLen = prf.getHLen(); int l = ceil(dkLen, hLen); int r = dkLen - (l - 1) * hLen; byte T[] = new byte[l * hLen]; int ti_offset = 0; for (int i = 1; i <= l; i++) { _F(T, ti_offset, prf, S, c, i); ti_offset += hLen; } if (r < hLen) { // Incomplete last block byte DK[] = new byte[dkLen]; System.arraycopy(T, 0, DK, 0, dkLen); return DK; } return T; } /** * Integer division with ceiling function. * * @see RFC 2898 5.2 Step 2. * @param a Numerator * @param b Denominator * @return ceil(a/b) */ protected int ceil(int a, int b) { int m = 0; if (a % b > 0) { m = 1; } return a / b + m; } /** * Function F. * * @see RFC 2898 5.2 Step 3. * @param dest * Destination byte buffer * @param offset * Offset into destination byte buffer * @param prf * Pseudo Random Function * @param S * Salt as array of bytes * @param c * Iteration count * @param blockIndex * The block index (>= 1). */ protected void _F(byte[] dest, int offset, PRF prf, byte[] S, int c, int blockIndex) { int hLen = prf.getHLen(); byte U_r[] = new byte[hLen]; // U0 = S || INT (i); byte U_i[] = new byte[S.length + 4]; System.arraycopy(S, 0, U_i, 0, S.length); INT(U_i, S.length, blockIndex); for (int i = 0; i < c; i++) { U_i = prf.doFinal(U_i); xor(U_r, U_i); } System.arraycopy(U_r, 0, dest, offset, hLen); } /** * Block-Xor. Xor source bytes into destination byte buffer. Destination * buffer must be same length or less than source buffer. * * @param dest destination byte buffer * @param src source bytes */ protected void xor(byte[] dest, byte[] src) { for (int i = 0; i < dest.length; i++) { dest[i] ^= src[i]; } } /** * Four-octet encoding of the integer i, most significant octet first. * * @see RFC 2898 5.2 Step 3. * @param dest destination byte buffer * @param offset zero-based offset into dest * @param i the integer to encode */ protected void INT(byte[] dest, int offset, int i) { dest[offset + 0] = (byte) (i / (256 * 256 * 256)); dest[offset + 1] = (byte) (i / (256 * 256)); dest[offset + 2] = (byte) (i / (256)); dest[offset + 3] = (byte) (i); } public PBKDF2Parameters getParameters() { return parameters; } public void setParameters(PBKDF2Parameters parameters) { this.parameters = parameters; } public void setPseudoRandomFunction(PRF prf) { this.prf = prf; } /** * Convenience client function. Convert supplied password with random 8-byte * salt and 1000 iterations (default) using HMacSHA1. Assume that password is in * ISO-8559-1 encoding. Output result as * "Salt:iteration-count:PBKDF2" with binary data in hexadecimal * encoding. *

* Example: Password "password" (without the quotes) leads to * 48290A0B96C426C3:1000:973899B1D4AFEB3ED371060D0797E0EE0142BD04 *

* The iteration count is configurable. In verification mode, the iteration * count supplied in the candidate string must be no less than the * * @param args * Supply the password as argument. * @throws IOException * apparently declared, but never thrown * @throws NoSuchAlgorithmException * Thrown if underlying crypto library does not support * requested algorithms (SHA1PRNG, HmacSHA1). */ public static void main(String[] args) throws IOException, NoSuchAlgorithmException { String password = "password"; String candidate = null; PBKDF2Formatter formatter = new PBKDF2HexFormatter(); int iterations = 1000; if (args.length >= 2 && args[0].equals("-i")) { iterations = Integer.parseInt(args[1]); args = Arrays.copyOfRange(args, 2, args.length); } if (args.length >= 1) { password = args[0]; } if (args.length >= 2) { candidate = args[1]; } if (candidate == null) { // Creation mode SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); byte[] salt = new byte[8]; sr.nextBytes(salt); PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1", salt, iterations); PBKDF2Engine e = new PBKDF2Engine(p); p.setDerivedKey(e.deriveKey(password)); candidate = formatter.toString(p); System.out.println(candidate); } else { // Verification mode PBKDF2Parameters p = new PBKDF2Parameters(); p.setHashAlgorithm("HmacSHA1"); p.setHashCharset("ISO-8859-1"); if (formatter.fromString(p, candidate)) { throw new IllegalArgumentException( "Candidate data does not have correct format (\"" + candidate + "\")"); } PBKDF2Engine e = new PBKDF2Engine(p); boolean verifyOK = (p.getIterationCount() >= iterations) && e.verifyKey(password); System.out.println(verifyOK ? "OK" : "FAIL"); System.exit(verifyOK ? 0 : 1); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy