其实很早就想介绍这个Windows内置的ActiveX/COM组件,我前面有一篇文章介绍如何借用.NET Framework实现类似于SHA1和MD5哈希算法,如果大家对此感兴趣也可以看一下。
好,言归正传,今天我要介绍的是如何让VBScript利用Windows原生自带的CAPICOM组件来实现随机数、Hash(MD5/SHA1)以及Base64算法,当然在VBScript的世界里,这些算法都有免组件的纯代码实现,当然使用组件的好处就是代码简洁和运行效率的提高,所以我们不妨尝试一下。
既然今天介绍的主角是CAPICOM,当然我们要推荐参考微软的MSDN文档,大家不妨先大致浏览一番,可能大家很快发现下面这段提示:
CAPICOM is a 32-bit only component that is available for use in the following operating systems: Windows Server 2008, Windows Vista and Windows XP. Instead, use the .NET Framework to implement security features.
由此可见非常遗憾的是CAPICOM仅仅是32位Windows XP、Windows Vista以及Windows 2008系统上才自带(对于64位系统可以参考文章在64位系统中使用CAPICOM),所以在包含CAPICOM特性代码部署到服务器上之前需要测试一下当前系统是否支持,如果不支持只有考虑.NET或者原生代码的实现了。
CAPICOM有很多对象(Objects),这里我也不一一介绍,主要关注的是CAPICOM.Utilities
和CAPICOM.HashedData
。本文也是通过这两个对象实现所需要的算法功能的。
CAPICOM.Utilities
实现随机数
Function GetRandom() Dim objCapiCom Set objCapiCom = CreateObject("CAPICOM.Utilities.1") GetRandom = objCapiCom.GetRandom(32, -1) Set objCapiCom = Nothing End Function |
可能大家要问为什么VBScript语言自带随机数功能,我们还需要使用CAPICOM来生成随机数,实际上我们通过脚本语言生成的随机数是伪随机数,当然这些伪随机数可以应用于一些要求不高的场合,伪随机数是通过算法和数学运算生成的模拟随机数,对于加密这类应用来说这种随机数是不安全的,而真正的随机数是通过硬件噪音生成的,比如在Linux及其他类Unix系统下,可以通过/dev/random
或者/dev/urandom
获得,因为这里我们实现的系统是Windows,所以可以变通的通过CAPICOM获取。
大家可能注意到上述代码objCapiCom.GetRandom(32, -1)
中的第二个参数-1
,微软给出的定义如下:
Const CAPICOM_ENCODE_ANY = -1 Const CAPICOM_ENCODE_BASE64 = 0 Const CAPICOM_ENCODE_BINARY = 1 |
关于这些常量定义的实际含义可以参考MSDN文档。
实现Base64编码和解码
在文章《Base64 の処理》有很好的例子。我简写如下:
Class Base64 Private objCapiCom Private Sub Class_Initialize() Set objCapiCom = CreateObject("CAPICOM.Utilities") End Sub Private Sub Class_Terminate() Set objCapiCom = Nothing End Sub Public Function encode(str) encode = objCapiCom.Base64Encode(str) End Function Public Function decode(str) decode = objCapiCom.Base64Decode(str) End Function End Class ' Set objBase64 = New Base64 ' MsgBox objBase64.decode(objBase64.encode("Hello World")) ' Set objBase64 = Nothing |
原文还介绍了如何对而进制文件进行Base64编解码,用到了ByteArrayToBinaryString
和BinaryStringToByteArray
方法:
<!--METADATA TYPE="TypeLib" UUID="{EF53050B-882E-4776-B643-EDA472E8E3F2}" --> <% Call Response.AddHeader( "Content-Type", "text/html; Charset=shift_jis" ) Set Stream = Server.CreateObject("ADODB.Stream") Set CAPIUtil = Server.CreateObject( "CAPICOM.Utilities" ) Stream.Open Stream.Type = adTypeBinary Stream.LoadFromFile Server.MapPath( "sample.jpg" ) strFile = CAPIUtil.ByteArrayToBinaryString( Stream.Read ) Stream.Close strEncoded = CAPIUtil.Base64Encode( strFile ) ' バイナリデータを通常文字列として処理 strDecoded = CAPIUtil.Base64Decode( strEncoded ) strFile2 = CAPIUtil.BinaryStringToByteArray( strDecoded ) Stream.Open Stream.Type = adTypeBinary Stream.Write strFile2 Stream.SaveToFile Server.MapPath("sample2.jpg"), _ adSaveCreateOverWrite Stream.Close %> |
关于Base64的其他实现办法,可以参考我前面的一篇文章《Base24、Base64编码的几种程序实现》。
CAPICOM.HashedData
实现常用的哈希函数
这里主要参考了《CAPICOM.dll을 이용한 Classic ASP(VBScript)에서 MD5 암호화 적용하기》,用到了一个Unicode字符串转换为Byte字符串的函数UStr2Bstr
,当然为了便于理解,我还找到了逆函数BStr2UStr
:
Function BStr2UStr(BStr) 'Byte string to Unicode string conversion Dim lngLoop BStr2UStr = "" For lngLoop = 1 to LenB(BStr) BStr2UStr = BStr2UStr & Chr(AscB(MidB(BStr,lngLoop,1))) Next End Function Function UStr2Bstr(UStr) 'Unicode string to Byte string conversion Dim lngLoop Dim strChar UStr2Bstr = "" For lngLoop = 1 to Len(UStr) strChar = Mid(UStr, lngLoop, 1) UStr2Bstr = UStr2Bstr & ChrB(AscB(strChar)) Next End Function |
比如常见的MD5实现代码如下:
Function MD5(str) Dim objCapiCom Set objCapiCom = CreateObject("CAPICOM.HashedData") objCapiCom.Algorithm = 3 objCapiCom.Hash UStr2Bstr(str) MD5 = objCapiCom.Value Set objCapiCom = Nothing End Function ' MsgBox MD5("123456") |
同样的大家也许注意到了objCapiCom.Algorithm
,属性3指明了所使用的哈西算法,主要的算法定义有以下列表,这些同样可以从MSDN中找到:
Const CAPICOM_HASH_ALGORITHM_SHA1 = 0 Const CAPICOM_HASH_ALGORITHM_MD2 = 1 Const CAPICOM_HASH_ALGORITHM_MD4 = 2 Const CAPICOM_HASH_ALGORITHM_MD5 = 3 Const CAPICOM_HASH_ALGORITHM_SHA256 = 4 Const CAPICOM_HASH_ALGORITHM_SHA384 = 5 Const CAPICOM_HASH_ALGORITHM_SHA512 = 6 |
同Base64编码解码一样,哈希函数也可以对二进制数据进行操作,具体做法是直接使用Hash
方法分块哈希二进制数据,具体可以参考Demon为我们提供的代码:
Function md5_file(filename, raw_output) Dim HashedData, Utility, Stream Set HashedData = CreateObject("CAPICOM.HashedData") Set Utility = CreateObject("CAPICOM.Utilities") Set Stream = CreateObject("ADODB.Stream") HashedData.Algorithm = 3 Stream.Type = 1 Stream.Open Stream.LoadFromFile filename Do Until Stream.EOS HashedData.Hash Stream.Read(1024) Loop If raw_output Then md5_file = Utility.HexToBinary(HashedData.Value) Else md5_file = HashedData.Value End If End Function |