【发布时间】:2013-12-02 09:45:24
【问题描述】:
检查更新#1
此逻辑是身份验证过程的候选者,由简单的 HTTP 请求完成:
- 我正在发送:userName + encrypted_userName(encrypted_userName 实际上是 userName 的加密结果,使用 AES 完成 & 作为密钥,我使用密码的 md5 哈希)。注意:我不会发送 md5 哈希密码。
- 在我比较的服务器上:encrypted_userName 和 own_encrypted_userName(因为在服务器上我可以访问用户的完整信息,所以我计算自己的 encrypted_userName)。
问题:这是一个安全漏洞吗?假设坏人捕获了完整的 HTTP 请求,他可以从这 2 个信息中提取密码吗?
代码详情(如果需要):
private static Cipher getCipher(String key, int mode) throws Exception{
byte[] rawKey = getRawKey(key.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Key key2 = skeySpec;
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(mode, key2);
return cipher;
}
private static byte[] getRawKey(byte[] seed) throws Exception {
/* BEFORE:
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
*/
byte[] raw = MD5Util.getMD5HashRaw(seed);
return raw;
}
(注意:我之所以使用密码的哈希是因为代码在平台之间兼容(客户端是Android设备),而注释版本不是)
更新#1
简答:
所呈现的逻辑甚至不能被视为一种安全的身份验证机制 (为什么?在下面查看迈克尔的答案)
决定使用 Kerberos(而不是 https,因为我不熟悉 + 似乎设置起来很复杂):
它不是真正的 Kerberos 版本(如 v4 或 v5),它只是我自己的实现,所以我们称之为“与 Kerberos 类似”(我知道,我知道:不要“滚动你自己的加密”!!! ),
这里有一些细节:
- 它适用于 UDP(现在)
-
身份验证只进行一次,由:
- 客户端发送 Authenticator 消息(包含:[userId] 纯文本和 [something_ecrypted] 和 [entered_user_password](目前 [something_ecrypted] 仅包含时间戳,称之为 [authenticator_creation_timestamp]))注意 : 密码不传输
- 服务器收到消息后,尝试使用 [actual_user_password] 解密 [something_ecrypted] -> 如果成功,则客户端就是它假装的那个人,所以我给他发回一个 OK 响应(如在 Kerberos 中,此响应包含一些内容,就像一个 [public_key](一个 RSA 密钥,但使用 user_password 加密)+票证授予票证(称之为 [TGT],使用只有服务器知道的密码加密,目前它不会过期,这个 [TGT] 还包含一些东西,比如这 2 个时间戳:[TGT_creation_time_stamp] + [authenticator_creation_timestamp](在 Authenticator 消息中收到的那个))
- 在收到此 OK 消息后,客户已经获得了一个有效的 [public_key].. 太好了!
-
防止“回复攻击”不是 100% 的保证,但我认为它“足够安全”:
- 在每个下一个 HTTP 请求中,我将这 2 个家伙 [new_request_creation_timestamp](使用 [public_key] 加密,上面采购的)+ [TGT](未触及,如上所述)作为标题附加
-
在服务器上,我只需要验证 [new_request_creation_timestamp] 和一些数学(显然 [TGT] 也需要有效):
** 我希望以下变量几乎相等
delta1 = [TGT_creation_time_stamp] - [authenticator_creation_timestamp]
delta2 = now()-[new_request_creation_timestamp]
(我实际上允许它们之间有 5 秒的差异,但从我的测试来看,它只是大约 10-20 毫秒,
** 所以初始增量(在创建对 Authenticator 的 OK 响应时计算)应该在下一次交互中持续存在。
我确实觉得这种新方法非常值得信赖,但是如果您有意见或发现逻辑上的 BUG,请分享.. 谢谢
【问题讨论】:
-
这个方案的问题是,如果可以用加密后的用户名代替密码,那么基本上就是密码了。您通过未加密、未验证的连接发送它。您必须使用 HTTPS 才能使此方案起作用。请参阅迈克尔的回答。
-
不要使用 SecureRandom#setSeed() 来派生密钥。见stackoverflow.com/questions/13433529/…
标签: java android security aes md5