一,哈希表(Hashtable)简述
在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key/value的键值对,其中key通常可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。Hashtable中key/value键值对均为object类型,所以Hashtable可以支持任何类型的key/value键值对.
二,哈希表的简单操作
在哈希表中添加一个key/value键值对:HashtableObject.Add(key,value);
在哈希表中去除某个key/value键值对:HashtableObject.Remove(key);
从哈希表中移除所有元素: HashtableObject.Clear();
判断哈希表是否包含特定键key: HashtableObject.Contains(key);
下面控制台程序将包含以上所有操作:
using System;
using System.Collections; //使用Hashtable时,必须引入这个命名空间
class hashtable
{
public static void Main()
{
Hashtable ht=new Hashtable(); //创建一个Hashtable实例
ht.Add("E","e");//添加key/value键值对
ht.Add("A","a");
ht.Add("C","c");
ht.Add("B","b");
string s=(string)ht["A"];
if(ht.Contains("E")) //判断哈希表是否包含特定键,其返回值为true或false
Console.WriteLine("the E key:exist");
ht.Remove("C");//移除一个key/value键值对
Console.WriteLine(ht["A"]);//此处输出a
ht.Clear();//移除所有元素
Console.WriteLine(ht["A"]); //此处将不会有任何输出
}
}
三,遍历哈希表
遍历哈希表需要用到DictionaryEntry Object,代码如下:
for(DictionaryEntry de in ht) //ht为一个Hashtable实例
{
Console.WriteLine(de.Key);//de.Key对应于key/value键值对key
Console.WriteLine(de.Value);//de.Key对应于key/value键值对value
}
四,对哈希表进行排序
对哈希表进行排序在这里的定义是对key/value键值对中的key按一定规则重新排列,但是实际上这个定义是不能实现的,因为我们无法直接在Hashtable进行对key进行重新排列,如果需要Hashtable提供某种规则的输出,可以采用一种变通的做法:
ArrayList akeys=new ArrayList(ht.Keys); //别忘了导入System.Collections
akeys.Sort(); //按字母顺序进行排序
for(string skey in akeys)
{
Console.Write(skey + ":");
Console.WriteLine(ht[skey]);//排序后输出
}
using System;
using System.IO;
using System.Security.Cryptography;
namespace Common
{
///
///Copyright (C), 2004, kwklover(邝伟科)
///File name:Hasher.cs
///Author:邝伟科 Version:1.0 Date:2004年4月22日
///Description:哈希(不可逆)加密通用类库函数
///
public class Hasher
{
private byte[] _HashKey; //哈希密钥存储变量
private string _HashText; //待加密的字符串
public Hasher()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
///
/// 哈希密钥
///
public byte[] HashKey
{
set
{
_HashKey=value;
}
get
{
return _HashKey;
}
}
///
/// 需要产生加密哈希的字符串
///
public string HashText
{
set
{
_HashText=value;
}
get
{
return _HashText;
}
}
///
/// 使用HMACSHA1类产生长度为 20 字节的哈希序列。需提供相应的密钥,接受任何大小的密钥。
///
///
public string HMACSHA1Hasher()
{
byte[] HmacKey=HashKey;
byte[] HmacData=System.Text.Encoding.UTF8.GetBytes(HashText);
HMACSHA1 Hmac = new HMACSHA1(HmacKey);
CryptoStream cs = new CryptoStream(Stream.Null, Hmac, CryptoStreamMode.Write);
cs.Write(HmacData, 0, HmacData.Length);
cs.Close();
byte[] Result=Hmac.Hash;
return Convert.ToBase64String(Result); //返回长度为28字节字符串
}
///
/// 使用MACTripleDES类产生长度为 8 字节的哈希序列。需提供相应的密钥,密钥长度可为 8、16 或 24 字节的密钥。
///
///
public string MACTripleDESHasher()
{
byte[] MacKey=HashKey;
byte[] MacData=System.Text.Encoding.UTF8.GetBytes(HashText);
MACTripleDES Mac=new MACTripleDES(MacKey);
byte[] Result=Mac.ComputeHash(MacData);
return Convert.ToBase64String(Result); //返回长度为12字节字符串
}
///
/// 使用MD5CryptoServiceProvider类产生哈希值。不需要提供密钥。
///
///
public string MD5Hasher()
{
byte[] MD5Data=System.Text.Encoding.UTF8.GetBytes(HashText);
MD5 Md5=new MD5CryptoServiceProvider();
byte[] Result=Md5.ComputeHash(MD5Data);
return Convert.ToBase64String(Result); //返回长度为25字节字符串
}
///
/// 使用SHA1Managed类产生长度为160位哈希值。不需要提供密钥。
///
///
public string SHA1ManagedHasher()
{
byte[] SHA1Data=System.Text.Encoding.UTF8.GetBytes(HashText);
SHA1Managed Sha1=new SHA1Managed();
byte[] Result=Sha1.ComputeHash(SHA1Data);
return Convert.ToBase64String(Result); //返回长度为28字节的字符串
}
///
/// 使用SHA256Managed类产生长度为256位哈希值。不需要提供密钥。
///
///
public string SHA256ManagedHasher()
{
byte[] SHA256Data=System.Text.Encoding.UTF8.GetBytes(HashText);
SHA256Managed Sha256=new SHA256Managed();
byte[] Result=Sha256.ComputeHash(SHA256Data);
return Convert.ToBase64String(Result); //返回长度为44字节的字符串
}
///
/// 使用SHA384Managed类产生长度为384位哈希值。不需要提供密钥。
///
///
public string SHA384ManagedHasher()
{
byte[] SHA384Data=System.Text.Encoding.UTF8.GetBytes(HashText);
SHA384Managed Sha384=new SHA384Managed();
byte[] Result=Sha384.ComputeHash(SHA384Data);
return Convert.ToBase64String(Result); //返回长度为64字节的字符串
}
///
/// 使用SHA512Managed类产生长度为512位哈希值。不需要提供密钥。
///
///
public string SHA512ManagedHasher()
{
byte[] SHA512Data=System.Text.Encoding.UTF8.GetBytes(HashText);
SHA512Managed Sha512=new SHA512Managed();
byte[] Result=Sha512.ComputeHash(SHA512Data);
return Convert.ToBase64String(Result); //返回长度为88字节的字符串
}
}
}
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
namespace Common
{
///
/// 加密解密通用类库函数
///Copyright (C), 2004, kwklover(邝伟科)
///File name:Crypto.cs
///Author:邝伟科 Version:1.0 Date:2004年4月21日
///Description:可逆的通用对称加密解密函数集
///
public class Crypto
{
private string _CryptText; //待加密和解密的字符序列变量
private byte[] _CryptKey; //加密解密私钥变量
private byte[] _CryptIV; //加密解密初始化向量IV变量
///
/// 待加密或解密的字符序列
///
public string CryptText
{
set
{
_CryptText=value;
}
get
{
return _CryptText;
}
}
///
/// 加密私钥
///
public byte[] CryptKey
{
set
{
_CryptKey=value;
}
get
{
return _CryptKey;
}
}
///
/// 加密的初始化向量IV
///
public byte[] CryptIV
{
set
{
_CryptIV=value;
}
get
{
return _CryptIV;
}
}
public Crypto()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
///
/// 加密函数,用于对字符串进行加密。需要提供相应的密钥和IV。
///
///
public string Encrypt()
{
string strEnText=CryptText;
byte[] EnKey=CryptKey;
byte[] EnIV=CryptIV;
byte[] inputByteArray=System.Text.Encoding.UTF8.GetBytes(strEnText);
//此处也可以创建其他的解密类实例,但注意不同(长度)的加密类要求不同的密钥Key和初始化向量IV
RijndaelManaged RMCrypto = new RijndaelManaged();
MemoryStream ms=new MemoryStream();
CryptoStream cs=new CryptoStream(ms,RMCrypto.CreateEncryptor(EnKey,EnIV),CryptoStreamMode.Write);
cs.Write(inputByteArray,0,inputByteArray.Length);
cs.FlushFinalBlock();
return Convert.ToBase64String(ms.ToArray());
}
///
/// 解密函数,用于经过加密的字符序列进行加密。需要提供相应的密钥和IV。
///
///
public string Decrypt()
{
string strDeText=CryptText;
byte[] DeKey=CryptKey;
byte[] DeIV=CryptIV;
byte[] inputByteArray=Convert.FromBase64String(strDeText);
//此处也可以创建其他的解密类实例,但注意不同的加密类要求不同(长度)的密钥Key和初始化向量IV
RijndaelManaged RMCrypto = new RijndaelManaged();
MemoryStream ms=new MemoryStream();
CryptoStream cs=new CryptoStream(ms,RMCrypto.CreateDecryptor(DeKey,DeIV),CryptoStreamMode.Write);
cs.Write(inputByteArray,0,inputByteArray.Length);
cs.FlushFinalBlock();
return System.Text.Encoding.UTF8.GetString(ms.ToArray());
}
}
}
下面使用上面类代码加密解密的一些部分数据,我本人无法对这些数据进行归纳为有价值的表达,望各位不吝赐教:
加密前英文字母,数字长度范围 | 加密前中文字符长度范围 | 加密后长度大小 |
0-15 | 0-5 | 24 |
16-31 | 6-10 | 44 |
32-47 | 11-15 | 64 |
48-63 | 16-20 | 88 |
64-79 | 21-15 | 108 |
80-95 | 16-30 | 128 |
96-111 | 31-35 | 152 |
前一阵给公安局做项目,用到了公钥加密技术及对称密钥加密技术。信息通过3DES进行加密,而密钥通过RSA公钥体系传送。客户端使用CPU卡eKey进行解密。但是在系统编写过程中发现,.net中的RSA加密算法为了提高安全性,在待加密数据前要添加一些随机数,因此,使用.NET中的RSA加密算法一次最多加密117字节数据(多于117字节需要拆分成多段分别加密再连接起来),经过加密后得到一个长度为128字节的加密数据。但这对于需要进行收发双方身份确认的公钥体系来说会带来不少麻烦。在我的系统中,我需要通过以下步骤实现对用户会话密钥的网上加密传递:
加密过程:
1、对会话密钥添加随机数,补充到128位,
2、使用CA私钥解密,结果为128位数据,
3、对数据使用用户公钥加密,得到128位数据,通过网络传送。
解密过程:
1、使用用户私钥解密网上传送的128位数据;
2、将结果使用CA公钥加密;
3、去掉用来混淆的随机数,提取出会话密钥
但.net中的RSA加密最多只能对117字节数据进行操作,导致128位数据不得不分两部分进行处理,于是加密数据不断膨胀。为了解决这个问题,并使得RSA加密、解密过程与eKey上的过程相一致,我只好编写自己的RSA加密算法。
经过查找了大量资料后,我决定利用现成的 BigInteger 类。可以参考http://www.codeproject.com/csharp/biginteger.asp 得到更多的信息。利用 BigInteger,我添加了两个方法RSAEncrypt和RSADecrypt,实现RSA加密解密。这样就再也不用受117字节的限制了。
下面给出了两段程序,程序一是使用.Net自带RSA加密算法实现加密解密,不过 TextLength 属性一旦超过 117,系统将无法加密; 程序二是经过改造的系统,可以对128位的数据进行加密,没有了117的限制。程序二省略了BigInteger类,需要的话可以从http://www.codeproject.com/csharp/biginteger.asp下载,不要忘了注释其中的Main方法,否则在编译时会有一个编译错误,说有两个入口点(当然也可以在项目属性中指定一个入口点)。
程序一:
using System.Security.Cryptography;
using System.Text;
class OldRSA
... {
static void Main()
...{
int TextLength = 117;
byte[] encryptedData;
byte[] decryptedData;
string Key1 = "<RSAKeyValue><Modulus>4n6EJsx4qNFpp6h+wcPdJz8sSMMRJEVJaBQEGsOOBHKNePo/v3M94Nf89+zL5lLH7/LuRgcUfnizVIETH/z9+H/yDuM0F3fjImN3UtK1TK0ioFf0cVC9lnErbEoEjmkeQIVUJUC4c+BmqtTN6UrhFCY3R3zGp3feeGqORLjeKVc=</Modulus><Exponent>AQAB</Exponent><P>7w2qsVRBn168Ehc4V/fiPML+7WUkORRIJ9I8i21Fs5GlvYrja2CzBzPLKrAHumLOCLgd/qKj0iApF17471nfKw==</P><Q>8oztAlInRK1VDuVLHnPPcNQsehbP9IF5p+kwRu07sFGwAHnyeWuRG0EpebvbGOE/1KzpKqb/WU8vSN4OeauohQ==</Q><DP>DIh+5oUwW5av7ZLiFVqdtenTS8b9uzBhCBVxry2vddaxBdr+SWbse/gvMrG/9fmwK6zbhbopNJ8TCHKmQoZHuQ==</DP><DQ>6g96q/GxeUG3Qk+dBP8HIL9vSEX5Wd8UEigicV9/aS/7IwqLJgbama1xI8tXrBO6MDbIL2PGKF4UqEG5QEqZrQ==</DQ><InverseQ>nyx28u1fREiIgXgx2S5+PXbB8wq0xVxnE2G2Mt0vq9xQDHbaXEFpfznjNaga8AhVluNahqG5uRGRY3OgQONO4g==</InverseQ><D>PVKj1R1nTc3lHU+xgiTVq9qe0tR9v6RCy7sfoV9xBCM/ypF20Q8Sod3Y0Ad87U9ccssDWFJyagukAi0wUGjfGfalF8/4PFwqzrGBLsN96klmKLMy7C6oihlriW+MyxmvagGsp3/r4sE6wGk5ISchjKIKyv/PyWoobDRe6orDzIE=</D></RSAKeyValue>";
try
...{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.FromXmlString(Key1);
byte[] dataToEncrypt = GenerateBytes(TextLength);
Console.WriteLine("Original buff: " + Convert.ToBase64String(dataToEncrypt) + " ");
encryptedData = RSA.Encrypt(dataToEncrypt, false);
Console.WriteLine("Encrypted buff: " + Convert.ToBase64String(encryptedData) + " ");
decryptedData = RSA.Decrypt(encryptedData,false);
Console.WriteLine("Decrypted buff: " + Convert.ToBase64String(decryptedData) + " ");
}
catch
...{
Console.WriteLine("Encryption failed.");
}
}
//***********************************************************************
// 随机生成一指定长度的字节数组
//***********************************************************************
public static byte[] GenerateBytes(int byteLength)
...{
byte[] buff = new Byte[byteLength];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
// 该数组已使用密码增强的随机字节进行填充
rng.GetBytes(buff);
return buff;
}
}
程序二:
using System.Security.Cryptography;
using System.Text;
class NewRSA
... {
public static void Main()
...{
int TextLength = 128;
byte[] encryptedData;
byte[] decryptedData;
string Key1 = "<RSAKeyValue><Modulus>4n6EJsx4qNFpp6h+wcPdJz8sSMMRJEVJaBQEGsOOBHKNePo/v3M94Nf89+zL5lLH7/LuRgcUfnizVIETH/z9+H/yDuM0F3fjImN3UtK1TK0ioFf0cVC9lnErbEoEjmkeQIVUJUC4c+BmqtTN6UrhFCY3R3zGp3feeGqORLjeKVc=</Modulus><Exponent>AQAB</Exponent><P>7w2qsVRBn168Ehc4V/fiPML+7WUkORRIJ9I8i21Fs5GlvYrja2CzBzPLKrAHumLOCLgd/qKj0iApF17471nfKw==</P><Q>8oztAlInRK1VDuVLHnPPcNQsehbP9IF5p+kwRu07sFGwAHnyeWuRG0EpebvbGOE/1KzpKqb/WU8vSN4OeauohQ==</Q><DP>DIh+5oUwW5av7ZLiFVqdtenTS8b9uzBhCBVxry2vddaxBdr+SWbse/gvMrG/9fmwK6zbhbopNJ8TCHKmQoZHuQ==</DP><DQ>6g96q/GxeUG3Qk+dBP8HIL9vSEX5Wd8UEigicV9/aS/7IwqLJgbama1xI8tXrBO6MDbIL2PGKF4UqEG5QEqZrQ==</DQ><InverseQ>nyx28u1fREiIgXgx2S5+PXbB8wq0xVxnE2G2Mt0vq9xQDHbaXEFpfznjNaga8AhVluNahqG5uRGRY3OgQONO4g==</InverseQ><D>PVKj1R1nTc3lHU+xgiTVq9qe0tR9v6RCy7sfoV9xBCM/ypF20Q8Sod3Y0Ad87U9ccssDWFJyagukAi0wUGjfGfalF8/4PFwqzrGBLsN96klmKLMy7C6oihlriW+MyxmvagGsp3/r4sE6wGk5ISchjKIKyv/PyWoobDRe6orDzIE=</D></RSAKeyValue>";
try
...{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.FromXmlString(Key1);
RSAParameters RSAKeyInfo = RSA.ExportParameters(true);
byte[] dataToEncrypt = GenerateBytes(TextLength);
Console.WriteLine("Original buff: " + Convert.ToBase64String(dataToEncrypt) + " ");
encryptedData = RSAEncrypt(dataToEncrypt, RSAKeyInfo.Exponent, RSAKeyInfo.Modulus);
Console.WriteLine("Encrypted buff: " + Convert.ToBase64String(encryptedData) + " ");
decryptedData = RSADecrypt(encryptedData, RSAKeyInfo.D, RSAKeyInfo.Modulus);
Console.WriteLine("Decrypted buff: " + Convert.ToBase64String(decryptedData) + " ");
}
catch
...{
Console.WriteLine("Encryption failed.");
}
}
//***********************************************************************
// RSA Encrypt
//***********************************************************************
static public byte[] RSAEncrypt(byte[] dataToEncrypt, byte[] Exponent, byte[] Modulus)
...{
BigInteger original = new BigInteger(dataToEncrypt);
BigInteger e = new BigInteger(Exponent);
BigInteger n = new BigInteger(Modulus);
BigInteger encrypted = original.modPow(e,n);
return HexStringToByte(encrypted.ToHexString());
}
//***********************************************************************
// RSA Decrypt
//***********************************************************************
static public byte[] RSADecrypt(byte[] encryptedData, byte[] D, byte[] Modulus)
...{
BigInteger encrypted = new BigInteger(encryptedData);
BigInteger d = new BigInteger(D);
BigInteger n = new BigInteger(Modulus);
BigInteger decrypted = encrypted.modPow(d,n);
return HexStringToByte(decrypted.ToHexString());
}
//***********************************************************************
// 将 HexString 转换为 byte[] 数组
//***********************************************************************
static public byte[] HexStringToByte(string hexString)
...{
byte[] byteResult = new byte[hexString.Length/2];
for(int i = 0; i < hexString.Length/2; i++)
byteResult[i] = Convert.ToByte(hexString.Substring(i*2,2),16);
return byteResult;
}
//***********************************************************************
// 随机生成一指定长度的字节数组
//***********************************************************************
public static byte[] GenerateBytes(int byteLength)
...{
byte[] buff = new Byte[byteLength];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
// 该数组已使用密码增强的随机字节进行填充
rng.GetBytes(buff);
return buff;
}
}