Clase System.Security.Cryptography.Xml.SignedXml
En este artículo se proporcionan comentarios adicionales a la documentación de referencia de esta API.
La SignedXml clase es la implementación de .NET de la sintaxis y la especificación de procesamiento de firmas XML de World Wide Web Consortium (W3C), también conocida como XMLDSIG (firma digital XML). XMLDSIG es una manera interoperable y basada en estándares para firmar y comprobar todo o parte de un documento XML u otros datos direccionables desde un identificador uniforme de recursos (URI).
Use la SignedXml clase siempre que necesite compartir datos XML firmados entre aplicaciones u organizaciones de forma estándar. Cualquier dato firmado con esta clase se puede comprobar mediante cualquier implementación conforme de la especificación W3C para XMLDSIG.
La SignedXml clase permite crear los tres tipos siguientes de firmas digitales XML:
Tipo de firma | Descripción |
---|---|
Firma sobres | La firma está contenida en el elemento XML que se está firmando. |
Firma envolvente | El XML firmado está incluido en el <Signature> elemento . |
Firma desasociada interna | La firma y el XML firmado están en el mismo documento, pero ninguno de los elementos contiene el otro. |
También hay un cuarto tipo de firma denominada firma desasociada externa que es cuando los datos y la firma están en documentos XML independientes. La SignedXml clase no admite firmas desasociadas externas.
Estructura de una firma XML
XMLDSIG crea un <Signature>
elemento, que contiene una firma digital de un documento XML u otros datos direccionables desde un URI. Opcionalmente, el <Signature>
elemento puede contener información sobre dónde encontrar una clave que comprobará la firma y qué algoritmo criptográfico se usó para firmar. La estructura básica es la siguiente:
<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>
Las partes principales de esta estructura son:
El elemento
<CanonicalizationMethod>
Especifica las reglas para volver a escribir el
Signature
elemento de XML/text en bytes para la validación de firmas. El valor predeterminado de .NET es http://www.w3.org/TR/2001/REC-xml-c14n-20010315, que identifica un algoritmo de confianza. Este elemento se representa mediante la SignedInfo.CanonicalizationMethod propiedad .El elemento
<SignatureMethod>
Especifica el algoritmo usado para la generación y validación de firmas, que se aplicó al
<Signature>
elemento para generar el valor en<SignatureValue>
. En el ejemplo anterior, el valor http://www.w3.org/2000/09/xmldsig#rsa-sha1 identifica una firma RSA PKCS1 SHA-1. Debido a problemas de colisión con SHA-1, Microsoft recomienda un modelo de seguridad basado en SHA-256 o superior. Este elemento se representa mediante la SignatureMethod propiedad .El elemento
<SignatureValue>
Especifica la firma criptográfica para el
<Signature>
elemento . Si esta firma no comprueba, se ha alterado alguna parte del<Signature>
bloque y el documento se considera no válido. Siempre que el<CanonicalizationMethod>
valor sea de confianza, este valor es altamente resistente a la manipulación. Este elemento se representa mediante la SignatureValue propiedad .Atributo
URI
del<Reference>
elementoEspecifica un objeto de datos mediante una referencia de URI. Este atributo se representa mediante la Reference.Uri propiedad .
No especificar el
URI
atributo , es decir, establecer la Reference.Uri propiedadnull
en , significa que se espera que la aplicación receptora conozca la identidad del objeto. En la mayoría de los casos, unnull
URI producirá una excepción. No use unnull
URI, a menos que la aplicación interopera con un protocolo que lo requiera.Si se establece el
URI
atributo en una cadena vacía, se indica que el elemento raíz del documento se está firmando, una forma de firma sobre.Si el valor del
URI
atributo comienza con #, el valor debe resolverse en un elemento del documento actual. Este formulario se puede usar con cualquiera de los tipos de firma admitidos (firma envolvente, firma envolvente o firma desasociada interna).Cualquier otra cosa se considera una firma desasociada de recursos externos y no es compatible con la SignedXml clase .
El elemento
<Transforms>
Contiene una lista ordenada de
<Transform>
elementos que describen cómo el firmante obtuvo el objeto de datos que se ha implícitado. Un algoritmo de transformación es similar al método de canónico, pero en lugar de volver a escribir el<Signature>
elemento, vuelve a escribir el contenido identificado por elURI
atributo del<Reference>
elemento. El<Transforms>
elemento se representa mediante la TransformChain clase .Cada algoritmo de transformación se define como xml (conjunto de nodos XPath) o bytes como entrada. Si el formato de los datos actuales difiere de los requisitos de entrada de transformación, se aplican reglas de conversión.
Cada algoritmo de transformación se define como producir XML o bytes como salida.
Si la salida del último algoritmo de transformación no está definida en bytes (o no se especificaron transformaciones), el método de canónica se usa como una transformación implícita (incluso si se especificó un algoritmo diferente en el
<CanonicalizationMethod>
elemento).Un valor de http://www.w3.org/2000/09/xmldsig#enveloped-signature para el algoritmo de transformación codifica una regla que se interpreta como quitar el
<Signature>
elemento del documento. De lo contrario, un comprobador de una firma sobre resumen el documento, incluida la firma, pero el firmante habría digerido el documento antes de aplicar la firma, lo que conduce a respuestas diferentes.
El elemento
<DigestMethod>
Identifica el método digest (hash criptográfico) que se va a aplicar en el contenido transformado identificado por el
URI
atributo del<Reference>
elemento. Esto se representa mediante la Reference.DigestMethod propiedad .
Elección de un método de canónica
A menos que interoperar con una especificación que requiera el uso de un valor diferente, se recomienda usar el método de canónica .NET predeterminado, que es el algoritmo XML-C14N 1.0, cuyo valor es http://www.w3.org/TR/2001/REC-xml-c14n-20010315. El algoritmo XML-C14N 1.0 debe ser compatible con todas las implementaciones de XMLDSIG, especialmente cuando se trata de una transformación final implícita que se va a aplicar.
Hay versiones de algoritmos de canónica que admiten la conservación de comentarios. No se recomienda conservar los métodos de canónica conservando comentarios porque infringen el principio de "firmar lo que se ve". Es decir, los comentarios de un <Signature>
elemento no modificarán la lógica de procesamiento de cómo se realiza la firma, simplemente lo que es el valor de la firma. Cuando se combina con un algoritmo de firma débil, lo que permite que los comentarios se incluyan proporcionan a un atacante libertad innecesaria para forzar una colisión de hash, lo que hace que un documento manipulado parezca legítimo. En .NET Framework, solo se admiten los canónicos integrados de forma predeterminada. Para admitir canónicos adicionales o personalizados, consulte la SafeCanonicalizationMethods propiedad . Si el documento usa un método de canónico que no está en la colección representada por la SafeCanonicalizationMethods propiedad , el CheckSignature método devolverá false
.
Nota:
Una aplicación extremadamente defensiva puede quitar los valores que no espera que los firmantes usen de la SafeCanonicalizationMethods colección.
¿Los valores de referencia están a salvo de la manipulación?
Sí, los <Reference>
valores son seguros de alterarlos. .NET comprueba el <SignatureValue>
cálculo antes de procesar cualquiera de los <Reference>
valores y sus transformaciones asociadas, y anulará pronto para evitar instrucciones de procesamiento potencialmente malintencionadas.
Elegir los elementos que se van a firmar
Se recomienda usar el valor de "" para el URI
atributo (o establecer la Uri propiedad en una cadena vacía), si es posible. Esto significa que todo el documento se considera para el cálculo de resumen, lo que significa que todo el documento está protegido contra alteraciones.
Es muy común ver URI
valores en forma de anclajes como #foo, haciendo referencia a un elemento cuyo atributo id. es "foo". Desafortunadamente, es fácil que esto se manipule porque esto incluye solo el contenido del elemento de destino, no el contexto. El uso de esta distinción se conoce como ajuste de firma XML (XSW).
Si la aplicación considera que los comentarios son semánticos (que no son comunes al tratar con XML), debe usar "#xpointer(/)" en lugar de "" y "#xpointer(id('foo'))" en lugar de "#foo". Las versiones de #xpointer se interpretan como comentarios, mientras que los formularios shortname excluyen los comentarios.
Si necesita aceptar documentos que solo están parcialmente protegidos y desea asegurarse de que está leyendo el mismo contenido protegido por la firma, use el GetIdElement método .
Consideraciones de seguridad sobre el elemento KeyInfo
Los datos del elemento opcional <KeyInfo>
(es decir, la KeyInfo propiedad ), que contiene una clave para validar la firma, no deben ser de confianza.
En concreto, cuando el KeyInfo valor representa una clave pública RSA, DSA o ECDSA, el documento podría haberse alterado, a pesar del CheckSignature método que informa de que la firma es válida. Esto puede ocurrir porque la entidad que realiza la manipulación solo tiene que generar una nueva clave y volver a firmar el documento alterado con esa nueva clave. Por lo tanto, a menos que la aplicación compruebe que la clave pública es un valor esperado, el documento debe tratarse como si se alterara. Esto requiere que la aplicación examine la clave pública insertada en el documento y compruébala en una lista de valores conocidos para el contexto del documento. Por ejemplo, si un usuario conocido podría entender que el documento podría ser emitido por un usuario conocido, comprobaría la clave en una lista de claves conocidas usadas por ese usuario.
También puede comprobar la clave después de procesar el documento mediante el CheckSignatureReturningKey método , en lugar de usar el CheckSignature método . Sin embargo, para obtener la seguridad óptima, debe comprobar la clave de antemano.
Como alternativa, considere la posibilidad de probar las claves públicas registradas del usuario, en lugar de leer lo que hay en el <KeyInfo>
elemento .
Consideraciones de seguridad sobre el elemento X509Data
El elemento opcional es un elemento secundario <X509Data>
del <KeyInfo>
elemento y contiene uno o varios certificados o identificadores X509 para los certificados X509. Los datos del <X509Data>
elemento tampoco deben ser de confianza inherente.
Al comprobar un documento con el elemento incrustado <X509Data>
, .NET comprueba solo que los datos se resuelven en un certificado X509 cuya clave pública se puede usar correctamente para validar la firma del documento. A diferencia de llamar al CheckSignature método con el verifySignatureOnly
parámetro establecido false
en , no se realiza ninguna comprobación de revocación, no se comprueba ninguna confianza en cadena y no se comprueba ninguna expiración. Incluso si la aplicación extrae el propio certificado y lo pasa al método con el CheckSignatureverifySignatureOnly
parámetro establecido false
en , que todavía no es suficiente validación para evitar la manipulación de documentos. El certificado todavía debe comprobarse según sea adecuado para el documento que se está firmando.
El uso de un certificado de firma incrustado puede proporcionar estrategias de rotación de claves útiles, ya sea en la <X509Data>
sección o en el contenido del documento. Al usar este enfoque, una aplicación debe extraer el certificado manualmente y realizar la validación similar a:
El certificado se emitió directamente o a través de una cadena por una entidad de certificación (CA) cuyo certificado público está incrustado en la aplicación.
El uso de la lista de confianza proporcionada por el sistema operativo sin comprobaciones adicionales, como un nombre de sujeto conocido, no es suficiente para evitar alteraciones en SignedXml.
Se comprueba que el certificado no ha expirado en el momento de la firma de documentos (o "ahora" para el procesamiento de documentos casi en tiempo real).
Para los certificados de larga duración emitidos por una ENTIDAD de certificación que admite la revocación, compruebe que el certificado no se revoca.
El firmante del certificado se comprueba según sea adecuado para el documento actual.
Elección del algoritmo de transformación
Si interopera con una especificación que ha dictado valores específicos (como XrML), debe seguir la especificación. Si tiene una firma sobre (por ejemplo, al firmar todo el documento), debe usar http://www.w3.org/2000/09/xmldsig#enveloped-signature (representada por la XmlDsigEnvelopedSignatureTransform clase ). También puede especificar la transformación XML-C14N implícita, pero no es necesario. Para una firma envolvente o desasociada, no se requieren transformaciones. La transformación XML-C14N implícita se encarga de todo.
Con la seguridad actualizada introducida por el Boletín de seguridad de Microsoft MS16-035, .NET ha restringido las transformaciones que se pueden usar en la comprobación de documentos de forma predeterminada, con transformaciones que no son de confianza que hacen CheckSignature que siempre devuelva false
. En concreto, las transformaciones que requieren una entrada adicional (especificada como elementos secundarios en el XML) ya no se permiten debido a su susceptibilidad del abuso por parte de los usuarios malintencionados. W3C aconseja evitar las transformaciones XPath y XSLT, que son las dos transformaciones principales afectadas por estas restricciones.
El problema con las referencias externas
Si una aplicación no comprueba que las referencias externas parezcan adecuadas para el contexto actual, se pueden abusar de maneras que proporcionan muchas vulnerabilidades de seguridad (como denegación de servicio, denegación de servicio distribuida Reflexiones ion denegación de servicio, divulgación de información, omisión de firmas y ejecución remota del código). Incluso si una aplicación validara el URI de referencia externo, habría un problema en que el recurso se cargaba dos veces: una vez cuando la aplicación la lee y una vez cuando SignedXml la lee. Dado que no hay ninguna garantía de que los pasos de lectura y comprobación de documentos de la aplicación tengan el mismo contenido, la firma no proporciona confiabilidad.
Dados los riesgos de las referencias externas, SignedXml se producirá una excepción cuando se encuentre una referencia externa. Para obtener más información sobre este problema, consulte el artículo de KB 3148821.