I have a simple android client which needs to 'talk' to a simple C# HTTP listener. I want to provide a basic level of authentication by passing username/password in POST requests.
MD5 hashing is trivial in C# and provides enough security for my needs but I can't seem to find how to do this at the android end.
EDIT: Just to address the concerns raised about MD5 weakness - the C# server runs on the PCs of the users of my android client. In many cases, they'll be accessing the server using wi-fi on their own LANs but, at their own risk, they may choose to access it from the internet. Also the service on the server needs to use pass-through for the MD5 to a 3rd party application I have no control over.
Here is an implementation you can use (updated to use more up to date Java conventions - for:each loop, StringBuilder instead of StringBuffer):
public static String md5(final String s) {
final String MD5 = "MD5";
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest
.getInstance(MD5);
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuilder hexString = new StringBuilder();
for (byte aMessageDigest : messageDigest) {
String h = Integer.toHexString(0xFF & aMessageDigest);
while (h.length() < 2)
h = "0" + h;
hexString.append(h);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
Although it is not recommended for systems that involve even the basic level of security (MD5 is considered broken and can be easily exploited), it is sometimes enough for basic tasks.
The accepted answer didn't work for me in Android 2.2. I don't know why, but it was "eating" some of my zeros (0) .
Apache commons also didn't work on Android 2.2, because it uses methods that are supported only starting from Android 2.3.x. Also, if you want to just MD5 a string, Apache commons is too complex for that. Why one should keep a whole library to use just a small function from it...
Finally I found the following code snippet here which worked perfectly for me. I hope it will be useful for someone...
public String MD5(String md5) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(md5.getBytes("UTF-8"));
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; ++i) {
sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
} catch(UnsupportedEncodingException ex){
}
return null;
}
The androidsnippets.com code does not work reliably because 0's seem to be cut out of the resulting hash.
A better implementation is here.
public static String MD5_Hash(String s) {
MessageDigest m = null;
try {
m = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
m.update(s.getBytes(),0,s.length());
String hash = new BigInteger(1, m.digest()).toString(16);
return hash;
}
If using Apache Commons Codec is an option, then this would be a shorter implementation:
String md5Hex = new String(Hex.encodeHex(DigestUtils.md5(data)));
Or SHA:
String shaHex= new String(Hex.encodeHex(DigestUtils.sha("textToHash")));
Source for above.
Please follow the link and upvote his solution to award the correct person.
Maven repo link: https://mvnrepository.com/artifact/commons-codec/commons-codec
Current Maven dependency (as of 6 July 2016):
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
A solution above using DigestUtils didn't work for me. In my version of Apache commons (the latest one for 2013) there is no such class.
I found another solution here in one blog. It works perfect and doesn't need Apache commons. It looks a little shorter than the code in accepted answer above.
public static String getMd5Hash(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
BigInteger number = new BigInteger(1, messageDigest);
String md5 = number.toString(16);
while (md5.length() < 32)
md5 = "0" + md5;
return md5;
} catch (NoSuchAlgorithmException e) {
Log.e("MD5", e.getLocalizedMessage());
return null;
}
}
You will need these imports:
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
This is a slight variation of Andranik and Den Delimarsky answers above, but its a bit more concise and doesn't require any bitwise logic. Instead it uses the built-in String.format method to convert the bytes to two character hexadecimal strings (doesn't strip 0's). Normally I would just comment on their answers, but I don't have the reputation to do so.
public static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
StringBuilder hexString = new StringBuilder();
for (byte digestByte : md.digest(input.getBytes()))
hexString.append(String.format("%02X", digestByte));
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
If you'd like to return a lower case string instead, then just change %02X to %02x.
Edit:
Using BigInteger like with wzbozon's answer, you can make the answer even more concise:
public static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
BigInteger md5Data = new BigInteger(1, md.digest(input.getBytes()));
return String.Format("%032X", md5Data);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
Here is Kotlin version from #Andranik answer.
We need to change getBytes to toByteArray (don't need to add charset UTF-8 because the default charset of toByteArray is UTF-8) and cast array[i] to integer
fun String.md5(): String? {
try {
val md = MessageDigest.getInstance("MD5")
val array = md.digest(this.toByteArray())
val sb = StringBuffer()
for (i in array.indices) {
sb.append(Integer.toHexString(array[i].toInt() and 0xFF or 0x100).substring(1, 3))
}
return sb.toString()
} catch (e: java.security.NoSuchAlgorithmException) {
} catch (ex: UnsupportedEncodingException) {
}
return null
}
Hope it help
I have made a simple Library in Kotlin.
Add at Root build.gradle
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
at App build.gradle
implementation 'com.github.1AboveAll:Hasher:-SNAPSHOT'
Usage
In Kotlin
val ob = Hasher()
Then Use hash() method
ob.hash("String_You_Want_To_Encode",Hasher.MD5)
ob.hash("String_You_Want_To_Encode",Hasher.SHA_1)
It will return MD5 and SHA-1 Respectively.
More about the Library
https://github.com/ihimanshurawat/Hasher
Please use SHA-512, MD5 is insecure
public static String getSHA512SecurePassword(String passwordToHash) {
String generatedPassword = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update("everybreathyoutake".getBytes());
byte[] bytes = md.digest(passwordToHash.getBytes());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
generatedPassword = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return generatedPassword;
}
Useful Kotlin Extension Function Example
fun String.toMD5(): String {
val bytes = MessageDigest.getInstance("MD5").digest(this.toByteArray())
return bytes.toHex()
}
fun ByteArray.toHex(): String {
return joinToString("") { "%02x".format(it) }
}
In our MVC application we generate for long param
using System.Security.Cryptography;
using System.Text;
...
public static string getMD5(long id)
{
// convert
string result = (id ^ long.MaxValue).ToString("X") + "-ANY-TEXT";
using (MD5 md5Hash = MD5.Create())
{
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(result));
// Create a new Stringbuilder to collect the bytes and create a string.
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
sBuilder.Append(data[i].ToString("x2"));
// Return the hexadecimal string.
result = sBuilder.ToString().ToUpper();
}
return result;
}
and same in Android application (thenk helps Andranik)
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
...
public String getIdHash(long id){
String hash = null;
long intId = id ^ Long.MAX_VALUE;
String md5 = String.format("%X-ANY-TEXT", intId);
try {
MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] arr = md.digest(md5.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < arr.length; ++i)
sb.append(Integer.toHexString((arr[i] & 0xFF) | 0x100).substring(1,3));
hash = sb.toString();
} catch (NoSuchAlgorithmException e) {
Log.e("MD5", e.getMessage());
}
return hash.toUpperCase();
}
i have used below method to give me md5 by passing string for which you want to get md5
public static String getMd5Key(String password) {
// String password = "12131123984335";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password.getBytes());
byte byteData[] = md.digest();
//convert the byte to hex format method 1
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
System.out.println("Digest(in hex format):: " + sb.toString());
//convert the byte to hex format method 2
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);
}
System.out.println("Digest(in hex format):: " + hexString.toString());
return hexString.toString();
} catch (Exception e) {
// TODO: handle exception
}
return "";
}
MD5 is a bit old, SHA-1 is a better algorithm, there is a example here.
(Also as they note in that post, Java handles this on it's own, no Android specific code.)
Far too wasteful toHex() conversion prevails in other suggestions, really.
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String md5string(String s) {
return toHex(md5plain(s));
}
public static byte[] md5plain(String s) {
final String MD5 = "MD5";
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance(MD5);
digest.update(s.getBytes());
return digest.digest();
} catch (NoSuchAlgorithmException e) {
// never happens
e.printStackTrace();
return null;
}
}
public static String toHex(byte[] buf) {
char[] hexChars = new char[buf.length * 2];
int v;
for (int i = 0; i < buf.length; i++) {
v = buf[i] & 0xFF;
hexChars[i * 2] = HEX_ARRAY[v >>> 4];
hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
this is working perfectly for me, I used this to get MD5 on LIST Array(then convert it to JSON object), but if you only need to apply it on your data. type format, replace JsonObject with yours.
Especially if you have a mismatch with python MD5 implementation use this!
private static String md5(List<AccelerationSensor> sensor) {
Gson gson= new Gson();
byte[] JsonObject = new byte[0];
try {
JsonObject = gson.toJson(sensor).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
MessageDigest m = null;
try {
m = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] thedigest = m.digest(JsonObject);
String hash = String.format("%032x", new BigInteger(1, thedigest));
return hash;
}
The provided solutions for the Scala language (a little shorter):
def getMd5(content: Array[Byte]) =
try {
val md = MessageDigest.getInstance("MD5")
val bytes = md.digest(content)
bytes.map(b => Integer.toHexString((b + 0x100) % 0x100)).mkString
} catch {
case ex: Throwable => null
}
Related
I would like to offer a md5 verifier in my android app which compares the server md5 and the just created one on the device.
The output should be like correct or incorrect and not that the user has to compare the hashes.
I already found out that it's possible to get the hash on android via
/system/xbin/busybox md5sum /sdcard/Download/FILENAME
. Of cause I can print the output of the command to screen but that's not what I want.
Because I don't want to reinvent the wheel is something like that already available? SHA1 would be possible too, both hashes are available.
Please help!
I have used this method to calculate md5 inside of Android Application
private String getMD5(String file){
String md5 = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
FileInputStream is = this.openFileInput(file);
DigestInputStream dis = new DigestInputStream(is, md);
byte data[] = new byte[1024];
#SuppressWarnings("unused")
int count;
while ((count = dis.read(data)) != -1) {
}
byte[] digest = md.digest();
for (int i=0; i < digest.length; i++) {
md5 += Integer.toString( ( digest[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return md5;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return md5;
}
With "the server md5" I guess you mean another file on the server containing an md5 hash.
So you could just download the md5 file, open it and compare the string inside with your calculated md5.
public String generateKey(String title, String userName){
char[] hexDigits = "0123456789abcdef".toCharArray();
String source;
String MD5 = null;
byte[] digest = null;
source = title + "balh" + userName ;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
digest = md.digest(source.getBytes("UTF-16"));
StringBuilder sb = new StringBuilder(32);
for (byte b : digest)
{
sb.append(hexDigits[(b >> 4) & 0x0f]);
sb.append(hexDigits[b & 0x0f]);
}
System.out.println("Gened KEY ===="+sb.toString());
return sb.toString();
} catch (Exception e) {
}
return "";
}
I use the same code to generate key in android, and in Servlet. But I get different results. What am I doing wrong? Or if those are not compatible then how to make them.
I used the following method in both server and android client. It worked. But don't know what's the problem I had.
http://mobile.dzone.com/news/android-snippet-making-md5
I am trying to implement rest call with key and secret using md5. I tried a way but its giving me an error,
{"status":"error","message":"Signature mismatch"}.
Please help me, its very urjent!!
this is my md5 method,
private static final String md5(final String parem) {
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest
.getInstance("MD5");
digest.update(parem.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
String h = Integer.toHexString(0xFF & messageDigest[i]);
while (h.length() < 2)
h = "0" + h;
hexString.append(h);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
Thanks in advance.
I did try with one of my classes and found only one difference:
Yours:
Input: test
Output: 098f6bcd4621d373cade4e832627b4f6
Mine:
Input: test
Output: 098F6BCD4621D373CADE4E832627B4F6
So only one diff, the uppercase....
if that does not solve it, i would say have a look if your hashing the correct data.
I am trying to generate a SHA256 hash in android, that I then pass to an ASP.NET Web API web service and compare the hash there. As such, I need to construct a hash in Android, that given the same inputs in ASP.NET will generate an equivalent hash. I'm pulling my hair out trying to figure out what I'm doing wrong.
Here's the Android code:
public String computeHash(String input) throws NoSuchAlgorithmException{
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.reset();
try{
digest.update(input.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e){
e.printStackTrace();
}
byte[] byteData = digest.digest(input.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++){
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
Here's the C# code for the server:
private static string ComputeHash(string input, HashAlgorithm algorithm)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hashedBytes.Length; i++)
{
sb.Append(String.Format("{0:x2}", hashedBytes[i]));
}
return sb.ToString();
}
Your Java code is wrong: you are adding the input bytes twice. If you are calculating this in one go, you need to either call only digest(bytes) or call digest() after update(bytes);
I was looking for a Kotlin version for my Android app.
I could not find one; here is what I came up with:
fun String.getSha256(): String {
val digest = MessageDigest.getInstance("SHA-256").apply { reset() }
val byteData: ByteArray = digest.digest(this.toByteArray())
return StringBuffer().apply {
byteData.forEach {
append(((it.toInt() and 0xff) + 0x100).toString(16).substring(1))
}
}.toString()
}
Migrating OP's solution from the question to an answer:
Here is the corrected Android/Java implementation (based on Nikolay Elenkov's answer):
public String computeHash(String input) throws NoSuchAlgorithmException, UnsupportedEncodingException{
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.reset();
byte[] byteData = digest.digest(input.getBytes("UTF-8"));
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++){
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
I am parsing XML from URL in my application and first of all I shlould verify XML signature. Thus, my question is, is there any simple implementation of signing XML and it's verification on android?
Thanks
Do you mean somethin like this?
Make a request to the webservice that allows you to get the MD5 hash of the XML
Then get the XML
Make a MD5 hash from your XML from inside android
Compare the 2 to verify if you have the entire unmodified XML.
This is a nice string to MD5 converter:
private String md5(String in) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
digest.reset();
digest.update(in.getBytes());
byte[] a = digest.digest();
int len = a.length;
StringBuilder sb = new StringBuilder(len << 1);
for (int i = 0; i < len; i++) {
sb.append(Character.forDigit((a[i] & 0xf0) >> 4, 16));
sb.append(Character.forDigit(a[i] & 0x0f, 16));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
return null;
}