Classe System.Security.Cryptography.Xml.SignedXml
Questo articolo fornisce osservazioni supplementari alla documentazione di riferimento per questa API.
La SignedXml classe è l'implementazione .NET di World Wide Web Consortium (W3C) XML Signature Syntax and Processing Specification, nota anche come XMLDSIG (XML Digital Signature). XMLDSIG è un metodo di interoperabilità basato su standard per firmare e verificare tutto o parte di un documento XML o di altri dati indirizzabili da un URI (Uniform Resource Identifier).
Usare la SignedXml classe ogni volta che è necessario condividere dati XML firmati tra applicazioni o organizzazioni in modo standard. Tutti i dati firmati tramite questa classe possono essere verificati da qualsiasi implementazione conforme della specifica W3C per XMLDSIG.
La SignedXml classe consente di creare i tre tipi seguenti di firme digitali XML:
Tipo di firma | Descrizione |
---|---|
Firma in busta | La firma è contenuta all'interno dell'elemento XML firmato. |
Firma insodevoltura | Il codice XML firmato è contenuto all'interno dell'elemento <Signature> . |
Firma disconnessa interna | La firma e il codice XML firmato si trovano nello stesso documento, ma nessuno degli elementi contiene l'altro. |
Esiste anche un quarto tipo di firma denominato firma disconnessa esterna, ovvero quando i dati e la firma si trovano in documenti XML separati. Le firme scollegate esterne non sono supportate dalla SignedXml classe .
Struttura di una firma XML
XMLDSIG crea un <Signature>
elemento contenente una firma digitale di un documento XML o altri dati indirizzabili da un URI. L'elemento <Signature>
può facoltativamente contenere informazioni sulla posizione in cui trovare una chiave che verificherà la firma e quale algoritmo di crittografia è stato usato per la firma. La struttura di base è la seguente:
<Signature xmlns:ds="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/2000/09/xmldsig#sha1"/>
<DigestValue>Base64EncodedValue==</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>AnotherBase64EncodedValue===</SignatureValue>
</Signature>
Le parti principali di questa struttura sono:
Elemento
<CanonicalizationMethod>
Specifica le regole per la riscrittura dell'elemento
Signature
da XML/text in byte per la convalida della firma. Il valore predefinito in .NET è http://www.w3.org/TR/2001/REC-xml-c14n-20010315, che identifica un algoritmo attendibile. Questo elemento è rappresentato dalla SignedInfo.CanonicalizationMethod proprietà .Elemento
<SignatureMethod>
Specifica l'algoritmo utilizzato per la generazione e la convalida della firma, che è stato applicato all'elemento
<Signature>
per produrre il valore in<SignatureValue>
. Nell'esempio precedente il valore http://www.w3.org/2000/09/xmldsig#rsa-sha1 identifica una firma RSA PKCS1 SHA-1. A causa di problemi di collisione con SHA-1, Microsoft consiglia un modello di sicurezza basato su SHA-256 o superiore. Questo elemento è rappresentato dalla SignatureMethod proprietà .Elemento
<SignatureValue>
Specifica la firma crittografica per l'elemento
<Signature>
. Se questa firma non viene verificata, alcune parti del<Signature>
blocco sono state manomesse e il documento viene considerato non valido. Purché il<CanonicalizationMethod>
valore sia attendibile, questo valore è altamente resistente alle manomissioni. Questo elemento è rappresentato dalla SignatureValue proprietà .Attributo
URI
dell'elemento<Reference>
Specifica un oggetto dati utilizzando un riferimento URI. Questo attributo è rappresentato dalla Reference.Uri proprietà .
Non specificando l'attributo
URI
, ovvero impostando la Reference.Uri proprietà sunull
, significa che l'applicazione ricevente deve conoscere l'identità dell'oggetto. Nella maggior parte dei casi, unnull
URI genererà un'eccezione generata. Non usare unnull
URI, a meno che l'applicazione non interagisca con un protocollo che lo richiede.L'impostazione dell'attributo
URI
su una stringa vuota indica che l'elemento radice del documento viene firmato, una forma di firma in busta.Se il valore dell'attributo
URI
inizia con #, il valore deve essere risolto in un elemento del documento corrente. Questo modulo può essere utilizzato con uno qualsiasi dei tipi di firma supportati (firma in busta, firma avvolgente o firma disconnessa interna).Qualsiasi altro elemento è considerato una firma disconnessa da una risorsa esterna e non è supportato dalla SignedXml classe .
Elemento
<Transforms>
Contiene un elenco ordinato di
<Transform>
elementi che descrivono come il firmatario ha ottenuto l'oggetto dati che è stato inserito. Un algoritmo di trasformazione è simile al metodo di canonizzazione, ma invece di riscrivere l'elemento<Signature>
, riscrive il contenuto identificato dall'attributoURI
dell'elemento<Reference>
. L'elemento<Transforms>
è rappresentato dalla TransformChain classe .Ogni algoritmo di trasformazione viene definito come xml (un set di nodi XPath) o byte come input. Se il formato dei dati correnti è diverso dai requisiti di input della trasformazione, vengono applicate regole di conversione.
Ogni algoritmo di trasformazione viene definito come produzione di xml o byte come output.
Se l'output dell'ultimo algoritmo di trasformazione non è definito in byte (o non sono state specificate trasformazioni), il metodo di canonizzazione viene usato come trasformazione implicita (anche se nell'elemento
<CanonicalizationMethod>
è stato specificato un algoritmo diverso).Un valore di per l'algoritmo di http://www.w3.org/2000/09/xmldsig#enveloped-signature trasformazione codifica una regola che viene interpretata come rimuovere l'elemento
<Signature>
dal documento. In caso contrario, un verificatore di una firma in busta consentirà di digerire il documento, inclusa la firma, ma il firmatario avrebbe inserito il documento prima dell'applicazione della firma, portando a risposte diverse.
Elemento
<DigestMethod>
Identifica il metodo digest (hash crittografico) da applicare al contenuto trasformato identificato dall'attributo
URI
dell'elemento<Reference>
. Questa proprietà è rappresentata dalla Reference.DigestMethod proprietà .
Scelta di un metodo di canonizzazione
A meno che l'interoperabilità con una specifica che richiede l'uso di un valore diverso, è consigliabile usare il metodo di canonizzazione .NET predefinito, ovvero l'algoritmo XML-C14N 1.0, il cui valore è http://www.w3.org/TR/2001/REC-xml-c14n-20010315. L'algoritmo XML-C14N 1.0 deve essere supportato da tutte le implementazioni di XMLDSIG, in particolare perché è una trasformazione finale implicita da applicare.
Esistono versioni degli algoritmi di canonizzazione che supportano il mantenimento dei commenti. I metodi di canonizzazione con mantenimento dei commenti non sono consigliati perché violano il principio "firma ciò che viene visto". Ciò significa che i commenti in un <Signature>
elemento non modificheranno la logica di elaborazione per la modalità di esecuzione della firma, ma solo il valore della firma. Se combinato con un algoritmo di firma debole, consentendo l'inserimento dei commenti dà a un utente malintenzionato la libertà inutile di forzare una collisione hash, rendendo legittimo un documento manomesso. In .NET Framework solo i canonizzatori predefiniti sono supportati per impostazione predefinita. Per supportare canonizzatori aggiuntivi o personalizzati, vedere la SafeCanonicalizationMethods proprietà . Se il documento usa un metodo di canonizzazione non incluso nell'insieme rappresentato dalla SafeCanonicalizationMethods proprietà , il CheckSignature metodo restituirà false
.
Nota
Un'applicazione estremamente difensiva può rimuovere tutti i valori che non prevede l'uso SafeCanonicalizationMethods dei firmatari dalla raccolta.
I valori di riferimento sono sicuri dalla manomissione?
Sì, i <Reference>
valori sono sicuri da manomissioni. .NET verifica il <SignatureValue>
calcolo prima di elaborare uno dei valori e delle <Reference>
trasformazioni associate e interromperà in anticipo per evitare istruzioni di elaborazione potenzialmente dannose.
Scegliere gli elementi da firmare
È consigliabile usare il valore "" per l'attributo URI
(o impostare la Uri proprietà su una stringa vuota), se possibile. Ciò significa che l'intero documento viene considerato per il calcolo del digest, il che significa che l'intero documento è protetto da manomissioni.
È molto comune vedere URI
i valori sotto forma di ancoraggi, ad esempio #foo, facendo riferimento a un elemento il cui attributo ID è "foo". Purtroppo, è facile manometterlo perché include solo il contenuto dell'elemento di destinazione, non il contesto. L'abuso di questa distinzione è noto come wrapping delle firme XML (XSW).
Se l'applicazione considera i commenti come semantici (che non sono comuni quando si tratta di XML), è consigliabile usare "#xpointer(/)" anziché "" e "#xpointer(id('foo'))" anziché "#foo". Le versioni #xpointer vengono interpretate come commenti inclusi, mentre i formati nome breve vengono esclusi dai commenti.
Se è necessario accettare documenti protetti solo parzialmente e assicurarsi di leggere lo stesso contenuto protetto dalla firma, usare il GetIdElement metodo .
Considerazioni sulla sicurezza sull'elemento KeyInfo
I dati nell'elemento facoltativo <KeyInfo>
, ovvero la KeyInfo proprietà , che contiene una chiave per convalidare la firma, non devono essere considerati attendibili.
In particolare, quando il KeyInfo valore rappresenta una chiave pubblica BARA, DSA o ECDSA, il documento potrebbe essere stato manomesso, nonostante il CheckSignature metodo che segnala che la firma sia valida. Ciò può verificarsi perché l'entità che esegue la manomissione deve solo generare una nuova chiave e firmare nuovamente il documento manomesso con tale nuova chiave. Pertanto, a meno che l'applicazione non verifichi che la chiave pubblica sia un valore previsto, il documento deve essere considerato come se fosse stato manomesso. Ciò richiede che l'applicazione esamini la chiave pubblica incorporata all'interno del documento e la verifichi in base a un elenco di valori noti per il contesto del documento. Ad esempio, se il documento potrebbe essere riconosciuto come emesso da un utente noto, è necessario controllare la chiave in base a un elenco di chiavi note usate da tale utente.
È anche possibile verificare la chiave dopo l'elaborazione del documento usando il CheckSignatureReturningKey metodo anziché usare il CheckSignature metodo . Tuttavia, per garantire la sicurezza ottimale, è necessario verificare la chiave in anticipo.
In alternativa, provare a provare le chiavi pubbliche registrate dell'utente, invece di leggere ciò che si trova nell'elemento <KeyInfo>
.
Considerazioni sulla sicurezza sull'elemento X509Data
L'elemento facoltativo <X509Data>
è un elemento figlio dell'elemento <KeyInfo>
e contiene uno o più certificati o identificatori X509 per i certificati X509. Anche i dati nell'elemento <X509Data>
non devono essere intrinsecamente attendibili.
Quando si verifica un documento con l'elemento incorporato <X509Data>
, .NET verifica solo che i dati vengano risolti in un certificato X509 la cui chiave pubblica può essere usata correttamente per convalidare la firma del documento. A differenza della chiamata al CheckSignature metodo con il verifySignatureOnly
parametro impostato su false
, non viene eseguito alcun controllo di revoca, non viene verificata alcuna relazione di trust della catena e non viene verificata alcuna scadenza. Anche se l'applicazione estrae il certificato stesso e lo passa al metodo con il CheckSignatureverifySignatureOnly
parametro impostato su false
, la convalida non è ancora sufficiente per evitare manomissioni del documento. Il certificato deve comunque essere verificato come appropriato per il documento firmato.
L'uso di un certificato di firma incorporato può fornire strategie utili per la <X509Data>
rotazione delle chiavi, sia nella sezione che nel contenuto del documento. Quando si usa questo approccio, un'applicazione deve estrarre manualmente il certificato ed eseguire la convalida in modo simile a:
Il certificato è stato emesso direttamente o tramite una catena da un'autorità di certificazione (CA) il cui certificato pubblico è incorporato nell'applicazione.
L'uso dell'elenco di attendibilità fornito dal sistema operativo senza controlli aggiuntivi, ad esempio un nome soggetto noto, non è sufficiente per evitare manomissioni in SignedXml.
Il certificato viene verificato che non sia scaduto al momento della firma del documento (o "ora" per l'elaborazione di documenti quasi in tempo reale).
Per i certificati di lunga durata rilasciati da una CA che supporta la revoca, verificare che il certificato non sia stato revocato.
L'oggetto del certificato viene verificato come appropriato per il documento corrente.
Scelta dell'algoritmo di trasformazione
Se si interagisce con una specifica che ha determinato valori specifici (ad esempio XrML), è necessario seguire la specifica. Se si dispone di una firma in busta, ad esempio quando si firma l'intero documento, è necessario usare http://www.w3.org/2000/09/xmldsig#enveloped-signature (rappresentato dalla XmlDsigEnvelopedSignatureTransform classe ). È possibile specificare anche la trasformazione XML-C14N implicita, ma non è necessaria. Per una firma avvolgente o scollegata, non sono necessarie trasformazioni. La trasformazione XML-C14N implicita si occupa di tutto.
Con l'aggiornamento della sicurezza introdotto dal bollettino microsoft sulla sicurezza MS16-035, .NET ha limitato le trasformazioni che possono essere usate nella verifica dei documenti per impostazione predefinita, con trasformazioni non attendibili che causano CheckSignature sempre la restituzione false
di . In particolare, le trasformazioni che richiedono input aggiuntivo (specificato come elementi figlio nel codice XML) non sono più consentite a causa della loro suscettività dell'abuso da parte di utenti malintenzionati. W3C consiglia di evitare le trasformazioni XPath e XSLT, che sono le due trasformazioni principali interessate da queste restrizioni.
Problema con i riferimenti esterni
Se un'applicazione non verifica che i riferimenti esterni siano appropriati per il contesto corrente, possono essere abusati in modi che forniscono molte vulnerabilità di sicurezza (tra cui Denial of Service, Distributed Reflection Denial of Service, Divulgazione di informazioni, Bypass della firma ed Esecuzione di codice remoto). Anche se un'applicazione dovesse convalidare l'URI di riferimento esterno, rimane un problema della risorsa caricata due volte: una volta quando l'applicazione lo legge e una volta quando SignedXml lo legge. Poiché non esiste alcuna garanzia che i passaggi di lettura e verifica del documento dell'applicazione abbiano lo stesso contenuto, la firma non fornisce attendibilità.
Considerati i rischi dei riferimenti esterni, SignedXml genererà un'eccezione quando viene rilevato un riferimento esterno. Per altre informazioni su questo problema, vedere l'articolo della Knowledge Base 3148821.