Microsoft Updater Application Block KeyValidator类设计
译者:
Tony Qu
KeyValidator类提供一个基于对称密钥的验证器,该章节将介绍KeyValidator设计的以下几个方面:
问题描述
设计目标、权衡和问题
解决方案描述
具体实现
问题描述
一个通常使用的验证方法是使用对称密钥进行签名和验证数据。为了支持这项技术,必须实现一个基于对称密钥的验证器。
设计目标、权衡和问题
1. KeyValidator应该支持由.net的KeyedHashAlgorithm类支持的对称密钥。
2. 开发人员应该能够在应用程序升级器的配置文件中指定密钥,该密钥应该用base64字符串存储。如果没有密钥被指定,KeyValidator会抛出一个异常。
解决方案描述
KeyValidator类派生自IValidator接口,它使用.net的KeyedHashAlgorithm类基于一个对称密钥生成和验证签名。
具体实现
KeyValidator类由
Microsoft.ApplicationBlocks.ApplicationUpdater.Validators命名空间实现。
实现KeyValidator需要考虑四个方面:
1. 密钥初始化
2. 签名
3. 验证
4. KeyValidator配置
密钥初始化
1. Init方法用于从应用程序升级器配置文件传递<validator>元素给KeyValidator类。该元素应该包含<key>子元素,该元素中以base64字符串存储密钥。<key>元素用于初始化验证密钥,该初始化过程代码在下面的代码片断中。
[VB.NET]
Sub Init()
Sub Init (ByVal config As XmlNode) Implements IValidator.Init
Dim keyNode As XmlNode = config.SelectSingleNode( "key" )
_key = ExtractKeyFromNode (keyNode)
If _key Is Nothing Then
'Throw a cryptographic exception
End If
End Sub
[C#]
void
IValidator.Init( XmlNode config )
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
XmlNode keyNode = config.SelectSingleNode( "key" );
_key = ExtractKeyFromNode( keyNode );
if ( null == _key )
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// Throw a cryptographic exception
}
}
2. Init方法使用一个ExtractKeyFromNode私有函数解析位于配置文件中的base64加密的字符串,并且返回一个byte数组。下面的代码展示了ExtractKeyFromNode函数。
[VB.NET]
Private
Function ExtractKeyFromNode()
Function ExtractKeyFromNode(ByVal keyNode As XmlNode) As Byte()
Dim key As Byte() = Nothing
If keyNode Is Nothing Then
Return Nothing
End If
Try
key = Convert.FromBase64String(keyNode.InnerText)
Catch e As Exception
'exception handling code
End Try
Return key
End Function
[C#]
private
byte
[] ExtractKeyFromNode( XmlNode keyNode )
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
byte[] key = null;
if ( null == keyNode )
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return null;
}
try
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
key = Convert.FromBase64String( keyNode.InnerText );
}
catch( Exception e )
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// exception handling code
}
return key;
}
签名
重载的Sign方法使用KeyedHashAlgorithm类生成hashed签名,下面的代码片断展示了这一过程:
[VB.NET]
Overloads
Function Sign()
Function Sign(ByVal filePath As String, ByVal key As String) As String _
Implements IValidator.Sign
Dim outSignature As Byte() = Nothing
Dim fs As FileStream = Nothing
Try
fs = New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)
Dim kha As KeyedHashAlgorithm = KeyedHashAlgorithm.Create()
kha.Key = Convert.FromBase64String(key)
outSignature = kha.ComputeHash(fs)
Catch e As Exception
' exception handling code
Finally
If Not (fs Is Nothing) Then
fs.Close()
End If
End Try
Return Convert.ToBase64String(outSignature)
End Function
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
Overloads
Function Sign()
Function Sign(ByVal xml As XmlNode, ByVal key As String) As String _
Implements IValidator.Sign
Dim outSignature As Byte() = Nothing
Dim xmlNodeByte As Byte() = Nothing
xmlNodeByte = Encoding.Unicode.GetBytes(Xml.InnerXml)
Try
Dim kha As KeyedHashAlgorithm = KeyedHashAlgorithm.Create()
kha.Key = Convert.FromBase64String(key)
outSignature = kha.ComputeHash(xmlNodeByte)
Catch e As Exception
'exception handling code
End Try
Return Convert.ToBase64String(outSignature)
End Function
[C#]
string
IValidator.Sign(
string
filePath,
string
key )
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
byte[] outSignature = null;
FileStream fs = null;
try
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create();
kha.Key = Convert.FromBase64String( key );
outSignature = kha.ComputeHash( fs );
}
catch( Exception e )
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// exception handling code
}
finally
if ( null != fs )
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
fs.Close();
}
}
return
Convert.ToBase64String( outSignature );
}
string
IValidator.Sign( XmlNode xml,
string
key )
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
byte[] outSignature = null;
byte[] xmlNodeByte = null;
xmlNodeByte = Encoding.Unicode.GetBytes( xml.InnerXml );
try
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create();
kha.Key = Convert.FromBase64String( key );
outSignature = kha.ComputeHash( xmlNodeByte );
}
catch( Exception e )
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// exception handling code
}
return Convert.ToBase64String( outSignature );
}
验证
Validate方法使用相同的KeyedHashAlgorithm验证签名和密钥,该密钥用于生成签名。Validate方法在下面的代码片断中实现:
[VB.NET]
Overloads
Function Validate()
Function Validate(ByVal filePath As String, ByVal signature As String) _
As Boolean Implements IValidator.Validate
Dim inSignature As Byte() = Nothing
Dim outSignature As Byte() = Nothing
inSignature = Convert.FromBase64String(signature)
Dim fs As New FileStream(filePath, FileMode.Open)
Try
Dim kha As KeyedHashAlgorithm = KeyedHashAlgorithm.Create()
kha.Key = _key
outSignature = kha.ComputeHash(fs)
Finally
fs.Close()
End Try
Return compareKeys(outSignature, inSignature)
End Function
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
Overloads
Function Validate()
Function Validate(ByVal xml As XmlNode, ByVal signature As String) _
As Boolean Implements IValidator.Validate
Dim inSignature As Byte() = Nothing
Dim outSignature As Byte() = Nothing
Dim xmlNodeByte As Byte() = Nothing
xmlNodeByte = Encoding.Unicode.GetBytes(Xml.InnerXml)
inSignature = Convert.FromBase64String(signature)
Dim kha As KeyedHashAlgorithm = KeyedHashAlgorithm.Create()
kha.Key = _key
outSignature = kha.ComputeHash(xmlNodeByte)
Return compareKeys(outSignature, inSignature)
End Function
[C#]
bool
IValidator.Validate(
string
filePath,
string
signature )
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
byte[] inSignature = null;
byte[] outSignature = null;
inSignature = Convert.FromBase64String( signature );
using ( FileStream fs = new FileStream( filePath, FileMode.Open ) )
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create();
kha.Key = _key;
outSignature = kha.ComputeHash( fs );
}
return compareKeys( outSignature, inSignature );
}
bool
IValidator.Validate( XmlNode xml,
string
signature )
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
byte[] inSignature = null;
byte[] outSignature = null;
byte[] xmlNodeByte = null;
xmlNodeByte = Encoding.Unicode.GetBytes( xml.InnerXml );
inSignature = Convert.FromBase64String( signature );
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create();
kha.Key = _key;
outSignature = kha.ComputeHash( xmlNodeByte );
return compareKeys( outSignature, inSignature );
}
这两个Validate的重载方法使用一个私有的compareKeys来确认签名匹配。compareKeys函数在下面的代码中实现:
[VB.NET]
Private
Function compareKeys()
Function compareKeys(ByVal firstKey() As Byte, ByVal secondKey() As Byte) _
As Boolean
If firstKey.Length <> secondKey.Length Then
Return False
End If
Dim i As Integer
For i = 0 To firstKey.Length - 1
If firstKey(i) <> secondKey(i) Then
Return False
End If
Next i
Return True
End Function
[C#]
private
bool
compareKeys(
byte
[] firstKey,
byte
[] secondKey )
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
if( firstKey.Length != secondKey.Length ) return false;
for( int i = 0 ; i < firstKey.Length; i++ )
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if( firstKey[ i ] != secondKey[ i ] ) return false;
}
return true;
}
KeyValidator配置
为了使用KeyValidator,你必须在应用程序升级器配置文件中包含一个<validator>元素,指定KeyValidator集合和类型的全名,以及你要使用的base64加密密钥。一个<validator>配置元素的例子如下所示:
<
validator
type
="Microsoft.ApplicationBlocks.ApplicationUpdater.Validators.KeyValidator"
assembly
="Microsoft.ApplicationBlocks.ApplicationUpdater,Version=1.0.0.0, Culture=neutral,PublicKeyToken=null"
>
<
key
>
ACQAAASAAACUAAAABgIAAAAkAABS[etc.]
</
key
>
</
validator
>