【问题标题】:Same hashing + encryption in different platforms generating unidentical values不同平台中的相同哈希 + 加密生成不同的值
【发布时间】:2013-05-12 16:12:28
【问题描述】:

我正在为一个社交网站编写一些网络服务。这些网络服务将被 android 用于制作 android-app。由于设计网站的人已经没有联系了,我看了一下整个网站的代码,都是用java用spring框架写的。我正在用 php 编写 Web 服务。

现在,当我尝试向 php 页面发送一个 post 请求时,该页面将确认给定的用户名和密码组合是否正确,然后返回一个会话 ID。但是我无法获得正确的哈希方法来获得保存在数据库中的正确哈希值。 因此,每次我都被 php 代码拒绝。

我在网站代码上找到的加密如下:

public static final synchronized String encrypt(String plaintext, String algorithm, String encoding) throws Exception
{
  MessageDigest msgDigest = null;
  String hashValue = null;
  try
  {
    msgDigest = MessageDigest.getInstance(algorithm);
    msgDigest.update(plaintext.getBytes(encoding));
    byte rawByte[] = msgDigest.digest();
    hashValue = (new BASE64Encoder()).encode(rawByte);

  }
  catch (NoSuchAlgorithmException e)
  {
    System.out.println("No Such Algorithm Exists");
  }
  catch (UnsupportedEncodingException e)
  {
    System.out.println("The Encoding Is Not Supported");
  }
  return hashValue;
}

例如,如果我将密码作为 monkey123 作为密码,它给出的哈希值以 base 64 编码为:hge2WiM7vlaTTS1qU404+Q==

现在,在 php 中苦苦挣扎了几个小时后,我意识到我可以在 android 本身中执行上述过程。所以,我写了以下代码:

MessageDigest pwdDigest=MessageDigest.getInstance("MD5");
pwdDigest.update(password.getBytes("UTF-16"));
byte rawbyte[]=pwdDigest.digest();
String passwordHash=Base64.encodeToString(rawbyte,Base64.DEFAULT);


URL url = new URL(loginURL);

HttpURLConnection Connection = (HttpURLConnection) url.openConnection();

Connection.setReadTimeout(10000);
Connection.setAllowUserInteraction(false);

Connection.setDoOutput(true);

//set the request to POST and send

Connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

DataOutputStream out = new DataOutputStream(Connection.getOutputStream());
out.writeBytes("username=" + URLEncoder.encode(username, "UTF-8"));
out.writeBytes("&password="+URLEncoder.encode(passwordHash,"UTF-8"));
out.flush();
out.close();
if(Connection.getResponseCode()==200){
  String data="Connected";            
  return data;
} else 
  return Connection.getResponseCode()+": "+Connection.getResponseMessage();

我预计这会成功,因为在这两种情况下,我都在执行相同的过程来加密密码,但令人惊讶的是,这并没有给出哈希值: hge2WiM7vlaTTS1qU404+Q== 但它给了:nZlvVe7GSS2Zso1dOwJrIA==

我真的很难找出这两者不同的原因。任何帮助将不胜感激。

【问题讨论】:

  • 您在网站代码中使用的编码是什么?
  • 加密函数调用为:public String getEncryptPassword() { try { return MyPasswordEncrypt.encrypt(getPassword(), "MD5", "UTF-16"); } catch (Exception e) { e.printStackTrace(); return encryptPassword; } }
  • 使用 UTF-16 后,您将其发送为 UTF-8 编码。这可能是可能的问题。
  • 我也尝试将android发布代码更改为UTF-16,这也无济于事..
  • 您是否也像在android应用程序中一样,再次对网站代码进行编码。

标签: java android encryption hash


【解决方案1】:

我不希望 MD5 在平台之间有所不同。它是稳定且有据可查的,并且是核心库的一部分。如果这在某些 Android 版本中被破坏,那么该手机将无法正常工作。

重新编码为 UTF-8 是无害的,因为所有 Base64 字符都适合较低的 ASCII 范围。 base64 字母表的三个字符需要 URL 编码,但如果出现问题,您会看到 %-转义。

Base64 的基础不太稳定(有很多不同的实现,没有一个规范的实现),但它也不完全是火箭科学。同样,我不认为错误的实现会真正流行起来,但 Base64 步骤可能是差异出现的地方。

就个人而言,我怀疑该错误是在password.getBytes("UTF-16") 调用期间引入的。验证这种预感的一种快速方法是在两个平台上的调试器中检查生成的字节数组。 根据java.lang.Charset,“UTF-16”编码将使用大端字节顺序,而您的 PHP 代码可能默认为小端,因为它在 x86 上运行并且不存在字节顺序标记(我不太了解 PHP,无法判断此行为是否定义明确)。尝试修改 Java 代码以使用 password.getBytes("UTF-16LE") 并查看是否有所不同。

旁注:MD5 不再被认为对散列密码是安全的;你会想要使用 scrypt 或 PBKDF2 之类的东西,有很多轮次和随机盐,但这本身就是一个话题。

【讨论】:

  • 巴伦德,我完全同意你的看法。事实上,我已经检查了两种情况下的输出 password.getBytes("UTF-16") 会给出什么,但它并不相同。因此,我可以确认它不是 base64 或 md5 导致问题。它是 UTF-16。在 android 中,password.getBytes("UTF-16") 给出数组值:-1 -2 49 0 49 0 52 0 49 0 52 0 50 0 49 0 97 0 98 0 99 0 而在 java 中,它给出数组值如:-2 -1 0 49 0 49 0 52 0 49 0 52 0 50 0 49 0 97 0 98 0 99 所以,这似乎是问题所在..对此有何看法?
  • Barend,我已经解决了这个问题。正如你所看到的,数组的值只是相邻放错了位置,我只是交换了这些值,以便它生成正确的哈希值。它确实如此。谢谢你的帮助。:)
猜你喜欢
  • 2017-11-15
  • 2015-06-12
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
  • 1970-01-01
  • 2012-01-18
  • 2015-02-25
  • 2012-03-19
相关资源
最近更新 更多