How to create same token for same input in Java - java

I am a beginner in java, I need to generate a token which should be same for the same input, otherwise it should be different.

You can simply get integer hash code by String.hashCode() but my suggestion is better to use SHA Hashing algorithm(MD5 is outdated) ( read this link for more about hashing). Following code snippets may help you to get hash string which is unique.
Strign text="text input"
java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(text.getBytes("UTF-8"));
String uniqueValue=bin2hex(hash); //To Convert binary to hexadecimal string
static String bin2hex(byte[] data) {
return String.format("%0" + (data.length * 2) + 'x', new BigInteger(1, data));
}

Related

Is it possible to create a deterministic secure random string based on some input from the client?

Two scenarios:
Say my user provides me the following:
long firstId;
long secondId;
or
UUID firstId;
long secondId;
and I have a variable to define a secret string:
private String mySecret = "2m75eB4xmAtMrz5cYQdSCch9R5R3xU6G";
Can I use the user inputs to generate a secure random string on the fly without hardcoding a string in the code (which seems pretty insecure)? If my program restarts and I provide the same input, I should get the same secret generated.
When you talk about security you should give some info about the context and what are you tryng to do, for random string i suggest a digest:
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(
originalString.getBytes(StandardCharsets.UTF_8));
private static String bytesToHex(byte[] hash) {
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
You can use as input string something like:
String input = mySecret + (firstId + secondId);
Given the same input this digest will give you always the same result but you would be exposed to an attack that is called dictionary attack, in this way a cracker that know what algorithm you are using "sha-256" can retrieve the original secret from the digest.

Can plaintexts be set or converted into salts in BCrypt?

I have a requirement for our password encryption exercise where the salt for a password is to be static and set as (business_id+business_start_date) values depending on the client's business id and start date. In the BCrypt documentation, it's said that BCrypt has salts built into the generated hashes to prevent rainbow table attacks. Most examples use the gensalt(int log_rounds) function.
IMO, I definitely would use a dynamic salt like others would do, as it is much easier to implement. However, if the insistence of implementing a static salt hash remains, is there a way to get BCrypt to accept static hashes OR; if not possible, what other encryptions can I use to for that requirement?
The application is mainly 80% reading content, with a little amount of create, update, delete operations.
I just did a test where I attempted to hash the password with the static salt.
This method is used in the BCrypt utility class:
public static String hashPassWord(String textPassword, String salt){
String hashPassword = BCrypt.hashpw(textPassword, salt);
return hashPassword;
}
The salt, which I was testing was in plain text eg.(busId:3,businessDate:2019-02-04)
String salt = new StringBuilder(busId).append(businessDate).toString();
I also had this method as standby, with the number of rounds (workload) set to 10.
public static String hashPassword(String textPassword){
String salt = BCrypt.gensalt(workload);
String hashPassword = BCrypt.hashpw(textPassword, salt);
return hashPassword;
}
The Invalid Salt Version error gets thrown into the exception when the hashpw() function was executed.
I've implemented base on kelalaka's comment. This is Bcrypt library always requires a formated salt. Depending on your plaintext size, if it is less then the BCRYPT_SALT_LEN,The rest of rnd filled with random bytes, the rest as in the library.
public static String gensalt(int log_rounds, SecureRandom random, String plaintextSalt) {
byte[] plaintextByte = plaintextSalt.getBytes();
byte rnd[] = new byte[BCRYPT_SALT_LEN];
//Use all of the string if size >= of the reqired rnd size
if (plaintextByte.length >= BCRYPT_SALT_LEN) {
System.arraycopy(plaintextByte, 0, rnd, 0, rnd.length);
} else {
//copy all of the string byte array
System.arraycopy(plaintextByte, 0, rnd, 0, plaintextByte.length);
//fill the rest with random
byte[] tempArray = new byte[BCRYPT_SALT_LEN - plaintextByte];
random.nextBytes(tempArray);
System.arraycopy(tempArray, 0, rnd, plaintextByte.length, temp.length);
}
StringBuffer rs = new StringBuffer();
rs.append("$2a$");
if (log_rounds < 10)
rs.append("0");
if (log_rounds > 30) {
throw new IllegalArgumentException(
"log_rounds exceeds maximum (30)");
}
rs.append(Integer.toString(log_rounds));
rs.append("$");
rs.append(encode_base64(rnd, rnd.length));
return rs.toString();
}

Java to PHP code difference

I've been given a chunk of code which describes an "algorithm" for token generation. This code is written in Java and works as is correctly. The given Java code must remain exactly as is, however, my requirement is to have the same "algorithm" used within a PHP application and I'm having a hard time retrieving the same result.
Java code:
public static void main(String[] args)
throws
UnsupportedEncodingException,
NoSuchAlgorithmException {
// Let's stick to a fixed date for this SO question
//DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
//dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
//String date = dateFormat.format(new Date());
String date = "201603251605";
String name = "some_dummy_data_one";
String size = "some_dummy_data_two";
// MD5 'name'
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(name.getBytes());
byte[] md5name = md5.digest();
// What happens here is beyond me - How would one translate this to PHP?
byte[] sizeBytes = (size + date).getBytes();
byte[] tokenBytes = new byte[md5name.length + sizeBytes.length];
System.arraycopy(sizeBytes, 0, tokenBytes, 0, sizeBytes.length);
System.arraycopy(md5name, 0, tokenBytes, sizeBytes.length, md5name.length);
// SHA256
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update(tokenBytes);
byte[] tokenHash = sha256.digest();
System.out.println(Base64.getEncoder().encodeToString(tokenHash));
}
This prints:
LsPw/5/4uKmQfVaU1LRASxG89mgMt7OxX+h7JGRo1ZU=
Ultimately and ideally I'd love a quick way to just get the same result with PHP code, but I've the urge to understand what's going on instead of just reaching the desired result.
Now to attempt to display that I have at least done a little homework and I'm not just seeking someone to do my work for me, up until the MD5 conversion of the strings, I've managed to get an identical base64 encoded SHA256 string of a single string skipping the middle chunk of code containing the 'System.arrayCopy' bit:
Java code:
public static void main(String[] args)
throws
UnsupportedEncodingException,
NoSuchAlgorithmException {
String date = "201603251605";
// MD5 'name'
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(date.getBytes());
byte[] md5Date = md5.digest();
// SHA256
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update(md5Date);
byte[] tokenHash = sha256.digest();
System.out.println(Base64.getEncoder().encodeToString(tokenHash));
}
PHP code:
private function generateToken() {
$md5Date = md5("201603251605", true);
$shaString = hash("sha256", $md5Date, true);
return base64_encode($shaString);
}
Java output:
mEkkRlBkwyoWFMsA+v/hP/m9sD6FdzM6LZHIORtr260=
PHP output:
mEkkRlBkwyoWFMsA+v/hP/m9sD6FdzM6LZHIORtr260=
So according to the above, my md5 and sha of a single string works fine, but that's not the problem. The problem is understanding what is happening when multiple strings are defined and are being mashed together in the Java code. Why does it seem overly complicated and is there a simple way of doing it in PHP?
Could somebody please explain what is happening in between the MD5 and SHA256 generation so that I can translate it to PHP code? I've attempted to read up on the Java System.arrayCopy documentation, but I feel as though I'm not experienced/smart enough to understand what is going on there. If there's a simple solution to this, I'd appreciate it very much.
You'll kick yourself when you realize how simple what it's actually doing is.
<?php
$date = "201603251605";
$name = "some_dummy_data_one";
$size = "some_dummy_data_two";
$md5_name = md5($name, true);
$token_bytes = $size.$date.$md5_name;
$token_hash = hash("sha256", $token_bytes, true);
echo base64_encode($token_hash);
arraycopy is just a pretty confusing way of appending size, date and md5name. Why the Java code is so complicated I have no idea.
Sounds like you got the answer for how to do it in PHP. The java code does basically the same thing. Here's what it's doing...
// This concatenates size+date and the md5 hash, and then creates a SHA-256 hash from that value.
byte[] sizeBytes = (size + date).getBytes(); //Get byte array of size and date concatenation
byte[] tokenBytes = new byte[md5name.length + sizeBytes.length]; //Create the right size byte array to fit md5 and size+date
System.arraycopy(sizeBytes, 0, tokenBytes, 0, sizeBytes.length); // copies the size+date byte array into the first part of the token bytes byte array.
System.arraycopy(md5name, 0, tokenBytes, sizeBytes.length, md5name.length); //concatenates the md5 hash and size+bytes into the tokenBytes array
// SHA256
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");//Get the SHA-256 algorithm instance
sha256.update(tokenBytes); // give it a value to hash
byte[] tokenHash = sha256.digest(); // create SHA-256 hash from token bytes

getting a negative value on md5

Im trying to get an MD5 with java and I receive a negative value.
Can an MD5 result in a negative value?
This is my code:
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] sigBytes = md5.digest((sharedSecret+"api_key"+API_KEY).getBytes());
api_sig = new BigInteger(sigBytes).toString(16);
Thank You.
new BigInteger(sigBytes) is interpreting your signature as a signed integer, so yes, it would be possible to get a negative number. If you want Your string to contain the hex representation of your md5 value, have a look at some of the answers here: How can I generate an MD5 hash?
As jbowes alluded to, new BigInteger(sigBytes) is interpreting the signature as a signed integer (meaning the first bit denotes whether the number is positive or negative). If you want it to interpret the bytes as an unsigned number, you should use new BigInteger(1, sigBytes) instead.
I use following method to get right MD5 hash String (and it never gave me "negative" value):
public static String createMD5Hash(String input) {
try {
MessageDigest m = MessageDigest.getInstance("MD5");
byte[] out = m.digest(input.getBytes());
return new String(Base64.encodeBase64(out));
} catch (NoSuchAlgorithmException e) {
return null;
}
}
Note that Base64 is class from apache commons:
import org.apache.commons.codec.binary.Base64;

Can you get this same Java SHA-1 in PHP please?

I find myself in a need to change website platforms from Java to PHP but I'd like to keep all my user's passwords...
I had this code do the password hashing prior to writting the hashed value as the password to the website:
MessageDigest md = null;
md = MessageDigest.getInstance("SHA");
md.update(plaintext.getBytes("UTF-8"));
byte raw[] = md.digest();
hash = new Base64().encodeToString(raw).replaceAll("\n", "").replaceAll("\r", "");
I think the Java code did SHA-1 hashing of the password but just prior to that it was byte encoded to UTF-8 and afterwards it was Base64 encoded.
I'd like to have a PHP code do the same, i.e. return the same value of a hash for the same password as in Java, only it seems that the PHP code doing SHA-1 hashing I have won't return the same SHA(-1, not Base64 encoded, I think?) value when compared to a Java Base64 decoded value of the hash...could it have something to do with the fact that my passwords in PHP are not UTF-8 byte encoded first (and how can I do that in PHP) please?
p.s.
Another strange thing...my passwords in Java are all 28characters long (usually something like this rnwn4zTNgH30l4pP8V05lRVGmF4=)...but the Base64().decode(hash) value of those password hashes is 10 characters long (an example [B#14e1f2b).
I thought Base64 did an additional 1 character to each 3 charters (28 or 27, excluding the padding = charter, is much more that a third larger than those 10 charcters) so am I doing the decoding call wrong somehow maybe???
And on top of all that the SHA-1 password hashed values in PHP are 40 characters long (in a UTF-8 mysql database) like so dd94709528bb1c83d08f3088d4043f4742891f4f?
[B#14e1f2b is definitely not a hash. It's a result of implicit conversion from byte[] to String.
It looks like you do something like this:
String decodedHash = Base64().decode(hash); // Produces [B#14e1f2b
However, the correct representation of the hash is a byte array:
byte[] decodedHash = Base64().decode(hash);
What I normally do with Java to compute a SHA-1 hash that is exactly identical to the PHP sha1() function is the following. The key is that toHexString is used to show the raw bytes in a printable way. If you use the PHP function and want to obtain the same result of your convoluted process, you need to use the parameter $raw_output to true in PHP to get the raw bytes and apply Base64. Full source code.
/**
* Compute a SHA-1 hash of a String argument
*
* #param arg the UTF-8 String to encode
* #return the sha1 hash as a string.
*/
public static String computeSha1OfString(String arg) {
try {
return computeSha1OfByteArray(arg.getBytes(("UTF-8")));
} catch (UnsupportedEncodingException ex) {
throw new UnsupportedOperationException(ex);
}
}
private static String computeSha1OfByteArray(byte[] arg) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(arg);
byte[] res = md.digest();
return toHexString(res);
} catch (NoSuchAlgorithmException ex) {
throw new UnsupportedOperationException(ex);
}
}
private static String toHexString(byte[] v) {
StringBuilder sb = new StringBuilder(v.length * 2);
for (int i = 0; i < v.length; i++) {
int b = v[i] & 0xFF;
sb.append(HEX_DIGITS.charAt(b >>> 4)).append(HEX_DIGITS.charAt(b & 0xF));
}
return sb.toString();
}
PHP's sha1() encodes each byte of the output as hexadecimal by default, but you can get the raw output by passing true as the second argument:
$digest = sha1($password, true); // This returns the same string of bytes as md.digest()
Then pass the digest to base64_encode and you are done:
base64_encode(sha1($password, true));
This returns the exact same SHA-1 hash as your java code.

Categories

Resources