署名対象のデータが署名自体と同じドキュメント内にある場合、通常、参照の URI は XPath シンタックスまたは XPointer シンタックスを使用して署名対象エレメントのアドレスを指定します。W3C 勧告「XML Signature Syntax and Processing」はこのシンタックスを推奨しているだけなので、想定される署名に基づいて実装を行う必要があります(さらに、サポートされないシンタックスを適切に処理するための十分なエラーチェックを追加する必要があります)。
AIR アプリケーションの署名は、enveloping 署名の一例です。アプリケーション内のファイルは Manifest エレメントに一覧表示されます。Manifest エレメントのアドレスは、Reference URI 属性内で、Manifest エレメントの ID を指す “#PackageContents” というストリングを使用して指定されます。
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="PackageSignature">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/TR/xmldsig-core#rsa-sha1"/>
<Reference URI="#PackageContents">
<Transforms>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>ZMGqQdaRKQc1HirIRsDpeBDlaElS+pPotdziIAyAYDk=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue Id="PackageSignatureValue">cQK...7Zg==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MII...T4e</X509Certificate>
</X509Data>
</KeyInfo>
<Object>
<Manifest Id="PackageContents">
<Reference URI="mimetype">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256">
</DigestMethod>
<DigestValue>0/oCb84THKMagtI0Dy0KogEu92TegdesqRr/clXct1c=</DigestValue>
</Reference>
<Reference URI="META-INF/AIR/application.xml">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256">
</DigestMethod>
<DigestValue>P9MqtqSdqcqnFgeoHCJysLQu4PmbUW2JdAnc1WLq8h4=</DigestValue>
</Reference>
<Reference URI="XMLSignatureValidation.swf">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256">
</DigestMethod>
<DigestValue>OliRHRAgc9qt3Dk0m0Bi53Ur5ur3fAweIFwju74rFgE=</DigestValue>
</Reference>
</Manifest>
</Object>
</Signature>
この署名を検証する間接参照は、Reference エレメントから
"#PackageContents"
を含む URI ストリングを取得し、Manifest エレメントを ByteArray オブジェクトで返す必要があります。「#」記号はエレメント ID 属性の値を示します。
次の例では、AIR アプリケーションの署名を検証する間接参照を実装します。実装は、既知の構造の AIR 署名を使用して単純化されています。汎用の間接参照はこれよりもかなり複雑になります。
package
{
import flash.events.ErrorEvent;
import flash.security.IURIDereferencer;
import flash.utils.ByteArray;
import flash.utils.IDataInput;
public class AIRSignatureDereferencer implements IURIDereferencer {
private const XML_SIG_NS:Namespace =
new Namespace( "http://www.w3.org/2000/09/xmldsig#" );
private var airSignature:XML;
public function AIRSignatureDereferencer( airSignature:XML ) {
this.airSignature = airSignature;
}
public function dereference( uri:String ):IDataInput {
var data:ByteArray = null;
try
{
if( uri != "#PackageContents" )
{
throw( new Error("Unsupported signature type.") );
}
var manifest:XMLList =
airSignature.XML_SIG_NS::Object.XML_SIG_NS::Manifest;
data = new ByteArray();
data.writeUTFBytes( manifest.toXMLString());
data.position = 0;
}
catch (e:Error)
{
data = null;
throw new Error("Reference not resolvable: " + uri + ", " + e.message);
}
finally
{
return data;
}
}
}
}
この種類の署名を検証するときは、Manifest エレメントのデータだけが検証されます。パッケージ内の実際のファイルはまったくチェックされません。パッケージのファイルが改ざんされていないかどうかをチェックするには、ファイルを読み取って SHA256 ダイジェストを計算し、その結果をマニフェストに記録されているダイジェストと比較する必要があります。XMLSignatureValidator は、このような二次参照を自動的にはチェックしません。
注意:
この例は、署名の検証プロセスを示すためだけに提供されています。それ自身の署名を検証する AIR アプリケーションはほとんど役に立ちません。アプリケーションが既に改ざんされている場合、改ざん者が検証チェックを取り除いている可能性があります。
外部リソースのダイジェスト値の計算
AIR には SHA256 ダイジェストを計算するためのビルトイン関数が含まれていませんが、Flex SDK には SHA256 ユーティリティクラスが含まれています。この SDK には、計算したダイジェストを署名に格納されているダイジェストと比較するのに役立つ Base64 エンコーダーユーティリティのクラスも含まれています。
次の関数の例では、AIR パッケージのマニフェストに含まれているファイルを読み取って検証します。
import mx.utils.Base64Encoder;
import mx.utils.SHA256;
private function verifyManifest( sigFile:File, manifest:XML ):Boolean
{
var result:Boolean = true;
var message:String = '';
var nameSpace:Namespace = manifest.namespace();
if( manifest.nameSpace::Reference.length() <= 0 )
{
result = false;
message = "Nothing to validate.";
}
for each (var reference:XML in manifest.nameSpace::Reference)
{
var file:File = sigFile.parent.parent.resolvePath( reference.@URI );
var stream:FileStream = new FileStream();
stream.open(file, FileMode.READ);
var fileData:ByteArray = new ByteArray();
stream.readBytes( fileData, 0, stream.bytesAvailable );
var digestHex:String = SHA256.computeDigest( fileData );
//Convert hexidecimal string to byte array
var digest:ByteArray = new ByteArray();
for( var c:int = 0; c < digestHex.length; c += 2 ){
var byteChar:String = digestHex.charAt(c) + digestHex.charAt(c+1);
digest.writeByte( parseInt( byteChar, 16 ));
}
digest.position = 0;
var base64Encoder:Base64Encoder = new Base64Encoder();
base64Encoder.insertNewLines = false;
base64Encoder.encodeBytes( digest, 0, digest.bytesAvailable );
var digestBase64:String = base64Encoder.toString();
if( digestBase64 == reference.nameSpace::DigestValue )
{
result = result && true;
message += " " + reference.@URI + " verified.\n";
}
else
{
result = false;
message += " ---- " + reference.@URI + " has been modified!\n";
}
base64Encoder.reset();
}
trace( message );
return result;
}
この関数は、Manifest エレメントに含まれているすべての参照を反復処理します。 各参照に対して SHA256 ダイジェストが計算され、base64 形式でエンコードされ、マニフェスト内のダイジェストと比較されます。AIR パッケージ内の URI は、アプリケーションディレクトリへの相対パスを指します。 このパスは署名ファイルの場所に基づいて解決されます。署名ファイルは常に、アプリケーションディレクトリ内の META-INF サブディレクトリにあります。Flex の SHA256 クラスは、ダイジェストを 16 進文字のストリングとして返します。このストリングを、16 進ストリングで表されたバイトを含む ByteArray に変換する必要があります。
Flash CS4 で mx.utils.SHA256 クラスと Base64Encoder クラスを使用するには、これらのクラスを探してアプリケーション開発ディレクトリにコピーするか、これらのクラスを含む SWF ライブラリを Flex SDK を使用してコンパイルします。