【问题标题】:Unable to verify XML Signature using Apache Santuario 1.4.6无法使用 Apache Santuario 1.4.6 验证 XML 签名
【发布时间】:2011-11-13 02:29:06
【问题描述】:

我正在寻求一些帮助来解决我在使用 Apache Santuario Java 库版本 1.4.6 验证 XML 签名时遇到的问题。我有一个客户端/服务器解决方案,其中客户端在将文档发送到服务器之前签署 DOM 文档。我将签名应用于文档,如下所示:

public static void applySignature(X509Certificate cert, PrivateKey privateKey, Document doc)
{
    try
    {
        XMLSignature sig = new XMLSignature(doc, 
                                            "", 
                                            XMLSignature.ALGO_ID_SIGNATURE_RSA);

        sig.addResourceResolver(new XmlSignatureResolver());
        doc.getDocumentElement().appendChild(sig.getElement());

        Transforms transforms = new Transforms(doc);
        transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
        transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);

        sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);

        sig.addKeyInfo(cert);
        sig.addKeyInfo(cert.getPublicKey());

        sig.sign(privateKey);
    }
    catch (XMLSecurityException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

我验证签名如下:

public static boolean verifySignature(X509Certificate cert, Document doc)
{
    boolean validSignature = false;

    try
    {
        Element nscontext = createDSctx(doc, "ds", Constants.SignatureSpecNS); 

        // Remove any attributes of Signed Info
        Node signInfoNode = XPathAPI.selectSingleNode(doc, "//ds:SignedInfo", nscontext);

        int numAttributes = signInfoNode.getAttributes().getLength();
        if (numAttributes > 0)
        {
            for (int i = 0; i < numAttributes; i++)
            {
                String attrName = signInfoNode.getAttributes().item(0).getNodeName();
                signInfoNode.getAttributes().removeNamedItem(attrName);
            }    
        }

        Element sigElement = 
            (Element) XPathAPI.selectSingleNode(doc, "//ds:Signature", nscontext); 
        XMLSignature signature = new XMLSignature(sigElement, "");

        signature.setFollowNestedManifests(true); 
        signature.addResourceResolver(new XmlSignatureResolver());

        validSignature = signature.checkSignatureValue(cert);

        //  Remove the signature
        sigElement.getParentNode().removeChild(sigElement);            
    }
    catch (XMLSignatureException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    catch (XMLSecurityException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    catch (TransformerException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return validSignature;// validSignature;
}

我遇到的问题是,当我在服务器上验证签名时(如果我在应用签名后立即验证,它将在客户端上工作),我收到以下警告:

2011-11-12 18:30:27 参考 [WARN] URI 验证失败 ""
2011-11-12 18:30:27 参考 [WARN] 预期摘要: EEl+J/jsY8Im2rgjsozBXRx​​kQjQ=
2011-11-12 18:30:27 参考 [警告] 实际摘要:Y7C0HCjugZbegkZT4E8A7Bd4qm0=

感谢您的帮助,
厄尼伯利森

=============

我用来将dom从客户端发送到服务器的代码是:

                // Use a Transformer for output
                TransformerFactory tFactory = TransformerFactory.newInstance();
                Transformer transformer = tFactory.newTransformer();
                transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
                transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
                transformer.setOutputProperty(OutputKeys.INDENT, "no");

                DOMSource source = new DOMSource(doc);
                StreamResult result = new StreamResult(m_SenderOutput);

                m_Logger.debug("Transforming...");
                transformer.transform(source, result);   
                m_SenderOutput.flush();
                m_Logger.debug("Transform complete...");
                m_ClientSocket.shutdownOutput();

服务端读取dom的代码是:

                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                dbf.setNamespaceAware(true);
                DocumentBuilder db = dbf.newDocumentBuilder();

                m_Logger.debug("Parsing Document");
                Document doc = db.parse(m_SenderInput);
                m_Logger.debug("Received DOM");   

apply签名前的dom类似如下(数据在签名前被加密):

<?xml version="1.0" encoding="UTF-8"?><SmMessageSet xmlns="urn:ccsds:recommendation:service_management:schema:sccs:R1.0" xmlns:ns2="urn:ccsds:recommendation:navigation:schema:ndmxml:R1.5"><xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Content"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/><xenc:CipherData><xenc:CipherValue>VosyFTcuAkzo6WPPLnnM2Nka+gpyD9r2cNy3fbSX8RjGg5dKktK9SGZAar5t3ci2mU6Nw9Ski2Td
g1WNei+kgns6vFET5Ff8m5/VIO24sBz30DPO5cAwfLax0slTjZWDRu7XXs/ORSK2PrB8B8qaO+me
W5iPLXjkkL4LnLwZfIvCSdG3JJoOTUhR6CstquTejRBLvTdvry8jB2RncjpV244eng7Bmk7HWcNd
Mz20DujfX14MTyKAQcVAgUhM9MpisveiDRdKYtXWCkma2NcUhpxqzjyPtyJtHVJQfaPZ2kla2NQV
DcMPUvmM+V0Y3kI5NBZq1vlIAg1i5JsZRniB+Q==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey></ds:KeyInfo><xenc:CipherData><xenc:CipherValue>8UCDr2ZzDvD5JczkPU7UnxRYBdxs6ZgL5s2ksHyn/FZvBVSwYh6o/Rnx41fnN6uygcylW++zoxSq
a9qcpuS8rFxtw6TtRzZeixJYBgZWVHp9NYiB4WbtZF6iR5EjaGKZdghUgCVtvKKbpbMQTTPRCBym
7HA2iQzNpGH1tcGegDB8+w3ALDP8QN5q3PG2uFBk880KXRozxAxKZVNKZfEZyat0fnzf6J8bTCac
n1lxV02jCWyz1/2Gd/jfo8B2BLXVMZWm0WiM7Z/uk4PFsTQjPmb1CD+E+7Oh8TRJzIqC1dyPQVV+
kgdoJbM/2sZka1VCuUzEIEQ1fhH+iUE0ymtuw+djwhfqDAow1pfRJOsak2cXzLoYO7mwqmIHoeaM
hN9IAtI/TfXDHNSL8ledhYT/ZL2gmNSR1Jze6JZPaXgqkmBEGVgqbzLex/5drxOf/DQVcugSnqEw
uHrikLsjU4jHozNg4PGidJNPCKLPgJaiLX1rgyo9N6pUDMVrNH+Tz1G7EFydzZOrZt+yY8Je17NL
ah8mBQb/S5zGD7642aDR4UmVQthD3LTMIG/oxbzMIh/OOcC432SZ+ShAvUD+bU+GDDdcOKzemLPB
EV6QLstFqonyHLSTQqgIMU5z2NxFpJIKRBClX09q5fOytaRVrGIZgJtOfuT4zFwjmwF66yuiQp0H
gD9O95A+ifmwe8k9KUsAO9Q8alxrXrqhptfsySCYDo2nSXbhSn5cKgsdK4jw5B6zsoQQZxJmzsYT
ZSbo0DhjAbZVszsU0HeTMRKRNlOABXAJPxSmqz2hT/wgYnWSMZt839swJyOZhaMuOUfShAP1iVo+
m5xM+zw7SnsAwFozNw==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData></SmMessageSet>

在 applySignature 之后和在 verifysignature 方法中修改 SignedInfo 之后的 XML 是相同的。当我通过套接字发送文档时,问题似乎出在 SignedInfo Referenced Items 上;不知何故,验证期间发生的转换添加了 46 个字节,导致检查失败。

不确定这是从哪里来的。有人有什么想法吗?

【问题讨论】:

    标签: java xml security apache


    【解决方案1】:

    这与您的错误没有直接关系,但稍后会导致失败:您不应该删除或更改 SignedInfo 元素中的任何内容。最终签名值是根据规范化的 SignedInfo 元素的摘要计算得出的。因此,如果您在验证签名之前更改任何内容,您肯定会破坏它。

    至于为什么您的文档摘要无效 - 这可能有多种原因,仅凭猜测就很难分析。如果您可以在此处发布未签名的文档,那么我可以告诉您正确的摘要值是多少,至少...

    【讨论】:

    • 感谢您的回复。该代码删除了 SignedInfo 的属性,因为我试图让文档看起来与它来自客户端的 applysignature 相同。我在想我在 dom 上执行的通过套接字发送数据的转换可能已经添加了额外的属性。
    【解决方案2】:

    问题不在于 SignedInfo,而在于 KeyInfo 节点。 KeyInfo 节点添加了一个命名空间属性,该属性在应用原始签名时不存在。在检查签名值之前删除 KeyInfo 属性可以解决我看到的问题。

    【讨论】:

      【解决方案3】:

      简短回答:在原始问题中,在applySignature 的开头添加doc.normalizeDocument();,所以在添加签名之前。

      更多信息:

      我知道这是一个老问题,但我有一个类似的问题,但任何答案都没有解决。 事实上,我认为其中一个答案是错误的,它说问题出在验证中。但是,我相信它是在签约!我希望把我的经验和解决方案放在这里可以帮助遇到类似问题的人。

      我正在生成一个 XML 文档,然后对其进行签名。我试图与 Apache Santuario 签约。当这不起作用时,我切换到 javax.xml.crypto.dsig。

      这两种方法生成的 XML 文档在我使用 Santuario 和外部工具检查时都没有正确签名!

      首先是我尝试生成的签名 XML 的示例:

      <signed-envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="somenamespace" xsi:schemaLocation="someshemelocation">
         <object xml:id="object0">
            ...
         </object>
         <signatures>
            <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
               ...
               <SignedInfo>
                  <Reference URI="#object0">
                  ...
               </SignedInfo>
            </Signature>
         </signatures>
      </signed-envelope>
      

      signed-envelope 的命名空间至关重要!没有命名空间,一切都完美无缺。但是当我将所需的命名空间添加到signed-envelope 时,签名会生成一个无法验证的 XML 文件。也不是通过外部工具(这意味着签名失败,而不是验证)!

      Santuario 使用 log4j 提供出色的调试输出。此输出表明,在验证时,以下命名空间已添加到 &lt;object&gt;&lt;SignedInfo&gt;

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      

      这是正确的验证方式!这里的答案之一表明它不正确并试图修复它,但这实际上是正确的。这里的问题不在于验证,而在于签名!

      问题是 Santuario 在签署文件时显示未添加此命名空间!这让我相信我生成的 Document 对象有问题。 我的 XML 文档是使用添加到根元素的命名空间生成的,如下所示:

      rootElement.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:noNamespaceSchemaLocation", "some_namespace");
      

      经过大量搜索,我发现doc.normalizeDocument() 解决了类似的问题。我试过了,一切正常!
      我不知道为什么doc.normalizeDocument() 使一切正常,但现在我可以使用 Apache Santuario 和 javax.xml.crypto.dsig 正确签名。所以他们都需要这个才能正确签名。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-03-03
        • 1970-01-01
        • 1970-01-01
        • 2013-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多