一 问题由来
对于Passport,微软解释道:Passport 是一种服务。利用该服务,用户可以通过只创建一个登录名和密码就能登录到 Microsoft Passport Network 站点和服务。通常来讲,当您在 Passport Network 上创建帐户时,Passport 只收集和存储用户名(通常是您的电子邮件地址)和密码,以创建您的凭据。如果您通过移动电话访问 Passport 服务,我们会要求您提供您的电话号码用作登录名,并要求您创建一个 PIN 用作您的密码。
在注册时,Passport 将一个 64 位数字的唯一标识符 (ID) 与每个 Passport Network 帐户关联。当您选择登录到 Passport Network 站点时,Passport 将此独一无二的标识符加密并发送到该站点,便于该站点用其判断从一个登录会话转到另一个会话的人员是否是同一个人。
二 微软的摘要算法
与Passport类似的想法,两年前,也使用了类似方法来提供统一系统登录入口,这个服务是由java语言实现的,而系统中有些部分是用.Net技术实现,因此要保证这个服务具有良好的互操作性.
在保证了服务实现遵循WS-I协议标准之后,遇到了如下问题:
因密码都经过了哈希处理,java中的哈希和.Net中的哈希对于相同的串产生了不同的输出.这个问题令人印象深刻,所以把它记录了下来.今天把它贴出来供有需要的人们参考.
以C#为例,首先看看.Net的哈希算法实现,
HashAlgorithm 类,声明为
public abstract class HashAlgorithm : ICryptoTransform, IDisposable
表示所有加密哈希算法实现均必须从中派生的基类,目前,微软提供了派生自这个类的类包括:
System.Security.Cryptography.KeyedHashAlgorithm
System.Security.Cryptography.MD5
System.Security.Cryptography.SHA1
System.Security.Cryptography.SHA256
System.Security.Cryptography.SHA384
System.Security.Cryptography.SHA512
它们都是抽象类,申明为:public abstract class MD5 : HashAlgorithm,其它依此类推.
其中,KeyedHashAlgorithm为键控哈希算法的抽象类,微软提供了两个实现:
System.Security.Cryptography.HMACSHA1
System.Security.Cryptography.MACTripleDES
键控哈希算法是依赖于密钥的单向哈希函数,用作消息验证代码。只有知道密钥的人才能验证哈希值。加密哈希算法提供没有机密的真实性。
最后的实现,微软称为加密服务提供程序(CSP),例如MD5的CSP是MD5CryptoServiceProvider类.
到这里,可以认为HashAlgorithm类及其实现形成了策略模式,加密服务提供者提供服务和修改服务不会影响客户代码.
对于使用,有两种方式,一种是可直接创建的MD5 类的实例.如
MD5 md5=new MD5CryptoServiceProvider();
byte[] hashvalue=md5.ComputeHash(data2ToHash);
另一种是利用CryptoConfig类访问加密配置信息来提供相应的算法.它支持很多加密算法:
SHA,MD5,SHA256,SHA384,SHA512,RSA,DSA,DES,TripleDES,RC2,Rijndael.
例如由加密配置系统返回的MD5 实例:
byte[] hashvalue1 = ((HashAlgorithm) CryptoConfig.CreateFromName("MD5")).ComputeHash(data1ToHash);
到这里CryptoConfig类和HashAlgorithm类及其实现形成了工厂方法模式,而CryptoConfig就是一个抽象工厂,用于创建不同的加密算法对象,客户代码通过CryptoConfig访问加密算法实现.
三 java的哈希算法实现
java.security.MessageDigest是一个工厂类,使用工厂方法getInstance即可获得不同的加密算法,并通过MessageDigest访问,例如:
MessageDigest ha=MessageDigest.getInstance("SHA");
MessageDigest ha=MessageDigest.getInstance("MD5");
具体实现由算法服务提供者提供.在使用方面两种环境都比较简洁.
四 测试两种环境下的摘要算法
运行下面两个类即可了,类清单为:
java代码:
package security;
import java.security.MessageDigest;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
/**
*
* <p>Title: </p>
*
* <p>Description:
* 用于同C#语言实现的摘要算法对比.
* </p>
*
* <p>Copyright: Copyright (c) 2005</p>
*
* <p>Company: </p>
*
* @author not attributable
* @version 1.0
*/
public class HACompareJC {
public HACompareJC() {
}
public static void main(String[] args) throws Exception {
HACompareJC hacomparejc = new HACompareJC();
String data = "锐意";
String ha = "SHA";
System.out.println(hacomparejc.testHA1(data, ha));
System.out.println(hacomparejc.testHA2(data, ha));
System.out.println(hacomparejc.testHA3(data, ha));
}
/**
* ??>}G?y????V=
* @param data String
* @throws Exception
*/
public String testHA1(String data, String ha) throws Exception {
byte[] buffer = data.getBytes("UTF8");
MessageDigest messageDigest = MessageDigest.getInstance(ha);
messageDigest.update(buffer);
String s = new String(messageDigest.digest(), "UTF8");
//System.out.println(s);
return new String(s);
}
/**
* 对哈希值使用base64编码.
* sxjuhT59R/2peb2UtspWPQ==
* @param data String
* @throws Exception
*/
public String testHA2(String data, String ha) throws Exception {
byte[] buffer = data.getBytes("UTF8");
MessageDigest messageDigest = MessageDigest.getInstance(ha);
messageDigest.update(buffer);
buffer = messageDigest.digest();
String s = new sun.misc.BASE64Encoder().encode(buffer);
//System.out.println(s);
return s;
}
/**
* 这个是和C#中的处理结果是相同的.
* 4b-5d-11-c9-96-2d-62-92-b7-9b-60-e3-c9-a4-47-5c
* @param data String
* @return String
*/
public String testHA3(String data, String ha) {
MessageDigest m = null;
try {
m = MessageDigest.getInstance(ha);
m.update(data.getBytes("gb2312"));
}
catch (NoSuchAlgorithmException ex1) {
}
catch (UnsupportedEncodingException ex) {
}
return toHex(m.digest());
}
/**
* 把传来的字节数组取底8位转换到十六进制表示的字符串,并返回.
* @param buffer byte[]
* @return String
*/
private String toHex(byte[] buffer) {
String result = "";
for (int i = 0; i < buffer.length; i++) {
result += Integer.toHexString( (0x000000ff & buffer[i]) |
0xffffff00).substring(6) + "-";
}
return result.substring(0, result.length() - 1);
}
}
C#代码:
using System;
using System.IO ;
using System.Security.Cryptography;
using System.Text ;
using System.ClassLibrary ;
namespace endecrypt
{
/// <summary>
/// HACompareJC 的摘要说明。
/// 用于同java语言实现的摘要算法对比.
/// </summary>
public class HACompareJC
{
public HACompareJC()
{
}
/// <summary>
/// .% H]+4TW2&
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string testHA1(string data,string am)
{
byte[] buffer=Encoding.UTF8.GetBytes(data);
//MD5 ha=new System.Security.Cryptography.MD5CryptoServiceProvider();
HashAlgorithm ha=(HashAlgorithm) CryptoConfig.CreateFromName(am.ToUpper());
buffer=ha.ComputeHash(buffer);
return Encoding.UTF8.GetString(ha.ComputeHash(buffer));
}
/// <summary>
/// 对哈希值使用base64编码.
/// sxjuhT59R/2peb2UtspWPQ==
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string testHA2(string data,string am)
{
byte[] buffer=Encoding.UTF8.GetBytes(data);
//MD5 ha=new System.Security.Cryptography.MD5CryptoServiceProvider();
HashAlgorithm ha=(HashAlgorithm) CryptoConfig.CreateFromName(am.ToUpper());
buffer=ha.ComputeHash(buffer);
return Convert.ToBase64String (buffer);
}
/// <summary>
/// 4b-5d-11-c9-96-2d-62-92-b7-9b-60-e3-c9-a4-47-5c
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string testHA3(string data,string am)
{
Encoding encode=Encoding.GetEncoding("gb2312");
byte[] buffer=encode.GetBytes(data);
//MD5 ha=new System.Security.Cryptography.MD5CryptoServiceProvider();
HashAlgorithm ha=(HashAlgorithm) CryptoConfig.CreateFromName(am.ToUpper());
buffer=ha.ComputeHash(buffer);
return toHex(buffer);
}
/// <summary>
/// 把传来的字节数组取底8位转换到十六进制表示的字符串,并返回.
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
private static string toHex(byte[] buffer)
{
String result = "";
for (int i = 0; i < buffer.Length; i++)
{
//result += Integer.toHexString((0x000000ff & buffer[i])|0xffffff00).substring(6) + "-";
uint tm=UInt32.Parse(Convert.ToString((0x000000ff & buffer[i])|0xffffff00));
result +=tm.ToString("x").Substring(6)+"-";
}
return result.Substring(0, result.Length - 1);
}
public static void Main()
{
string data="锐意";
string ha="sHA1";
string tm=HACompareJC.testHA1(data,ha);
log.logs(tm);
tm=HACompareJC.testHA2(data,ha);
log.logs(tm);
tm=HACompareJC.testHA3(data,ha);
log.logs(tm);
}
}
}
通过在网络中传输到客户端的密码哈希值,同在内存中比较由登录用户提供的口令哈希值(或者是由在本地的系统将口令计算为哈希值,实际上那个系统就是采用了这种方式,才引出这个问题)来确定是否允许该用户进入系统.