Compartilhar via


System.Security.Cryptography.Xml.SignedXml classe

Este artigo fornece observações complementares à documentação de referência para essa API.

A SignedXml classe é a implementação .NET da Sintaxe de Assinatura XML do W3C (World Wide Web Consortium) e Especificação de Processamento, também conhecida como XMLDSIG (XML Digital Signature). XMLDSIG é uma maneira interoperável baseada em padrões para assinar e verificar todo ou parte de um documento XML ou outros dados endereçáveis a partir de um URI (Uniform Resource Identifier).

Use a SignedXml classe sempre que precisar compartilhar dados XML assinados entre aplicativos ou organizações de maneira padrão. Quaisquer dados assinados usando essa classe podem ser verificados por qualquer implementação em conformidade da especificação W3C para XMLDSIG.

A SignedXml classe permite que você crie os seguintes três tipos de assinaturas digitais XML:

Tipo de assinatura Descrição
Assinatura envelopada A assinatura está contida no elemento XML que está sendo assinado.
Assinatura envolvente O XML assinado está contido no <Signature> elemento .
Assinatura interna destacada A assinatura e o XML assinado estão no mesmo documento, mas nenhum elemento contém o outro.

Há também um quarto tipo de assinatura chamado assinatura externa desanexada, que é quando os dados e a assinatura estão em documentos XML separados. Assinaturas externas desanexadas não são suportadas SignedXml pela classe.

Estrutura de uma assinatura XML

XMLDSIG cria um elemento , que contém uma assinatura digital de um documento XML ou outros dados que é endereçável a partir de um <Signature> URI. O <Signature> elemento pode, opcionalmente, conter informações sobre onde encontrar uma chave que verificará a assinatura e qual algoritmo criptográfico foi usado para assinatura. A estrutura básica é a seguinte:

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

As principais partes desta estrutura são:

  • O elemento <CanonicalizationMethod>

    Especifica as regras para reescrever o Signature elemento de XML/texto em bytes para validação de assinatura. O valor padrão no .NET é http://www.w3.org/TR/2001/REC-xml-c14n-20010315, que identifica um algoritmo confiável. Esse elemento é representado pela SignedInfo.CanonicalizationMethod propriedade.

  • O elemento <SignatureMethod>

    Especifica o algoritmo usado para geração e validação de assinatura, que foi aplicado ao <Signature> elemento para produzir o valor em <SignatureValue>. No exemplo anterior, o valor http://www.w3.org/2000/09/xmldsig#rsa-sha1 identifica uma assinatura RSA PKCS1 SHA-1. Devido a problemas de colisão com SHA-1, a Microsoft recomenda um modelo de segurança baseado em SHA-256 ou superior. Esse elemento é representado pela SignatureMethod propriedade.

  • O elemento <SignatureValue>

    Especifica a assinatura criptográfica para o <Signature> elemento. Se essa assinatura não for verificada, alguma parte do <Signature> bloco foi adulterada e o documento é considerado inválido. Desde que o <CanonicalizationMethod> valor seja confiável, esse valor é altamente resistente a adulterações. Esse elemento é representado pela SignatureValue propriedade.

  • O URI atributo do <Reference> elemento

    Especifica um objeto de dados usando uma referência de URI. Esse atributo é representado pela Reference.Uri propriedade.

    • Não especificar o atributo, ou seja, definir a propriedade como null, significa que o URI aplicativo de recebimento deve saber a Reference.Uri identidade do objeto. Na maioria dos casos, um null URI resultará em uma exceção sendo lançada. Não use um URI, a menos que seu aplicativo esteja interoperando com um null protocolo que o exija.

    • Definir o atributo como uma cadeia de caracteres vazia indica que o URI elemento raiz do documento está sendo assinado, uma forma de assinatura envelopada.

    • Se o valor do URI atributo começar com #, o valor deverá ser resolvido para um elemento no documento atual. Este formulário pode ser usado com qualquer um dos tipos de assinatura suportados (assinatura envelopada, assinatura envolvente ou assinatura interna desanexada).

    • Qualquer outra coisa é considerada uma assinatura desanexada de recurso externo e não é suportada SignedXml pela classe.

  • O elemento <Transforms>

    Contém uma lista ordenada de elementos que descrevem como o signatário obteve o objeto de <Transform> dados que foi digerido. Um algoritmo de transformação é semelhante ao método de canonização, mas em vez de reescrever o elemento, ele reescreve o <Signature> conteúdo identificado pelo URI atributo do <Reference> elemento. O <Transforms> elemento é representado pela TransformChain classe.

    • Cada algoritmo de transformação é definido como tomando XML (um conjunto de nós XPath) ou bytes como entrada. Se o formato dos dados atuais for diferente dos requisitos de entrada de transformação, as regras de conversão serão aplicadas.

    • Cada algoritmo de transformação é definido como produzindo XML ou bytes como a saída.

    • Se a saída do último algoritmo de transformação não for definida em bytes (ou nenhuma transformação tiver sido especificada), o método de canonização será usado como uma transformação implícita (mesmo que um algoritmo diferente tenha sido especificado no <CanonicalizationMethod> elemento ).

    • Um valor de para o algoritmo de http://www.w3.org/2000/09/xmldsig#enveloped-signature transformação codifica uma regra que é interpretada como remover o <Signature> elemento do documento. Caso contrário, um verificador de uma assinatura envelopada digerirá o documento, incluindo a assinatura, mas o signatário teria digerido o documento antes que a assinatura fosse aplicada, levando a respostas diferentes.

  • O elemento <DigestMethod>

    Identifica o método digest (hash criptográfico) a ser aplicado no conteúdo transformado identificado pelo URI atributo do <Reference> elemento. Isto é representado pela Reference.DigestMethod propriedade.

Escolhendo um método de canonização

A menos que interopere com uma especificação que exija o uso de um valor diferente, recomendamos que você use o método de canonização .NET padrão, que é o algoritmo XML-C14N 1.0, cujo valor é http://www.w3.org/TR/2001/REC-xml-c14n-20010315. O algoritmo XML-C14N 1.0 é necessário para ser suportado por todas as implementações de XMLDSIG, particularmente porque é uma transformação final implícita a ser aplicada.

Existem versões de algoritmos de canonização que suportam a preservação de comentários. Métodos de canonização de preservação de comentários não são recomendados porque violam o princípio do "sinal do que é visto". Ou seja, os comentários em um <Signature> elemento não alterarão a lógica de processamento de como a assinatura é executada, apenas qual é o valor da assinatura. Quando combinado com um algoritmo de assinatura fraco, permitir que os comentários sejam incluídos dá a um invasor liberdade desnecessária para forçar uma colisão de hash, fazendo com que um documento adulterado pareça legítimo. No .NET Framework, somente canonicalizadores internos são suportados por padrão. Para oferecer suporte a canonicalizadores adicionais ou personalizados, consulte a SafeCanonicalizationMethods propriedade. Se o documento usar um método de canonização que não esteja na coleção representada pela SafeCanonicalizationMethods propriedade, o CheckSignature método retornará false.

Observação

Um aplicativo extremamente defensivo pode remover quaisquer valores que ele não espera que os signatários usem da SafeCanonicalizationMethods coleção.

Os valores de referência são seguros contra adulteração?

Sim, os <Reference> valores estão a salvo de adulteração. O .NET verifica a <SignatureValue> computação antes de processar qualquer um dos valores e suas transformações associadas e abortará antecipadamente para evitar instruções de <Reference> processamento potencialmente mal-intencionadas.

Escolha os elementos a serem assinados

Recomendamos que você use o valor de "" para o URI atributo (ou defina a Uri propriedade como uma cadeia de caracteres vazia), se possível. Isso significa que todo o documento é considerado para o cálculo de resumo, o que significa que todo o documento está protegido contra adulteração.

É muito comum ver URI valores na forma de âncoras como #foo, referindo-se a um elemento cujo atributo ID é "foo". Infelizmente, é fácil que isso seja adulterado porque isso inclui apenas o conteúdo do elemento de destino, não o contexto. Abusar dessa distinção é conhecido como XML Signature Wrapping (XSW).

Se seu aplicativo considera comentários semânticos (o que não é comum ao lidar com XML), então você deve usar "#xpointer(/)" em vez de "", e "#xpointer(id('foo'))" em vez de "#foo". As versões #xpointer são interpretadas como incluindo comentários, enquanto as formas de nome curto estão excluindo comentários.

Se você precisar aceitar documentos que estão apenas parcialmente protegidos e quiser garantir que está lendo o mesmo conteúdo que a assinatura protegeu, use o GetIdElement método.

Considerações de segurança sobre o elemento KeyInfo

Os dados no elemento opcional <KeyInfo> (ou seja, a propriedade), que contém uma chave para validar a KeyInfo assinatura, não devem ser confiáveis.

Em particular, quando o valor representa uma chave pública RSA, DSA ou ECDSA nua, o KeyInfo documento pode ter sido adulterado, apesar do CheckSignature método informar que a assinatura é válida. Isso pode acontecer porque a entidade que faz a adulteração só precisa gerar uma nova chave e assinar novamente o documento adulterado com essa nova chave. Portanto, a menos que seu aplicativo verifique se a chave pública é um valor esperado, o documento deve ser tratado como se tivesse sido adulterado. Isso requer que seu aplicativo examine a chave pública incorporada no documento e verifique-a em relação a uma lista de valores conhecidos para o contexto do documento. Por exemplo, se o documento pudesse ser entendido como emitido por um usuário conhecido, você verificaria a chave em relação a uma lista de chaves conhecidas usadas por esse usuário.

Você também pode verificar a chave depois de processar o documento usando o método, em vez de usar o CheckSignatureReturningKeyCheckSignature método. Mas, para a segurança ideal, você deve verificar a chave de antemão.

Como alternativa, considere tentar as chaves públicas registradas do usuário, em vez de ler o <KeyInfo> que está no elemento.

Considerações de segurança sobre o elemento X509Data

O elemento opcional <X509Data> é filho do <KeyInfo> elemento e contém um ou mais certificados X509 ou identificadores para certificados X509. Os dados no <X509Data> elemento também não devem ser inerentemente confiáveis.

Ao verificar um documento com o elemento incorporado <X509Data> , o .NET verifica apenas se os dados são resolvidos para um certificado X509 cuja chave pública pode ser usada com êxito para validar a assinatura do documento. Ao contrário de chamar o método com o verifySignatureOnly parâmetro definido como false, nenhuma verificação de revogação é executada, nenhuma confiança de CheckSignature cadeia é verificada e nenhuma expiração é verificada. Mesmo que seu aplicativo extraia o próprio certificado e o passe para o método com o CheckSignatureverifySignatureOnly parâmetro definido como false, isso ainda não é validação suficiente para impedir a adulteração do documento. O certificado ainda precisa ser verificado como apropriado para o documento que está sendo assinado.

O uso de um certificado de assinatura incorporado pode fornecer estratégias úteis de rotação de chaves, seja na <X509Data> seção ou no conteúdo do documento. Ao usar essa abordagem, um aplicativo deve extrair o certificado manualmente e executar uma validação semelhante a:

  • O certificado foi emitido diretamente ou por meio de uma cadeia por uma Autoridade de Certificação (CA) cujo certificado público está incorporado no aplicativo.

    Usar a lista de confiança fornecida pelo sistema operacional sem verificações adicionais, como um nome de assunto conhecido, não é suficiente para impedir a adulteração no SignedXml.

  • Verifica-se que o certificado não expirou no momento da assinatura do documento (ou "agora" para processamento de documentos quase em tempo real).

  • Para certificados de longa duração emitidos por uma autoridade de certificação que ofereça suporte à revogação, verifique se o certificado não foi revogado.

  • O assunto do certificado é verificado como sendo apropriado para o documento atual.

Escolhendo o algoritmo de transformação

Se você estiver interoperando com uma especificação que ditou valores específicos (como XrML), então você precisa seguir a especificação. Se você tiver uma assinatura envelopada XmlDsigEnvelopedSignatureTransform (como ao assinar o documento inteiro), precisará usar http://www.w3.org/2000/09/xmldsig#enveloped-signature (representado pela classe). Você também pode especificar a transformação XML-C14N implícita, mas não é necessária. Para uma assinatura envolvente ou destacada, nenhuma transformação é necessária. A transformação XML-C14N implícita cuida de tudo.

Com a atualização de segurança introduzida pelo Boletim de Segurança da Microsoft MS16-035, o .NET restringiu quais transformações podem ser usadas na verificação de documentos por padrão, com transformações não confiáveis fazendo com CheckSignature que sempre retornemfalse. Em particular, as transformações que exigem entrada adicional (especificadas como elementos filho no XML) não são mais permitidas devido à sua suscetibilidade de abuso por usuários mal-intencionados. O W3C aconselha evitar as transformações XPath e XSLT, que são as duas principais transformações afetadas por essas restrições.

O problema com referências externas

Se um aplicativo não verificar se as referências externas parecem apropriadas para o contexto atual, elas podem ser abusadas de maneiras que fornecem muitas vulnerabilidades de segurança (incluindo Negação de Serviço, Negação de Serviço de Reflexão Distribuída, Divulgação de Informações, Bypass de Assinatura e Execução Remota de Código). Mesmo se um aplicativo validasse o URI de referência externa, permaneceria um problema de o recurso ser carregado duas vezes: uma vez quando o aplicativo lê-lo e outra quando SignedXml lê-lo. Como não há garantia de que as etapas de leitura e verificação de documentos do aplicativo tenham o mesmo conteúdo, a assinatura não fornece confiabilidade.

Dado os riscos de referências externas, SignedXml lançará uma exceção quando uma referência externa for encontrada. Para obter mais informações sobre esse problema, consulte o artigo 3148821 da Base de Dados de Conhecimento.