Implementera IURIDereferencer-gränssnittet

Adobe AIR 1.5 och senare

För att validera en XML-signatur måste du ange en implementering av IURIDereferencer-gränssnittet. Implementeringen matchar URI:erna i Reference-elementen i ett XML-signaturdokument och returnerar informationen så att sammanfattningen kan beräknas. Den beräknade sammanfattningen jämförs med sammanfattningen i signaturen för att avgöra om den refererade informationen har modifierats sedan signaturen skapades.

Obs! HTML-baserade AIR-program måste importera ett SWF-bibliotek som innehåller en ActionScript-implementering för att XML-signaturer ska kunna valideras. IURIDereferencer-gränssnittet kan inte implementeras i JavaScript.

IURIDerefencer-gränssnittet har en enda metod, dereference(uri:String) , som måste implementeras. XMLSignatureValidator-objektet anropar den här metoden för varje referens i signaturen. Metoden måste returnera informationen i ett ByteArray-objekt.

I de flesta fall måste du även lägga till egenskaper eller metoder som gör att IURIDereferencer-objektet kan hitta den refererade informationen. Om den signerade informationen exempelvis finns i samma dokument som signaturen kan du lägga till en medlemsvariabel som innehåller en referens till XML-dokumentet. Metoden dereference() kan sedan använda den här variabeln, jämte URI:n, för att hitta informationen som refereras. På samma sätt, om den signerade informationen finns i en katalog i det lokala filsystemet, kan dereference() -metoden behöva en egenskap som innehåller sökvägen till katalogen för att kunna matcha de refererade filerna.

XMLSignatureValidator förlitar sig helt och hållet på IURIDereferencer-objektet för att tolka URI-strängar. Standardreglerna för dereferencing-URI:er anges i avsnitt 4.3.3 i W3C:s rekommendation för XML-signatursyntax och -bearbetning.

IURIDereferencer-URI:er i inbäddade (enveloped) signaturer

När en inbäddad XML-signatur genereras, infogas signaturelementen i den signerade informationen. Om du exempelvis har signerat följande meddelande med en struktur för en inbäddad signatur:

<message> 
    <data>...</data> 
</message>

Ser det resulterande signerade dokumentet ut så här:

<message> 
    <data>...</data> 
     <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> 
        <SignedInfo> 
            <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> 
            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> 
            <Reference URI=""> 
                <Transforms> 
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> 
                </Transforms> 
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> 
                <DigestValue>yv6...Z0Y=</DigestValue> 
            </Reference> 
        </SignedInfo> 
        <SignatureValue>cCY...LQ==</SignatureValue> 
        <KeyInfo> 
            <X509Data> 
                <X509Certificate>MII...4e</X509Certificate> 
            </X509Data> 
        </KeyInfo> 
    </Signature> 
</message>

Observera att signaturen innehåller ett enda Reference-element med en tom sträng som dess URI. En tom sträng i den här kontexten refererar till dokumentets rot.

Observera också att transformeringsalgoritmen anger att en transformering av en inbäddad signatur har tillämpats. När den här typen av transformering har tillämpats tar XMLSignatureValidator automatiskt bort signaturen i dokumentet innan sammanfattningen beräknas. Det betyder att IURIDereferencer inte behöver ta bort Signature-elementet när informationen returneras.

Följande exempel illustrerar en IURIDereferencer för inbäddade signaturer:

package 
{ 
    import flash.events.ErrorEvent; 
      import flash.events.EventDispatcher;  
    import flash.security.IURIDereferencer; 
    import flash.utils.ByteArray; 
    import flash.utils.IDataInput; 
 
    public class EnvelopedDereferencer 
        extends EventDispatcher implements IURIDereferencer 
    { 
        private var signedMessage:XML; 
         
        public function EnvelopedDereferencer( signedMessage:XML ) 
        { 
            this.signedMessage = signedMessage; 
        } 
 
        public function dereference( uri:String ):IDataInput 
        { 
            try 
            { 
                if( uri.length != 0 ) 
                { 
                    throw( new Error("Unsupported signature type.") ); 
                } 
                var data:ByteArray = new ByteArray(); 
                data.writeUTFBytes( signedMessage.toXMLString() ); 
                data.position = 0; 
            } 
            catch (e:Error) 
                { 
                var error:ErrorEvent = 
                    new ErrorEvent("Ref error " + uri + " ", false, false, e.message); 
                this.dispatchEvent(error); 
                data = null; 
                throw new Error("Reference not resolvable: " + uri + ", " + e.message); 
            } 
            finally 
            { 
                return data; 
            } 
        }         
    } 
}

Den här IURIDereferencer-klassen använder en konstruktorfunktion med en parameter, signedMessage , för att göra dokumentet med den inbäddade signaturen tillgängligt för dereference() -metoden. Eftersom referensen i en inbäddad signatur alltid refererar till roten för den signerade informationen skriver dereferencer() -metoden dokumentet till en bytearray och returnerar ByteArray-objektet.

IURIDereferencer-URI:er i inbäddade (enveloped) och separata (detached) signaturer

Om den signerade informationen finns i samma dokument som själva signaturen använder URI:erna i referenserna vanligtvis XPath- eller XPointer-syntax för att hantera elementen som signerats. W3C:s rekommendation för XML-signatursyntax och -bearbetning rekommenderar bara den här syntaxen. Det betyder att du bör basera implementeringen på de signaturer som du förväntar dig att hitta (och lägga till nödvändiga felkontroller för att hantera syntax som inte stöds).

Signaturen för ett AIR-program är ett exempel på en inbäddad signatur. Filerna i programmet visas i ett Manifest-element. Manifest-elementet hanteras i Reference URI-attributet med strängen "#PackageContents", som refererar till Manifest-elementets 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>

Ett IURIDereferencer-objekt som används för att validera den här signaturen måste hämta URI-strängen som innehåller "#PackageContents" från Reference-elementet och returnera Manifest-elementet i ett ByteArray-objekt. Symbolen "#" refererar till attributvärdet för ett element-ID.

Följande exempel implementerar ett IURIDereferencer-objekt för validering av AIR-programsignaturer. Implementeringen i exemplet baseras på den kända strukturen för en AIR-signatur. En mer allmängiltig IURIDereferencer kan vara mycket mer komplex.

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; 
            } 
             
        } 
    } 
}

När du verifierar den här typen av signatur valideras bara informationen i Manifest-elementet. Själva filerna i paketet kontrolleras inte över huvud taget. Om du vill kontrollera om filerna i paketet har manipulerats måste du läsa filerna, beräkna SHA256-sammanfattningen och jämföra resultatet med sammanfattningen i manifestet. XMLSignatureValidator kontrollerar inte automatiskt den här typen av sekundära referenser.

Obs! Det här exemplet är endast avsett att illustrera signaturvalideringsprocessen. Ett AIR-program som validerar sin egen signatur är inte särskilt användbart. Om programmet redan har manipulerats skulle valideringskontrollen enkelt kunna tas bort.

Beräkna sammanfattningsvärden för externa resurser

AIR innehåller inga inbyggda funktioner för beräkning av SHA256-sammanfattningar, men Flex SDK inkluderar en SHA256-verktygsklass. SDK-paketet inkluderar också Base64-kodverktygsklassen som är praktisk för att jämföra den beräknade sammanfattningen med sammanfattningen i en signatur.

Följande exempelfunktion läser och validerar filerna i ett AIR-paketmanifest:

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; 
}

Funktionen loopar genom alla referenserna i Manifest-elementet. För varje referens beräknas SHA256-sammanfattningen, kodad i base64-format, och jämförs med sammanfattningen i manifestet. URI:erna i ett AIR-paket refererar till sökvägar som är relativa till programkatalogen. Sökvägarna matchas baserat på platsen för signaturfilen, som alltid finns i META-INF-underkatalogen i programkatalogen. Observera att Flex SHA256-klassen returnerar sammanfattningen som en sträng med hexadecimala tecken. Den här strängen måste konverteras till en bytearray som innehåller de byte som representeras i den hexadecimala strängen.

Om du vill använda mx.utils.SHA256- och Base64Encoder-klasserna i Flash CS4 kan du antingen leta upp och kopiera klasserna i programutvecklingskatalogen eller kompilera en biblioteks-SWF-fil som innehåller klasserna med hjälp av Flex SDK.

IURIDereferencer-URI:er i separata (detached) signaturer med referenser till externa data

Om en URI refererar till en extern resurs måste informationen hämtas och läsas in i ett ByteArray-objekt. Om URI:n innehåller en absolut URL behöver du bara läsa en fil eller begära en URL. Om, vilket förmodligen är vanligare, URI:n innehåller referenser till en relativ sökväg måste IURIDereferencer-implementeringen specificera hur sökvägarna ska matchas i de signerade filerna.

I följande exempel används ett File-objekt som initieras när IURIDereferencer-instansen konstrueras som utgångspunkt för matchningen av signerade filer.

package 
{ 
    import flash.events.ErrorEvent; 
    import flash.events.EventDispatcher; 
    import flash.filesystem.File; 
    import flash.filesystem.FileMode; 
    import flash.filesystem.FileStream; 
    import flash.security.IURIDereferencer; 
    import flash.utils.ByteArray; 
    import flash.utils.IDataInput; 
    public class RelativeFileDereferencer 
        extends EventDispatcher implements IURIDereferencer 
    { 
        private var base:File; 
         
        public function RelativeFileDereferencer( base:File ) 
        { 
            this.base = base; 
        } 
 
        public function dereference( uri:String ):IDataInput 
        { 
            var data:ByteArray = null; 
            try{ 
                var referent:File = this.base.resolvePath( uri ); 
                var refStream:FileStream = new FileStream();             
                data = new ByteArray(); 
                refStream.open( referent, FileMode.READ ); 
                 
                refStream.readBytes( data, 0, data.bytesAvailable ); 
                 
            } catch ( e:Error ) { 
                data = null; 
                throw new Error("Reference not resolvable: " + referent.nativePath + ", " + e.message ); 
            } finally { 
                return data; 
            } 
        }         
    } 
}

Funktionen dereference() letar upp filen som anges av referens-URI:n, läser in filinnehållet i en bytearray och returnerar ByteArray-objektet.

Obs! Innan du validerar externa fjärreferenser bör du överväga om ditt program är sårbart för en “phone home” eller andra typer av angrepp av ett signaturdokument som skapats i syfte att skada.