IURIDereferencer 인터페이스 구현

Adobe AIR 1.5 이상

XML 서명의 유효성을 검사하려면 IURIDereferencer 인터페이스의 구현을 제공해야 합니다. 구현은 XML 서명 문서의 Reference 요소 내에 있는 URI를 확인하며 다이제스트를 계산할 수 있도록 데이터를 반환합니다. 계산된 다이제스트는 서명을 만든 이후 참조된 데이터를 변경했는지 여부를 확인하기 위해 서명의 다이제스트와 비교합니다.

참고: HTML 기반 AIR 응용 프로그램은 XML 서명의 유효성을 검사하기 위해 ActionScript 구현을 포함하는 SWF 라이브러리를 가져와야 합니다. IURIDereferencer 인터페이스는 JavaScript에서 구현할 수 없습니다.

IURIDerefencer 인터페이스는 구현해야 할 단일 메서드인 dereference(uri:String) 를 포함합니다. XMLSignatureValidator 객체는 서명의 각 참조에 대해 이 메서드를 호출합니다. 메서드는 ByteArray 객체의 데이터를 반환해야 합니다.

대부분의 경우 역참조자 객체가 참조된 데이터를 찾을 수 있도록 속성 또는 메서드도 추가해야 합니다. 예를 들어 서명된 데이터가 서명과 동일한 문서에 있는 경우 XML 문서에 대한 참조를 제공하는 멤버 변수를 추가할 수 있습니다. 그렇게 하면 dereference() 메서드가 이 변수를 URI와 함께 사용하여 참조된 데이터를 찾을 수 있습니다. 마찬가지로 서명된 데이터가 로컬 파일 시스템의 디렉토리에 있는 경우 dereference() 메서드에는 참조된 파일을 확인하기 위해 해당 디렉토리에 대한 경로를 제공하는 속성이 필요할 수 있습니다.

XMLSignatureValidator는 URI 문자열을 해석할 때 전적으로 역참조자를 이용합니다. URI 역참조에 대한 일반 규칙은 XML 서명 구문 및 처리에 대한 W3C 권장 사항의 4.3.3항에 나와 있습니다.

엔벌로프된 서명에서 URI 역참조

엔벌로프된 XML 서명을 생성하면 서명 요소가 서명된 데이터에 삽입됩니다. 예를 들어 엔벌로프된 서명 구조를 사용하여 다음 메시지에 서명한 경우

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

서명된 문서 결과는 다음과 같습니다.

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

서명에 해당 URI로 빈 문자열이 있는 단일 Reference 요소가 포함되어 있습니다. 이 컨텍스트에서 빈 문자열은 문서의 루트를 나타냅니다.

또한 변형 알고리즘은 엔벌로프된 서명 변형이 적용되었음을 지정합니다. 엔벌로프된 서명 변형이 적용된 경우 XMLSignatureValidator는 다이제스트를 계산하기 전에 자동으로 문서에서 서명을 제거합니다. 즉 역참조자는 데이터를 반환할 때 Signature 요소를 제거할 필요가 없습니다.

다음 예제에서는 엔벌로프된 서명에 대한 역참조자를 보여 줍니다.

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

이 역참조자 클래스는 signedMessage 매개 변수와 함께 생성자 함수를 사용하여 dereference() 메서드에서 사용할 수 있는 엔벌로프된 서명 문서를 만들 수 있습니다. 엔벌로프된 서명의 참조는 항상 서명된 데이터의 루트를 참조하므로 dereferencer() 메서드는 문서를 바이트 배열에 작성하고 반환합니다.

엔벌로프 중인 서명 및 분리된 서명에서 URI 역참조

서명된 데이터가 서명 자체와 동일한 문서에 있는 경우 참조의 URI는 일반적으로 XPath 또는 XPointer 구문을 사용하여 서명된 요소를 처리합니다. XML 서명 구문 및 처리를 위한 W3C 권장 사항에서는 이 구문만 권장하므로 발견할 수 있는 서명을 기반으로 구현하고 지원되지 않는 구문을 적절히 처리할 수 있는 오류 확인을 추가해야 합니다.

AIR 응용 프로그램의 서명은 엔벌로프 중인 서명의 예입니다. 응용 프로그램의 파일은 Manifest 요소에 나열됩니다. Manifest 요소는 Manifest 요소의 ID를 나타내는 “#PackageContents” 문자열을 사용하여 Reference URI 특성에서 처리합니다.

<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는 이러한 2차 참조를 자동으로 확인하지 않습니다.

참고: 이 예제는 서명 유효성 검사 프로세스를 보여 주기 위해서만 제공됩니다. 자체 서명의 유효성을 검사하기 위해 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 클래스를 사용하려면 이러한 클래스를 찾아서 응용 프로그램 개발 디렉토리에 복사하거나 Flex SDK를 사용하여 이 클래스를 포함하는 라이브러리 SWF를 컴파일합니다.

외부 데이터를 참조하는 분리된 서명에서 URI 역참조

URI가 외부 리소스를 나타내는 경우 데이터에 액세스하여 ByteArray 객체로 로드해야 합니다. URI에 절대 URL이 포함되어 있는 경우 단순히 파일을 읽거나 URL을 요청하면 됩니다. 보다 일반적으로 URI에 절대 경로가 포함되어 있는 경우 IURIDereferencer 구현에는 서명된 파일에 대한 경로를 확인하는 방법이 포함되어 있어야 합니다.

다음 예제에서는 역참조자 인스턴스가 서명된 파일 확인을 위한 기반으로 생성된 경우 초기화된 File 객체를 사용합니다.

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

dereference() 함수는 간단하게 참조 URI에서 처리하는 파일을 찾고, 파일 내용을 바이트 배열로 로드하고, ByteArray 객체를 반환합니다.

참고: 원격 외부 참조의 유효성을 검사하기 전에 응용 프로그램이 “phone home” 또는 악의적으로 생성된 서명 문서에 의한 비슷한 유형의 공격에 취약하지 않은지 확인하십시오.