【问题标题】:MD5 hashing in AndroidAndroid中的MD5哈希
【发布时间】:2011-06-18 07:17:56
【问题描述】:

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

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

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

【问题讨论】:

  • 不要使用 MD5。使用 SHA512。
  • 为什么? SHA512 并不比 MD5 难。您不希望五年后仍被使用 MD5 的旧客户端所困。
  • 我希望你在你的协议中使用一个随机数,这样你就可以抛弃重放攻击。
  • @NickJohnson :回答你的问题你为什么要故意选择较弱的选项?还有另一个问题......你为什么觉得有必要对我发布的问题发表评论16个月前?但是,如果您真的想知道(如果您查看上面对 SLaks 的评论),那是 alpha 阶段代码,而 PC 端(不是我写的)使用了 MD5 散列。该要求基本上是针对不涉及额外复杂性的直通场景。当时我有大约 10 个 alpha 阶段的测试人员,他们知道这些风险。自从我提出这个问题以来,已经加入了更复杂的安全性。
  • ...什么?不,这不仅是错误的,而且是非常危险的错误。

标签: android cryptography md5


【解决方案1】:

Here 是您可以使用的实现(更新为使用更多最新的 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 is considered broken and can be easily exploited) 的系统,但有时对于基本任务来说已经足够了。

【讨论】:

  • 感谢您的帮助。请参阅我的编辑,了解为什么 MD5 在这个阶段就足够了。
  • 投票否决 IMO,以便出现下面更正确的答案。
  • 0 不满足,如下所示。
  • 更新为使用较新的 java 标准(for:each, StringBuilder)
  • 有没有没有实现MD5的android操作系统(导致NoSuchAlgorithmException抛出)?
【解决方案2】:

androidsn-ps.com 代码无法可靠运行,因为 0 似乎已从生成的哈希中删除。

更好的实现是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;
}

【讨论】:

  • 在这种情况下,“m”在 Android 上是否可以为空?
  • @androiddeveloper 你完全正确。此处catch块实现不佳,需要有return语句,否则调用m.update时会出现空引用错误。我也不确定,但是虽然这可能不会去除单个字节上的 0,但我认为它可能仍然会去除整个 32 字符散列上的前导 0。而不是 toString(16) 我认为 String.Format("%032X", bigInt) 按预期工作。另外,您可以选择是否要使用大写或小写的十六进制(“%032x”表示小写)。
  • 这个版本有缺陷。如果你的 MD5 以“0”开头,生成的 MD5 不会有前导 0。请不要使用此方案。
【解决方案3】:

MD5有点老了,SHA-1是更好的算法,there is a example here

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

【讨论】:

  • 不 - 当我在 2011 年 1 月(19 个月前)提出这个问题时,我不想要 MD5 的替代品,我不确定您为什么觉得此时需要回答我的问题.
  • @Squonk 我回复了,因为这是 stackoverflow 背后的总体思路。无论事实发生多久,始终尝试为以后可能遇到问题的人提供更好的答案。至于建议SHA-1,我没办法知道你是专门反对SHA-1的,但很多人不会,所以再说一次,它可能会帮助将来遇到这个问题的其他人,并指导他们到更现代的算法。
  • 我想不出一个单一的情况,MD5 是一个糟糕的选择,而 SHA1 是一个好的选择。如果您需要抗碰撞性,则需要 SHA2 而不是 SHA1。如果您散列密码,则需要 bcrypt 或 PBKDF2。或者在 OP 的情况下,正确的解决方案可能是 SSL。
  • @Adam 这不是答案,这是评论。
【解决方案4】:

如果使用 Apache Commons Codec 是一个选项,那么这将是一个更短的实现:

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

或 SHA:

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

Source 以上。

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


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>

【讨论】:

  • 您为什么要为标准库中已经存在的 API 使用外部库?
  • 这是唯一一个可以使用而不觉得我是 90 年代的答案 ;-) 一个不错的单线,而其他解决方案看起来像黑客。
【解决方案5】:

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

我找到了另一个解决方案here in one blog。它运行完美,不需要 Apache commons。它看起来比上面接受的答案中的代码短一点。

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;

【讨论】:

  • 重要的是要知道 MD5 哈希的长度是 32 个字符或更少,谢谢!
  • 最后几行可能只是替换为:return String.Format("%032X", number);否则我真的很喜欢这个答案。
【解决方案6】:

接受的答案在 Android 2.2 中对我不起作用。我不知道为什么,但它正在“吃掉”我的一些零 (0) 。 Apache commons 也不适用于 Android 2.2,因为它使用的方法仅支持从 Android 2.3.x 开始。此外,如果您只想对一个字符串进行 MD5,则 Apache commons 太复杂了。为什么要保留整个库以仅使用其中的一个小功能...

最后我找到了以下代码 sn-p here 对我来说非常有效。我希望它对某人有用...

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

【讨论】:

  • 为我工作。我已更改 md5.getBytes("UTF-8")。我用上面的代码检查了:q4m'x68n6_YDB4ty8VC4&}wqBtn^68W,结果是0c70bb931f03b75af1591f261eb77d0b,而不是c70bb931f03b75af1591f261eb77d0b。 0 到位
【解决方案7】:

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

【讨论】:

    【解决方案8】:

    在我们的 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 应用程序中也是如此(然后帮助 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();
    }
    

    【讨论】:

      【解决方案9】:

      我使用下面的方法通过传递你想要获取 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 "";
      }
      

      【讨论】:

        【解决方案10】:

        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 时,答案是关于“更好”的 toHex,因此它是无响应的。注意:Donald Knuth 真正的问题是程序员在错误的地方和错误的时间花费了太多时间来担心效率。过早的优化是编程中万恶之源(或至少是大部分)。
        【解决方案11】:

        这是上面 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

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

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

        【讨论】:

          【解决方案12】:

          我在 Kotlin 中制作了一个简单的库。

          在根 build.gradle 添加

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

          在 App build.gradle

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

          用法

          在 Kotlin 中

          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

          【讨论】:

            【解决方案13】:

            请使用 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;
            }
            

            【讨论】:

              【解决方案14】:

              这是来自@Andranik 答案的 Kotlin 版本。 我们需要将getBytes更改为toByteArray(不需要添加字符集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
              }
              

              希望对你有帮助

              【讨论】:

                【解决方案15】:

                这对我来说非常有效,我使用它在 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;
                
                
                }
                

                【讨论】:

                  【解决方案16】:

                  有用的 Kotlin 扩展函数示例

                  fun String.toMD5(): String {
                      val bytes = MessageDigest.getInstance("MD5").digest(this.toByteArray())
                      return bytes.toHex()
                  }
                  
                  fun ByteArray.toHex(): String {
                      return joinToString("") { "%02x".format(it) }
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2012-08-17
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-12-08
                    • 2019-04-24
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多