Pergunta

Existe uma maneira de definir o prefixo sobre a assinatura de um documento XML Assinado (classe SignedXml em .net)?

Assim em vez de:

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#>
...
</Signature>

Eu poderia ter o seguinte:

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#>
...
</ds:Signature>
Foi útil?

Solução

Em primeiro lugar, não há realmente nenhuma boa razão para fazer isso. As duas formas são funcionalmente equivalentes. Qualquer processador XML bem-comportado vai lidar com eles absolutamente idêntica. Então, se você está tentando falar com um aplicativo que não implementar corretamente namespaces XML, é melhor (IMO) apenas para deixar o formulário padrão sozinho. (E mesmo nesse caso, seria melhor, se possível, para obter o deficiente aplicação fixa em seu lugar.)

Dito isso, você pode configurar manualmente o prefixo na XmlElement retornado pelo SignedXml.GetXml () e seus elementos filho usando XPath como esta:

XmlElement signature = signedXml.GetXml();
foreach (XmlNode node in signature.SelectNodes(
    "descendant-or-self::*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#']"))
{
    node.Prefix = "ds";
}

Outras dicas

Ele não pode ser feito. Se você modificar o XML depois de ter sido assinado pode não ser capaz de ser verificada, que era o caso no exemplo acima. IMO esta é uma falha na implementação assinatura digital da MSFT que você terá que viver com ele.

Um monte de gente vai dizer que não há nenhuma razão para fazer isso, e eles são tecnicamente correta. Mas quando você está lidando com um grande fornecedor (ou seja, um governo estadual ou banco), boa sorte levá-los a mudá-lo em sua extremidade. A maioria das implementações de referência incluí-lo.

UPDATE: Os sinais de assinatura tudo no elemento SignedInfo, então se você ir atualizando esse elemento após o fato, então a assinatura não é mais válido. Você "adulterado" com a mensagem.

Pode ser feito, mas é necessário modificar a classe SignedXml para adicionar o prefixo antes de começar o valor digerir do nó SignedInfo.

O método ComputeSignature será modificar para adicionar o prefixo parâmetro

public void ComputeSignature(string prefix){...}

Quando este método é chamado ele calcula a assinatura Valor digerindo o valor do nó SignedInfo, se você receber esse valor, sem o prefixo "ds" e em seguida, adicione o prefixo você receberá uma assinatura inválida, então você terá que adicionar o prefixo antes de obter o valor de digerir do nó SignedInfo.

Este valor digerir é gerado no método GetC14NDigest, de modo que este método será modificar para adicionar o parâmetro prefixo e adicionar o prefixo antes de começar o valor digerir

private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
    XmlDocument document = new XmlDocument();
    document.PreserveWhitespace = false;
    XmlElement e = this.SignedInfo.GetXml(); //get the signedinfo nodes
    document.AppendChild(document.ImportNode(e, true));        
    Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;       
    SetPrefix(prefix, document.DocumentElement); /*Set the prefix before getting the HASH*/
    canonicalizationMethodObject.LoadInput(document);
    return canonicalizationMethodObject.GetDigestedOutput(hash);
}

Ok, então agora você tem a assinatura Valor dos nós SignedInfo com o prefixo "ds", que se disse você ainda não tem o xml com o prefixo ainda, por isso, se você acabou de chamar o método GetXml você obterá xml sem o prefixo e, claro, porque o valor de assinatura foi calculado considerando as ds prefixo você terá uma assinatura inválida. Para evitar isso e obter a estrutura xml com o prefixo que você tem que modificar o método GetXml, adicione o parâmetro prefixo, e chamar o método SetPrefix wich irá acrescentar o prefixo "ds" a todos os nós na assinatura XML

public XmlElement GetXml(string prefix)
{
    XmlElement e = this.GetXml();
    SetPrefix(prefix, e); //return the xml structure with the prefix
    return e;
}

Vou deixar aqui a classe com essas modificações

CUSTOM CLASS

internal sealed class CustomSignedXml : SignedXml
{
    XmlElement obj = null;
    public CustomSignedXml (XmlDocument xml)
        : base(xml)
    {
    }

    public CustomSignedXml (XmlElement xmlElement)
        : base(xmlElement)
    {

    }

    public XmlElement GetXml(string prefix)
    {
        XmlElement e = this.GetXml();
        SetPrefix(prefix, e);
        return e;
    }

    public void ComputeSignature(string prefix)
    {
        this.BuildDigestedReferences();
        AsymmetricAlgorithm signingKey = this.SigningKey;
        if (signingKey == null)
        {
            throw new CryptographicException("Cryptography_Xml_LoadKeyFailed");
        }
        if (this.SignedInfo.SignatureMethod == null)
        {
            if (!(signingKey is DSA))
            {
                if (!(signingKey is RSA))
                {
                    throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed");
                }
                if (this.SignedInfo.SignatureMethod == null)
                {
                    this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
                }
            }
            else
            {
                this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
            }
        }
        SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription;
        if (description == null)
        {
            throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
        }
        HashAlgorithm hash = description.CreateDigest();
        if (hash == null)
        {
            throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
        }
        this.GetC14NDigest(hash, prefix);
        this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
    }         

    private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
    {

        XmlDocument document = new XmlDocument();
        document.PreserveWhitespace = false;
        XmlElement e = this.SignedInfo.GetXml();
        document.AppendChild(document.ImportNode(e, true));               

        Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;            
        SetPrefix(prefix, document.DocumentElement); //Set the prefix before getting the HASH
        canonicalizationMethodObject.LoadInput(document);
        return canonicalizationMethodObject.GetDigestedOutput(hash);
    }

    private void BuildDigestedReferences()
    {
        Type t = typeof(SignedXml);
        MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
        m.Invoke(this, new object[] { });
    }

    private void SetPrefix(string prefix, XmlNode node)
    {
       foreach (XmlNode n in node.ChildNodes)
          SetPrefix(prefix, n);
       node.Prefix = prefix;
    }
}

E a maneira de usá-lo

CustomSignedXml signedXml = new CustomSignedXml();
.
.//your code
. 

//compute the signature with the "ds" prefix

signedXml.ComputeSignature("ds");

//get the xml of the signature with the "ds" prefix

XmlElement xmlDigitalSignature = signedXml.GetXml("ds");
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top