Android中的MD5哈希


91

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

MD5哈希在C#中是微不足道的,可以为我的需求提供足够的安全性,但是我似乎找不到在android端如何做到这一点。

编辑:只是为了解决有关MD5弱点的担忧-C#服务器在我的Android客户端用户的PC上运行。在许多情况下,他们将在自己的LAN上使用wi-fi访问服务器,但风险自负,他们可能选择从Internet访问服务器。服务器上的服务也需要使用MD5直通到我无法控制的第三方应用程序。


6
不要使用MD5。使用SHA512。
SLaks 2011年

1
为什么?SHA512不比MD5难。您不想从现在开始五年后就被使用MD5的旧客户端所困扰。
SLaks 2011年

2
我希望您在协议中使用随机数,这样您就可以丢弃重播攻击。
sarnold 2011年

1
@NickJohnson:要回答您的问题,为什么您会故意选择较弱的选项?还有另一个问题...您为什么要对我16个月前发布的问题发表评论?但是,如果您真的想知道(如果您查看对SLaks的评论),那是alpha阶段代码,PC端(不是我写的)使用了MD5哈希。基本上,此要求是不涉及额外复杂性的直通方案。当时我大约有10位Alpha阶段测试人员知道这些风险。自从我问了这个问题以来,已经纳入了更复杂的安全性。
Squonk 2012年

1
...什么?不,这不仅是错误的,而且是危险的错误。
尼克·约翰逊

Answers:


230

是您可以使用的实现(已更新为使用更多最新的Java约定- for:each循环,StringBuilder而不是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 "";
}

尽管不建议甚至使用基本安全级别的系统(MD5 被认为是损坏的,并且很容易利用),但有时对于基本任务来说已经足够了。


谢谢,可以解决问题。请参阅我的编辑,以了解为什么MD5在此阶段就足够了。
Squonk 2011年

6
投票给IMO,以便在下面显示更正确的答案。
亚当

4
不能满足0,如下所示。
SohailAziz 2012年

更新为使用较新的Java标准(适用于:each,StringBuilder)
loeschg 2014年

3
是否有尚未实现MD5的Android操作系统(导致抛出NoSuchAlgorithmException)?
VSB

49

接受的答案在Android 2.2中对我不起作用。我不知道为什么,但是那是在“吃”我的一些零(0)。 Apache commons在Android 2.2上也不起作用,因为它使用了仅从Android 2.3.x开始才受支持的方法。另外,如果只想使用MD5字符串,那么Apache commons太复杂了。为什么要保留整个库以仅使用其中的一个小函数...

最终,我在这里找到了下面的代码段这些代码段对我来说非常合适。我希望这对某人有用...

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

4
为我工作。我更改了md5.getBytes(“ UTF-8”)。我已经使用以下代码检查了它:q4m'x68n6_YDB4ty8VC4&} wqBtn ^ 68W,结果为0c70bb931f03b75af1591f261eb77d0b,而不是c70bb931f03b75af1591f261eb77d0b。0到位
Inoy

27

androidsnippets.com代码无法可靠运行,因为似乎从结果哈希中剔除了0。

一个更好的实现在这里

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

在这种情况下,“ m”在Android上可以为空吗?
Android开发人员

3
@androiddeveloper你是完全正确的。catch块在此处的实现不佳,需要有一个return语句,否则在调用m.update时将出现空引用错误。我也不确定,但是虽然这可能不会在单个字节上去除0,但我认为它仍会在整个32个字符的哈希中去除前导0。我认为String.Format(“%032X”,bigInt)可以正常工作,而不是toString(16)。另外,您可以选择是否要使用大写或小写的十六进制(“%032x”表示小写)。
rsimp

1
这个版本有缺陷。如果您的MD5以“ 0”开头,则生成的MD5将不会有前导0。请不要使用此解决方案。
elcuco

20

如果可以选择使用Apache Commons Codec,那么它将是一个较短的实现:

String md5Hex = new String(Hex.encodeHex(DigestUtils.md5(data)));

或SHA:

String shaHex= new String(Hex.encodeHex(DigestUtils.sha("textToHash")));

以上来源

请点击链接并投票解决他的解决方案,以奖励正确的人。


Maven回购链接:https//mvnrepository.com/artifact/commons-codec/commons-codec

当前的Maven依赖关系(截至2016年7月6日):

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>

1
为什么要为标准库中已经存在的API使用外部库?
m0skit0

12

上面使用DigestUtils的解决方案对我不起作用。在我的Apache Commons版本(2013年的最新版本)中,没有此类。

在一个博客中找到了另一个解决方案。它工作完美,不需要Apache公共资源。它看起来比上面接受的答案中的代码短一点。

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

您将需要这些导入:

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

重要的是要知道id MD5哈希长度是32个字符或更少,谢谢!
Pelanes 2014年

3
最后几行可能只需替换为:return String.Format(“%032X”,number); 否则我真的很喜欢这个答案。
rsimp

10

这与上面的Andranik和Den Delimarsky的回答略有不同,但是它更加简洁,不需要任何按位逻辑。相反,它使用内置String.format方法将字节转换为两个字符的十六进制字符串(不去除0)。通常,我只会评论他们的答案,但我没有这样做的声誉。

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

如果您想返回一个小写的字符串,则只需更改%02X%02x

编辑:使用BigInteger就像wzbozon的答案一样,您可以使答案更加简洁:

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

4

我在科特林做了一个简单的图书馆。

在Root build.gradle添加

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

在App build.gradle

implementation 'com.github.1AboveAll:Hasher:-SNAPSHOT'

用法

在科特林

val ob = Hasher()

然后使用hash()方法

ob.hash("String_You_Want_To_Encode",Hasher.MD5)

ob.hash("String_You_Want_To_Encode",Hasher.SHA_1)

它将分别返回MD5和SHA-1。

有关图书馆的更多信息

https://github.com/ihimanshurawat/Hasher


2

这是@Andranik回答的Kotlin版本。我们需要更改getBytestoByteArray(不需要添加字符集UTF-8,因为默认字符集toByteArray为UTF-8)并将array [i]转换为整数

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
}

希望对你有帮助


1

MD5有点旧,SHA-1是更好的算法,这里有一个例子

正如他们在那篇文章中所指出的那样,Java自行处理此问题,没有Android特定的代码。


4
否-当我在2011年1月(19个月前)问这个问题时,我不想要MD5的替代品,而且我不确定为什么此时您需要回答我的问题。
2012年

21
@Squonk我回应了,因为那是stackoverflow背后的基本思想。无论事实发生了多长时间,请始终尝试为以后可能遇到此问题的人们获得更好的答案。至于建议SHA-1,我没有办法知道您特别反对SHA-1,但是很多其他人不会这样做,因此,它可能会在将来帮助其他遇到这种情况的人,并指导他们到更现代的算法。
亚当

3
我无法想到MD5是一个不好的选择,而SHA1是一个好的选择的一种情况。如果需要耐碰撞性,则需要SHA2而不是SHA1。如果您对密码进行哈希处理,则需要bcrypt或PBKDF2。或者在OP的情况下,正确的解决方案可能是SSL。
CodesInChaos

3
@Adam这不是答案,这是评论。
Antzi

1

在我们的MVC应用程序中,我们生成长参数

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

和Android应用程序中的相同(thenk帮助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();
}

1

我已经使用下面的方法通过传递想要获取md5的字符串给我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 "";
}

1

请使用SHA-512,MD5不安全

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

0

确实,在其他建议中,toHex()转换太浪费了。

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

当任务是关于MD5时,答案是关于“更好”的十六进制,因为它是无响应的。注意:Donald Knuth 真正的问题是程序员花了太多时间来担心在错误的地方和错误的时间执行效率。过早的优化是编程中所有邪恶(或至少是大多数邪恶)的根源。
zaph

0

这对我来说是完美的,我用它来获取LIST Array上的MD5(然后将其转换为JSON对象),但是如果您只需要将其应用于数据上。类型格式,用您的替换JsonObject。

特别是如果您与python MD5实现不匹配,请使用此功能!

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;


}

-1

为Scala语言提供的解决方案(稍短一些):

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
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.