Implementatie van de IURIDereferencer-interface

Adobe AIR 1.5 of hoger

Wanneer u een XML-handtekening wilt valideren, moet u een implementatie van de IURIDereferencer-interface verstrekken. Deze implementatie moet ervoor zorgen dat de URI's binnen de Reference-elementen van een XML-handtekeningdocument worden opgelost en dat de gegevens worden geretourneerd zodat de samenvatting kan worden berekend. De berekende samenvatting wordt vergeleken met de samenvatting in de ondertekening om te bepalen of de gegevens waarnaar wordt verwezen, zijn gewijzigd sinds de handtekening werd gemaakt.

Opmerking: HTML-gebaseerde AIR-toepassingen moeten een SWF-bibliotheek importeren die een ActionScript-implementatie bevat ten einde XML-handtekeningen te kunnen valideren. De IURIDereferencer-interface kan niet worden geïmplementeerd in JavaScript.

De IURIDerefencer-interface heeft één methode, dereference(uri:String) , die moet worden geïmplementeerd. Het XMLSignatureValidator-object roept deze methode aan voor iedere referentie in de handtekening. De methode moet de gegevens retourneren in een ByteArray-object.

In de meeste gevallen zult u ook eigenschappen of methoden moeten toevoegen waarmee uw dereferencer-object de gegevens waarnaar wordt verwezen, kan vinden. Als de ondertekende gegevens zich bijvoorbeeld bevinden in hetzelfde document als de ondertekening, kunt u een lidvariabele toevoegen die een referentie naar het XML-document biedt. De methode dereference() kan deze variabele dan in combinatie met de URI gebruiken om de gegevens waarnaar wordt verwezen, te zoeken. Als de ondertekende gegevens zich bevinden in een map op het lokale bestandssysteem, kan de methode dereference() een eigenschap nodig hebben die het pad opgeeft naar de desbetreffende map ten einde de bestanden waarnaar wordt verwezen, op te lossen.

Voor het interpreteren van de URI-tekenreeksen is de XMLSignatureValidator geheel afhankelijk van de dereferencer. De standaardregels voor het oplossen van URI's vindt u in sectie 4.3.3 van de W3C Recommendation for XML Signature Syntax and Processing.

URI's in enveloped handtekeningen oplossen

Wanneer een enveloped XML-handtekening wordt gegenereerd, worden de Signature-elementen ingevoegd in de ondertekende gegevens. Als u bijvoorbeeld het volgende bericht ondertekent met behulp van een enveloped handtekening:

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

Ziet het ontstane ondertekende document er zo uit:

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

U ziet dat de handtekening één enkel Reference-element bevat met een lege tekenreeks als URI. In deze context verwijst een lege tekenreeks naar de hoofdmap van het document.

U ziet ook dat het transformatiealgoritme specificeert dat een enveloped handtekeningtransformatie wordt toegepast. Wanneer een enveloped handtekeningtransformatie is toegepast, verwijdert de XMLSignatureValidator automatisch de handtekening uit het document voordat de samenvatting wordt berekend. Dit betekent dat de dereferencer het Signature-element niet hoeft te verwijderen wanneer de gegevens worden geretourneerd.

In het volgende voorbeeld wordt een dereferencer voor enveloped handtekeningen geïllustreerd:

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

Deze dereferencer-klasse maakt gebruik van een constructorfunctie met een parameter signedMessage , waarmee het enveloped handtekeningdocument beschikbaar wordt gesteld voor de methode dereference() . Aangezien de referentie in een enveloped handtekening altijd verwijst naar de hoofdmap van de ondertekende gegevens, schrijft de methode dereferencer() het document naar een bytearray, waarna het wordt geretourneerd.

Oplossen van URI's in enveloping en detached handtekeningen

Wanneer de ondertekende gegevens zich bevinden in hetzelfde document als de handtekening, gebruiken de URI's in de referenties meestal de XPath- of XPointer-syntaxis om te verwijzen naar de elementen die worden ondertekend. In de W3C-aanbevelingen voor de syntaxis en verwerking van XML-handtekeningen wordt alleen deze syntaxis aanbevolen. Het is dus aan te raden uw implementatie te baseren op de handtekeningen die u naar verwachting aan zult treffen. Zorg ervoor dat u voldoende foutafhandelingsprocedures inbouwt voor de verwerking van syntaxis die niet wordt ondersteund.

De handtekening van een AIR-toepassing is een voorbeeld van een enveloping handtekening. De bestanden in de toepassing worden aangegeven in een Manifest-element. Naar het Manifest-element wordt verwezen in het attribuut Reference URI door middel van de tekenreeks “#PackageContents”, die verwijst naar de ID van het Manifest-element:

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

Een dereferencer voor het valideren van deze handtekening moet de URI-tekenreeks die "#PackageContents" uit het Reference-element bevat, accepteren en het Manifest-element in een ByteArray-object retourneren. Het symbool “#” verwijst naar de waarde van een element-ID-attribuut.

In het volgende voorbeeld wordt een dereferencer voor het valideren van handtekeningen voor AIR-toepassingen geïmplementeerd. De implementatie wordt eenvoudig gehouden doordat wordt afgegaan op de bekende structuur van een AIR-handtekening. Een dereferencer voor algemeen gebruik kan aanzienlijk complexer zijn.

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

Wanneer u dit type handtekening verifieert, worden alleen de gegevens in het Manifest-element gevalideerd. De feitelijke bestanden in het pakket worden in het geheel niet gecontroleerd. Als u wilt controleren of er met de bestanden in het pakket is geknoeid, moet u de bestanden lezen, de SHA256-samenvatting berekenen en het resultaat vergelijken met de samenvatting die is geregistreerd in het manifest. De XMLSignatureValidator controleert dergelijke secundaire referenties niet automatisch.

Opmerking: Dit voorbeeld wordt alleen gegeven om het validatieproces van handtekeningen te illustreren. Het heeft weinig zin om een AIR-toepassing zijn eigen handtekening te laten valideren. Als er al met de toepassing is geknoeid, kan degene die dit doet de validatiecontrole gewoon verwijderen.

Samenvattingswaarden voor externe bronnen berekenen

AIR is niet voorzien van ingebouwde functies voor het berekenen van SHA256-samenvattingen, maar de Flex SDK is voorzien van een hulpprogrammaklasse SHA256. De SDK omvat ook de Base64 encoder-hulpprogrammaklasse waarmee de berekende samenvatting kan worden vergeleken met de samenvatting die is opgeslagen in de handtekening.

De volgende voorbeeldfunctie leest en valideert de bestanden in een AIR-pakketmanifest:

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

De functie doorloopt alle referenties in het Manifest-element. Voor iedere referentie wordt de SHA256-samenvatting berekend, gecodeerd in base64-indeling en vergeleken met de samenvatting in het manifest. De URI's in een AIR-pakket verwijzen naar paden die relatief zijn ten opzichte van de toepassingsmap. De paden worden opgelost op basis van de locatie van het handtekeningbestand, dat zich altijd bevindt in de submap META-INF in de toepassingsmap. Houd er rekening mee dat de klasse Flex SHA256 de samenvatting retourneert in de vorm van een reeks hexadecimale tekens. Deze tekenreeks moet worden geconverteerd naar een ByteArray die de bytes bevat die worden vertegenwoordigd door de hexadecimale tekenreeks.

Als u de klassen mx.utils.SHA256 en Base64Encoder in Flash CS4 wilt gebruiken, kunt u deze klassen opzoeken en kopiëren naar de ontwikkelmap van uw toepassing, of kunt u met behulp van de Flex SDK een bibliotheek-SWF compileren die deze klassen bevat.

Oplossen van URI's in detached handtekeningen die verwijzen naar externe gegevens

Wanneer een URI verwijst naar een externe bron, moeten de gegevens worden geopend en geladen in een ByteArray-object. Als de URI een absolute URL bevat, hoeft er alleen maar een bestand te worden gelezen of een URL te worden opgevraagd. Wanneer de URI een relatief pad bevat, hetgeen meestal het geval zal zijn, moet uw IURIDereferencer-implementatie een manier bevatten om de paden naar de ondertekende bestanden op te lossen.

In het volgende voorbeeld wordt een File-object gebruikt dat wordt geïnitialiseerd wanneer de dereferencer-instantie wordt geconstrueerd als basis voor het oplossen van ondertekende bestanden.

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

De functie dereference() zoekt eigenlijk alleen het bestand dat is geadresseerd door de referentie-URI, laadt de inhoud van het bestand in een byte-array, en retourneert het ByteArry-object.

Opmerking: Voordat u de externe referenties gaat valideren, moet u bedenken of uw toepassing mogelijk ontvankelijk is voor een 'phone home' of soortgelijk type aanval vanuit een met verkeerde bedoelingen opgesteld handtekeningdocument.