Hello everybody I am working on the first piece of communication between server and client of my game. Obviously due to the fact that I am starting from zero, I am projecting each part of the program carefully.
I was looking in Swing API and I found the JPasswordField that is a normal InputField, but for passwords.
It returns as you know a string if the deprecated method getText() is called or an array of chars if is called getPassword.
Reading in SO I understood that is not a good idea to use getText, nor something like
String password = String.valueOf(passwordField.getPassword());
because doing so I am creating a String that can stay in memory for long time.
What I tried to create is something that can convert that password without using strings and I created this:
public static String digest(char[] in) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
ArrayList<Byte> list = new ArrayList<Byte>();
for(int i = 0; i<in.length; i++){
String ch = String.valueOf(in[i]);
byte[] b = ch.getBytes();
for(int j = 0; j<b.length;j++){
list.add(b[j]);
}
}
byte[] inputInByte = new byte[list.size()];
for(int i =0;i<list.size();i++){
inputInByte[i] = list.get(i);
}
md.update(inputInByte);
byte byteData[] = md.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
String hex = Integer.toHexString(0xff & byteData[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
The question is: is this algorithm correct and good for the security of the password? I had to use a String to convert from char to byte.
Also I return an hashed string, is there any problem in that? It should be quite difficult to find the password starting from the hash ;)
How about database connection? Hsqldb allow me to create query, but each query is a string......
Using SHA-256 digest is a hash method, not a cryptologic one. It is like a fingerprint. Can you get a person from his fingerprints without testing everybody's (6 billions) fingerprint ? No. It used to store password in databases in php, for example. We just store the pass's hash, and when the user want to connect, we compute the newly entered password hash, and compare it with the database's hash.
This prevents users from stealing passwords if the database is hacked. But you cannot get the password from the hash. I hope i answered to your question.
By the way, consider using apache lib for message digest, it is easier and more safe i think
I think your code is quite ok, but you are still working with String to create the byte value, so you maybe better change String.valueOf(in[i]); to something like this:
public static String digest(char[] in) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
ArrayList<Byte> list = new ArrayList<Byte>();
for(int i = 0; i<in.length; i++){
byte b = (byte) in[i]
list.add(b);
}
byte[] inputInByte = new byte[list.size()];
for(int i =0;i<list.size();i++){
inputInByte[i] = list.get(i);
}
md.update(inputInByte);
byte byteData[] = md.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
String hex = Integer.toHexString(0xff & byteData[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
that is also easier than using that for cycle and two step conversion to byte.
Related
I'm trying to convert a hash function originally written in Java to Javascript in our codebase. However, I am getting different results.
Below is the code in Java
public static String hashText(String str) {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-512");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
messageDigest = null;
}
byte[] bytes = str.getBytes();
for (int i = 0; i < 64; i++) {
messageDigest.update(bytes);
bytes = messageDigest.digest();
}
hashedText = new String(Base64.encode(bytes, 2));
return hashedText.replace(StringUtils.LF, "");
}
And here is what I wrote in Javascript
function hashText(text){
const crypto = require('crypto')
const hash = crypto.createHash('sha512');
const digest = hash.update(text).digest();
return digest.toString("base64")
}
console.log(hashText(text))
I've been trying to figure out what I am doing wrong here but no success yet. I need help!
That for loop in the java, it looks like it's rehashing the hash 64 times. Looks like you're only hashing once in JavaScript. Here's an example of how you might do that loop in node.js.
function hashText(text, iter = 1) {
const crypto = require('crypto');
let digest = text;
for (let i = 0; i < iter; i++) {
const hash = crypto.createHash('sha512');
digest = hash.update(digest).digest();
}
return digest.toString('base64');
}
const text = 'asdf 1234 zxcv 5678';
console.log(hashText(text, 64));
*Edit: I don't use java, didn't know how MessageDigest.digest(), worked. Specifically:
... The digest is reset after this call is made.
In node, this means running createHash every iteration of the loop. Thanks #dave_thompson_085 for pointing this out!
It turns out the way nodejs crypto hash copy works is kinda different. As stated in the docs, it creates a new Hash object that contains a deep copy of the internal state of the current Hash object. However, what I need is to create a new hash with the encoded string.
Below is a sample of a working snippet
function hashText(text, length) {
const crypto = require('crypto');
let digest = text;
for (let i = 0; i < length; i++) {
const hash = crypto.createHash('sha512');
hash.update(digest);
digest = hash.digest();
}
return digest.toString('base64');
}
console.log(hashText(text, 64));
Thanks to everyone that helped one way or the other
I have a strange problem in getting equivalent hash code from C# code translated into Java. I don't know, what MessageDigest update method do. It should only update the contents of digest and should compute hash after calling digest.
Same thing I am doing in C# with SHAManaged512.ComputeHash(content). But I am not getting same hash code.
Following is the Java code.
public static String hash(String body, String secret) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(body.getBytes("UTF-8"));
byte[] bytes = md.digest(secret.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
} catch (Exception e) {
throw new RuntimeException();
}
}
Following is C# Code
private byte[] ComputeContentHash(string contentBody)
{
using (var shaM = new SHA512Managed())
{
var content = string.Concat(contentBody, Options.SecretKey);
var hashedValue = shaM.ComputeHash(ToJsonStream(content));
return hashedValue;
}
}
public static Stream ToJsonStream(object obj)
{
return new MemoryStream(Encoding.Unicode.GetBytes(obj.ToString()));
}
I had the exact same issue. Its been two years since you asked but just in case someone comes across this question, here's the solution
public static string encryptHash(string APIkey, string RequestBodyJson)
{
var secretBytes = Encoding.UTF8.GetBytes(APIkey);
var saltBytes = Encoding.UTF8.GetBytes(RequestBodyJson);
using (var sHA256 = new SHA256Managed())
{
byte[] bytes = sHA256.ComputeHash(saltBytes.Concat(secretBytes).ToArray());
//convert to hex
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
builder.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
return builder.ToString();
}
}
The solution was to put first Secret Key and concate it with pay load data.
Encoding.Unicode (which you are using in the C# ToJsonStream method) is not UTF8. It's UTF16. See MSDN. (Also keep in mind that UTF16 can be little or big endian.) You're looking for Encoding.UTF8.
First thing to do is check if the byte array you're hashing is the same.
private String getString(byte[] bytes)
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++)
{
byte b = bytes[i];
sb.append(0xFF & b);
}
return sb.toString();
}
public String encrypt(String source)
{
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(source.getBytes());
return getString(bytes);
}
catch (Exception e)
{
e.printStackTrace(); }
return null;
}
If my text = "test"
The First Part toString()) produces a value of "Encryption$2#6966b26b"
And the second part then gets that and produces a value of "91431072057033211115202222781313839180246"
But why is the md5 a number and not 31f521a06d5060d1f38159c74a1f7cf2 or something similar?
The function "encrypt()" returns a MD5 hash. You should rename it to "hash", because hashing != encrypting.
If you want to encrypt a string, you can look here: https://gist.github.com/bricef/2436364
It's clearly stated in code yuou are using MD5 hashing algorithm
Now your question is why:
But why is the md5 a number and not 31f521a06d5060d1f38159c74a1f7cf2 or something similar?
your answer is simple, look at code which generates you string from your byte array.
byte b = bytes[i];
sb.append(0xFF & b);
you take byte, ie 0x20 then you perform logical and operation with integer 0x255 and then you add decimal representation of result yo your StringBuilder.
What you want to do is more like
sb.append(Integer.toHexString(0xff&b));
I would say MD5 hash, because the code says MessageDigest.getInstance("MD5") :D
I want to implement SHA512 hashing using a salt. I started here, leading to this mcve:
import java.security.MessageDigest;
import org.junit.Test;
public class Sha512Mcve {
private final String ENCODING = "ISO-8859-1";
#Test
public void test() {
System.out.println(computeHashFor("whatever"));
}
private String computeHashFor(String toHash) {
String salt = "salt";
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-512");
// md.update(salt.getBytes(ENCODING));
byte[] bytes = md.digest(toHash.getBytes(ENCODING));
return toUnixRepresentation(salt, bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private String toUnixRepresentation(String salt, byte[] bytes) {
StringBuilder sb = new StringBuilder();
sb.append("$6$");
sb.append(salt);
sb.append("$");
for (int i = 0; i < bytes.length; i++) {
int c = bytes[i] & 0xFF;
if (c < 16) sb.append("0");
sb.append(Integer.toHexString(c));
}
return sb.toString();
}
}
Thing is: when I leave the line md.update() commented out, this code gives me the exact same results as some online hash generators (like this one).
For example, hashing the word "whatever" gives a hash value ae3d....63a.
But when I run my code with that salt operation; I get different results (again compared against that online tool, which allows to set a salt string, too).
My implementation results in 413...623; the online tool says F25...686.
Any explanation in which way "salting" leads to "implementation specific" results?
Is there something I should do differently in my code?
Salt before or after?
What the calculator does when you set the salt option
whateversalt
What you are doing in your code
saltwhatever
resutls from the calculator
whateversalt
F2527142C752B05467EE53B44735397F5B4C870DF0F154A0CF3AC23B31CF42EE7E1002D326B57DF60ED4B7449CF101290BDC0BECCB677AAAD846CFBE140DF686
saltwhatever
41333B9BAFC14CB3D1106D72A5D461F348B9EA1304A82989E00E5FC2D3239339492FCA12ED5EBF5F6802955C95B5F7ADA4CA035A911C2F29ABE905C3923CF623
Therefore to match the calculation you just have to reverse the order and add the salt last
md.update(toHash.getBytes(ENCODING));
byte[] bytes = md.digest(salt.getBytes(ENCODING));
Or even
md.update(toHash.getBytes(ENCODING));
md.update(salt.getBytes(ENCODING));
byte[] bytes = md.digest();
I know that there is a lot of similar topics, but still... can someone provide me a working example of method which generates MD5 String.
I'm currently using MessageDigest, and I'm doing the following to get a string
sun.misc.BASE64Encoder().encode(messageDigest.digest())
I guess there is some better way to do that.
Thanks in advance!
I'd use commons-codec
Base64 - Base64.encodeBase64(digestBytes)
Hex-string - Hex.encodeHex(digestBytes)
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] arr = md.digest(bytesOfMessage);
return Base64.getEncoder().encodeToString(arr);
note: md5 is not considered as good hash algorithm anymore, consider choosing SHAs
// Convert to hex string
StringBuffer sb = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
if ((0xff & messageDigest[i]) < 0x10) {
sb.append('0');
}
sb.append(Integer.toHexString(0xff & messageDigest[i]));
}
String md5 = sb.toString();
This assumes you actually want your MD5 printed as an hex string, not BASE64-encoded. That's the way it is normally represented.
I've seen next solution:
byte[] digest = md.digest(someDataByteArray);
StringBuilder hex = new StringBuilder();
for (byte b : digest) {
hex.append(String.format("%02x", b));
}
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
...
String input = "westerngun";
MessageDigest digest = MessageDigest.getInstance("MD5"); // not thread-safe, create instance for each thread
byte[] result = digest.digest(input.getBytes()); // get MD5 hash array, could contain negative
String hex = DatatypeConverter.printHexBinary(result).toLowerCase(); // convert byte array to hex string
If you want a number:
Integer number = Integer.parseInt(hex, 16); // parse hex number to integer. If overflowed, use Long.parseLong()