Update and Fix PHP bcrypt to login server bcrypt compatibility (#462)
Update jBCrypt from version 0.2 to 0.4 which resolves a security bug with character encoding and corrects an integer overflow bug.
Modify jBCrypt library to handle $2y$ hashes, which are produced by PHP's password_hash bcrypt function. The original jBCrypt library only recognizes $2a$ hashes which makes it harder for a website CMS (content management system) to interface with the server's login and databases.
Credit to: Ariel Salomon for compatibility support of 2b, 2y, and 2x hashes fe14360010 (diff-61d1d5b7b3c721fac60702cf559d70aa)
This commit is contained in:
@@ -11,12 +11,14 @@
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package tools;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* BCrypt implements OpenBSD-style Blowfish password hashing using
|
||||
* the scheme described in "A Future-Adaptable Password Scheme" by
|
||||
@@ -58,16 +60,15 @@ import java.security.SecureRandom;
|
||||
* 10, and the valid range is 4 to 30.
|
||||
*
|
||||
* @author Damien Miller
|
||||
* @version 0.2
|
||||
* @version 0.4
|
||||
*/
|
||||
public class BCrypt {
|
||||
// BCrypt parameters
|
||||
|
||||
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
|
||||
private static final int BCRYPT_SALT_LEN = 16;
|
||||
|
||||
// Blowfish parameters
|
||||
private static final int BLOWFISH_NUM_ROUNDS = 16;
|
||||
|
||||
// Initial contents of key schedule
|
||||
private static final int P_orig[] = {
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
@@ -334,7 +335,6 @@ public class BCrypt {
|
||||
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
|
||||
};
|
||||
|
||||
// bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls
|
||||
// this "ciphertext", but it is really plaintext or an IV. We keep
|
||||
// the name to make code comparison easier.
|
||||
@@ -342,7 +342,6 @@ public class BCrypt {
|
||||
0x4f727068, 0x65616e42, 0x65686f6c,
|
||||
0x64657253, 0x63727944, 0x6f756274
|
||||
};
|
||||
|
||||
// Table for Base64 encoding
|
||||
static private final char base64_code[] = {
|
||||
'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||
@@ -352,7 +351,6 @@ public class BCrypt {
|
||||
'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9'
|
||||
};
|
||||
|
||||
// Table for Base64 decoding
|
||||
static private final byte index_64[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
@@ -369,7 +367,6 @@ public class BCrypt {
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
||||
51, 52, 53, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
// Expanded Blowfish key
|
||||
private int P[];
|
||||
private int S[];
|
||||
@@ -387,11 +384,12 @@ public class BCrypt {
|
||||
private static String encode_base64(byte d[], int len)
|
||||
throws IllegalArgumentException {
|
||||
int off = 0;
|
||||
StringBuffer rs = new StringBuffer();
|
||||
StringBuilder rs = new StringBuilder();
|
||||
int c1, c2;
|
||||
|
||||
if (len <= 0 || len > d.length)
|
||||
if (len <= 0 || len > d.length) {
|
||||
throw new IllegalArgumentException("Invalid len");
|
||||
}
|
||||
|
||||
while (off < len) {
|
||||
c1 = d[off++] & 0xff;
|
||||
@@ -424,8 +422,9 @@ public class BCrypt {
|
||||
* @return the decoded value of x
|
||||
*/
|
||||
private static byte char64(char x) {
|
||||
if ((int)x < 0 || (int)x > index_64.length)
|
||||
if ((int) x < 0 || (int) x > index_64.length) {
|
||||
return -1;
|
||||
}
|
||||
return index_64[(int) x];
|
||||
}
|
||||
|
||||
@@ -440,32 +439,37 @@ public class BCrypt {
|
||||
*/
|
||||
private static byte[] decode_base64(String s, int maxolen)
|
||||
throws IllegalArgumentException {
|
||||
StringBuffer rs = new StringBuffer();
|
||||
StringBuilder rs = new StringBuilder();
|
||||
int off = 0, slen = s.length(), olen = 0;
|
||||
byte ret[];
|
||||
byte c1, c2, c3, c4, o;
|
||||
|
||||
if (maxolen <= 0)
|
||||
if (maxolen <= 0) {
|
||||
throw new IllegalArgumentException("Invalid maxolen");
|
||||
}
|
||||
|
||||
while (off < slen - 1 && olen < maxolen) {
|
||||
c1 = char64(s.charAt(off++));
|
||||
c2 = char64(s.charAt(off++));
|
||||
if (c1 == -1 || c2 == -1)
|
||||
if (c1 == -1 || c2 == -1) {
|
||||
break;
|
||||
}
|
||||
o = (byte) (c1 << 2);
|
||||
o |= (c2 & 0x30) >> 4;
|
||||
rs.append((char) o);
|
||||
if (++olen >= maxolen || off >= slen)
|
||||
if (++olen >= maxolen || off >= slen) {
|
||||
break;
|
||||
}
|
||||
c3 = char64(s.charAt(off++));
|
||||
if (c3 == -1)
|
||||
if (c3 == -1) {
|
||||
break;
|
||||
}
|
||||
o = (byte) ((c2 & 0x0f) << 4);
|
||||
o |= (c3 & 0x3c) >> 2;
|
||||
rs.append((char) o);
|
||||
if (++olen >= maxolen || off >= slen)
|
||||
if (++olen >= maxolen || off >= slen) {
|
||||
break;
|
||||
}
|
||||
c4 = char64(s.charAt(off++));
|
||||
o = (byte) ((c3 & 0x03) << 6);
|
||||
o |= c4;
|
||||
@@ -474,8 +478,9 @@ public class BCrypt {
|
||||
}
|
||||
|
||||
ret = new byte[olen];
|
||||
for (off = 0; off < olen; off++)
|
||||
for (off = 0; off < olen; off++) {
|
||||
ret[off] = (byte) rs.charAt(off);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -513,20 +518,50 @@ public class BCrypt {
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the
|
||||
* current offset into data
|
||||
* @return the next word of material from data
|
||||
* @param signp a "pointer" (as a one-entry array) to the
|
||||
* cumulative flag for non-benign sign extension
|
||||
* @return correct and buggy next word of material from data as int[2]
|
||||
*/
|
||||
private static int streamtoword(byte data[], int offp[]) {
|
||||
private static int[] streamtowords(byte data[], int offp[], int signp[]) {
|
||||
int i;
|
||||
int word = 0;
|
||||
int words[] = { 0, 0 };
|
||||
int off = offp[0];
|
||||
int sign = signp[0];
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
word = (word << 8) | (data[off] & 0xff);
|
||||
words[0] = (words[0] << 8) | (data[off] & 0xff);
|
||||
words[1] = (words[1] << 8) | (int)data[off];
|
||||
if (i > 0) sign |= words[1] & 0x80;
|
||||
off = (off + 1) % data.length;
|
||||
}
|
||||
|
||||
offp[0] = off;
|
||||
return word;
|
||||
signp[0] = sign;
|
||||
return words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycically extract a word of key material
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the
|
||||
* current offset into data
|
||||
* @return the next word of material from data
|
||||
*/
|
||||
private static int streamtoword(byte data[], int offp[]) {
|
||||
int signp[] = { 0 };
|
||||
return streamtowords(data, offp, signp)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycically extract a word of key material, with sign-extension bug
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the
|
||||
* current offset into data
|
||||
* @return the next word of material from data
|
||||
*/
|
||||
private static int streamtoword_bug(byte data[], int offp[]) {
|
||||
int signp[] = { 0 };
|
||||
return streamtowords(data, offp, signp)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -540,15 +575,20 @@ public class BCrypt {
|
||||
/**
|
||||
* Key the Blowfish cipher
|
||||
* @param key an array containing the key
|
||||
* @param sign_ext_bug true to implement the 2x bug
|
||||
*/
|
||||
private void key(byte key[]) {
|
||||
private void key(byte key[], boolean sign_ext_bug) {
|
||||
int i;
|
||||
int koffp[] = {0};
|
||||
int lr[] = {0, 0};
|
||||
int plen = P.length, slen = S.length;
|
||||
|
||||
for (i = 0; i < plen; i++)
|
||||
for (i = 0; i < plen; i++) {
|
||||
if (!sign_ext_bug)
|
||||
P[i] = P[i] ^ streamtoword(key, koffp);
|
||||
else
|
||||
P[i] = P[i] ^ streamtoword_bug(key, koffp);
|
||||
}
|
||||
|
||||
for (i = 0; i < plen; i += 2) {
|
||||
encipher(lr, 0);
|
||||
@@ -569,15 +609,53 @@ public class BCrypt {
|
||||
* http://www.openbsd.org/papers/bcrypt-paper.ps
|
||||
* @param data salt information
|
||||
* @param key password information
|
||||
* @param sign_ext_bug true to implement the 2x bug
|
||||
* @param safety bit 16 is set when the safety measure is requested
|
||||
*/
|
||||
private void ekskey(byte data[], byte key[]) {
|
||||
private void ekskey(byte data[], byte key[],
|
||||
boolean sign_ext_bug, int safety) {
|
||||
int i;
|
||||
int koffp[] = {0}, doffp[] = {0};
|
||||
int lr[] = {0, 0};
|
||||
int plen = P.length, slen = S.length;
|
||||
int signp[] = { 0 }; // non-benign sign-extension flag
|
||||
int diff = 0; // zero iff correct and buggy are same
|
||||
|
||||
for (i = 0; i < plen; i++)
|
||||
P[i] = P[i] ^ streamtoword(key, koffp);
|
||||
for (i = 0; i < plen; i++) {
|
||||
int words[] = streamtowords(key, koffp, signp);
|
||||
diff |= words[0] ^ words[1];
|
||||
P[i] = P[i] ^ words[sign_ext_bug ? 1 : 0];
|
||||
}
|
||||
|
||||
int sign = signp[0];
|
||||
|
||||
/*
|
||||
* At this point, "diff" is zero iff the correct and buggy algorithms produced
|
||||
* exactly the same result. If so and if "sign" is non-zero, which indicates
|
||||
* that there was a non-benign sign extension, this means that we have a
|
||||
* collision between the correctly computed hash for this password and a set of
|
||||
* passwords that could be supplied to the buggy algorithm. Our safety measure
|
||||
* is meant to protect from such many-buggy to one-correct collisions, by
|
||||
* deviating from the correct algorithm in such cases. Let's check for this.
|
||||
*/
|
||||
diff |= diff >> 16; /* still zero iff exact match */
|
||||
diff &= 0xffff; /* ditto */
|
||||
diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
|
||||
sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
|
||||
sign &= ~diff & safety; /* action needed? */
|
||||
|
||||
/*
|
||||
* If we have determined that we need to deviate from the correct algorithm,
|
||||
* flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
|
||||
* let's stick to it now. It came out of the approach we used above, and it's
|
||||
* not any worse than any other choice we could make.)
|
||||
*
|
||||
* It is crucial that we don't do the same to the expanded key used in the main
|
||||
* Eksblowfish loop. By doing it to only one of these two, we deviate from a
|
||||
* state that could be directly specified by a password to the buggy algorithm
|
||||
* (and to the fully correct one as well, but that's a side-effect).
|
||||
*/
|
||||
P[0] ^= sign;
|
||||
|
||||
for (i = 0; i < plen; i += 2) {
|
||||
lr[0] ^= streamtoword(data, doffp);
|
||||
@@ -603,32 +681,37 @@ public class BCrypt {
|
||||
* @param salt the binary salt to hash with the password
|
||||
* @param log_rounds the binary logarithm of the number
|
||||
* of rounds of hashing to apply
|
||||
* @param sign_ext_bug true to implement the 2x bug
|
||||
* @param safety bit 16 is set when the safety measure is requested
|
||||
* @param cdata the plaintext to encrypt
|
||||
* @return an array containing the binary hashed password
|
||||
*/
|
||||
public byte[] crypt_raw(byte password[], byte salt[], int log_rounds,
|
||||
int cdata[]) {
|
||||
private byte[] crypt_raw(byte password[], byte salt[], int log_rounds,
|
||||
boolean sign_ext_bug, int safety, int cdata[]) {
|
||||
int rounds, i, j;
|
||||
int clen = cdata.length;
|
||||
byte ret[];
|
||||
|
||||
if (log_rounds < 4 || log_rounds > 30)
|
||||
if (log_rounds < 4 || log_rounds > 30) {
|
||||
throw new IllegalArgumentException("Bad number of rounds");
|
||||
}
|
||||
rounds = 1 << log_rounds;
|
||||
if (salt.length != BCRYPT_SALT_LEN)
|
||||
if (salt.length != BCRYPT_SALT_LEN) {
|
||||
throw new IllegalArgumentException("Bad salt length");
|
||||
}
|
||||
|
||||
init_key();
|
||||
ekskey(salt, password);
|
||||
ekskey(salt, password, sign_ext_bug, safety);
|
||||
for (i = 0; i != rounds; i++) {
|
||||
key(password);
|
||||
key(salt);
|
||||
key(password, sign_ext_bug);
|
||||
key(salt, false);
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
for (j = 0; j < (clen >> 1); j++)
|
||||
for (j = 0; j < (clen >> 1); j++) {
|
||||
encipher(cdata, j << 1);
|
||||
}
|
||||
}
|
||||
|
||||
ret = new byte[clen * 4];
|
||||
for (i = 0, j = 0; i < clen; i++) {
|
||||
@@ -640,6 +723,23 @@ public class BCrypt {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given plaintext to byte representation.
|
||||
* @param plaintext the plaintext password to convert
|
||||
* @return Byte representation of given plaintext.
|
||||
*/
|
||||
private static byte[] stringToBytes(String plaintext) {
|
||||
byte plaintextb[];
|
||||
|
||||
try {
|
||||
plaintextb = plaintext.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new AssertionError("UTF-8 is not supported");
|
||||
}
|
||||
|
||||
return plaintextb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password using the OpenBSD bcrypt scheme
|
||||
* @param password the password to hash
|
||||
@@ -648,48 +748,67 @@ public class BCrypt {
|
||||
* @return the hashed password
|
||||
*/
|
||||
public static String hashpw(String password, String salt) {
|
||||
byte passwordb[] = stringToBytes(password);
|
||||
|
||||
return hashpw(passwordb, salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password using the OpenBSD bcrypt scheme
|
||||
* @param passwordb the password to hash, as a byte array
|
||||
* @param salt the salt to hash with (perhaps generated
|
||||
* using BCrypt.gensalt)
|
||||
* @return the hashed password
|
||||
*/
|
||||
public static String hashpw(byte passwordb[], String salt) {
|
||||
BCrypt B;
|
||||
String real_salt;
|
||||
byte passwordb[], saltb[], hashed[];
|
||||
byte saltb[], hashed[];
|
||||
char minor = (char) 0;
|
||||
int rounds, off = 0;
|
||||
StringBuffer rs = new StringBuffer();
|
||||
StringBuilder rs = new StringBuilder();
|
||||
|
||||
if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
|
||||
if (salt.charAt(0) != '$' || salt.charAt(1) != '2') {
|
||||
throw new IllegalArgumentException("Invalid salt version");
|
||||
if (salt.charAt(2) == '$')
|
||||
}
|
||||
if (salt.charAt(2) == '$') {
|
||||
off = 3;
|
||||
else {
|
||||
} else {
|
||||
minor = salt.charAt(2);
|
||||
if (minor != 'a' || salt.charAt(3) != '$')
|
||||
if ((minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b')
|
||||
|| salt.charAt(3) != '$') {
|
||||
throw new IllegalArgumentException("Invalid salt revision");
|
||||
}
|
||||
off = 4;
|
||||
}
|
||||
|
||||
// Extract number of rounds
|
||||
if (salt.charAt(off + 2) > '$')
|
||||
if (salt.charAt(off + 2) > '$') {
|
||||
throw new IllegalArgumentException("Missing salt rounds");
|
||||
}
|
||||
rounds = Integer.parseInt(salt.substring(off, off + 2));
|
||||
|
||||
real_salt = salt.substring(off + 3, off + 25);
|
||||
try {
|
||||
passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new AssertionError("UTF-8 is not supported");
|
||||
}
|
||||
|
||||
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
|
||||
|
||||
if (minor >= 'a') // add null terminator
|
||||
passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);
|
||||
|
||||
B = new BCrypt();
|
||||
hashed = B.crypt_raw(passwordb, saltb, rounds,
|
||||
minor == 'x', // true for sign extension bug ('2x')
|
||||
minor == 'a' ? 0x10000 : 0, // safety factor for '2a'
|
||||
(int[])bf_crypt_ciphertext.clone());
|
||||
|
||||
rs.append("$2");
|
||||
if (minor >= 'a')
|
||||
if (minor >= 'a') {
|
||||
rs.append(minor);
|
||||
}
|
||||
rs.append("$");
|
||||
if (rounds < 10)
|
||||
if (rounds < 10) {
|
||||
rs.append("0");
|
||||
}
|
||||
if (rounds > 30) {
|
||||
throw new IllegalArgumentException(
|
||||
"rounds exceeds maximum (30)");
|
||||
@@ -704,21 +823,36 @@ public class BCrypt {
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method
|
||||
* @param prefix the prefix value (default $2y)
|
||||
* @param log_rounds the log2 of the number of rounds of
|
||||
* hashing to apply - the work factor therefore increases as
|
||||
* 2**log_rounds.
|
||||
* @param random an instance of SecureRandom to use
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if prefix or log_rounds is invalid
|
||||
*/
|
||||
public static String gensalt(int log_rounds, SecureRandom random) {
|
||||
StringBuffer rs = new StringBuffer();
|
||||
public static String gensalt(String prefix, int log_rounds, SecureRandom random)
|
||||
throws IllegalArgumentException {
|
||||
StringBuilder rs = new StringBuilder();
|
||||
byte rnd[] = new byte[BCRYPT_SALT_LEN];
|
||||
|
||||
if (!prefix.startsWith("$2") ||
|
||||
(prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y') &&
|
||||
prefix.charAt(2) != 'b') {
|
||||
throw new IllegalArgumentException ("Invalid prefix");
|
||||
}
|
||||
if (log_rounds < 4 || log_rounds > 31) {
|
||||
throw new IllegalArgumentException ("Invalid log_rounds");
|
||||
}
|
||||
|
||||
random.nextBytes(rnd);
|
||||
|
||||
rs.append("$2a$");
|
||||
if (log_rounds < 10)
|
||||
rs.append("$2");
|
||||
rs.append(prefix.charAt(2));
|
||||
rs.append("$");
|
||||
if (log_rounds < 10) {
|
||||
rs.append("0");
|
||||
}
|
||||
if (log_rounds > 30) {
|
||||
throw new IllegalArgumentException(
|
||||
"log_rounds exceeds maximum (30)");
|
||||
@@ -731,12 +865,42 @@ public class BCrypt {
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method
|
||||
* @param prefix the prefix value (default $2y)
|
||||
* @param log_rounds the log2 of the number of rounds of
|
||||
* hashing to apply - the work factor therefore increases as
|
||||
* 2**log_rounds.
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if prefix or log_rounds is invalid
|
||||
*/
|
||||
public static String gensalt(int log_rounds) {
|
||||
public static String gensalt(String prefix, int log_rounds)
|
||||
throws IllegalArgumentException {
|
||||
return gensalt(prefix, log_rounds, new SecureRandom());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method
|
||||
* @param log_rounds the log2 of the number of rounds of
|
||||
* hashing to apply - the work factor therefore increases as
|
||||
* 2**log_rounds.
|
||||
* @param random an instance of SecureRandom to use
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if prefix or log_rounds is invalid
|
||||
*/
|
||||
public static String gensalt(int log_rounds, SecureRandom random)
|
||||
throws IllegalArgumentException {
|
||||
return gensalt("$2y", log_rounds, random);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method
|
||||
* @param log_rounds the log2 of the number of rounds of
|
||||
* hashing to apply - the work factor therefore increases as
|
||||
* 2**log_rounds.
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if prefix or log_rounds is invalid
|
||||
*/
|
||||
public static String gensalt(int log_rounds)
|
||||
throws IllegalArgumentException {
|
||||
return gensalt(log_rounds, new SecureRandom());
|
||||
}
|
||||
|
||||
@@ -745,8 +909,10 @@ public class BCrypt {
|
||||
* selecting a reasonable default for the number of hashing
|
||||
* rounds to apply
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if prefix or log_rounds is invalid
|
||||
*/
|
||||
public static String gensalt() {
|
||||
public static String gensalt()
|
||||
throws IllegalArgumentException {
|
||||
return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
|
||||
}
|
||||
|
||||
@@ -758,6 +924,18 @@ public class BCrypt {
|
||||
* @return true if the passwords match, false otherwise
|
||||
*/
|
||||
public static boolean checkpw(String plaintext, String hashed) {
|
||||
byte plaintextb[] = stringToBytes(plaintext);
|
||||
return checkpw(plaintextb, hashed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a plaintext byte[] password matches a previously hashed
|
||||
* one
|
||||
* @param plaintext the plaintext password to verify
|
||||
* @param hashed the previously-hashed password
|
||||
* @return true if the passwords match, false otherwise
|
||||
*/
|
||||
public static boolean checkpw(byte[] plaintext, String hashed) {
|
||||
byte hashed_bytes[];
|
||||
byte try_bytes[];
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user