Android 中的 MD5哈希

我有一个简单的 android 客户端,它需要与一个简单的 C # HTTP 侦听器“通话”。我想通过在 POST 请求中传递用户名/密码来提供基本级别的身份验证。

MD5散列在 C # 中是微不足道的,并且为我的需要提供了足够的安全性,但是我似乎找不到在 android 端如何做到这一点。

编辑: 只是为了解决关于 MD5弱点的担忧—— C # 服务器运行在我的 android 客户端用户的 PC 上。在许多情况下,他们会在自己的局域网上使用 wi-fi 访问服务器,但是,在他们自己的风险下,他们可能会选择通过互联网访问服务器。此外,服务器上的服务需要使用 MD5到我无法控制的第三方应用程序的传递。

167175 次浏览

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 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;
}

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.)

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;

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 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
}

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 "";
}

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 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;
}
}

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;
}

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

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;




}

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) }
}