當簽署的資料與簽名本身位於同一份文件中,參照中的 URI 通常會使用 XPath 或 XPointer 語法來敘述已簽署的元素。W3C 建議事項的「XML 簽章語法及處理」(XML-Signature Syntax and Processing) 只建議使用此語法,所以您應該根據將處理的簽名來進行實作 (並且增加充分的錯誤檢查作業,以從容地處理未受支援的語法)。
AIR 應用程式的簽名就是封外簽名的一個例子。應用程式中的檔案列於 Manifest 元素內。Manifest 元素是在 Reference URI 特質中以「#PackageContents」字串描述,而此字串參照 Manifest 元素的 ID:
<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 字串取出,然後以 ByteArray 物件傳回 Manifest 元素。「#」符號是指元素的 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 類別會將摘要以十六進位字元的字串傳回。這個字串必須轉換為 ByteArray,其中含有以十六進位字串所表示的位元組。
若要在 Flash CS4 中使用 mx.utils.SHA256 和 Base64Encoder 類別,您可以找出這些類別並將它們複製到應用程式開發目錄中,也可以編譯一個文件庫 SWF,其中含有使用 Flex SDK 的類別。