我正在寻找一些帮助来解决我在使用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 / jsY8Im2rgjsozBXRxkQjQ =
2011-11-12 18:30:27参考文献[WARN] 实际摘要: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");
应用签名之前的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引用的项目有关;不知何故,验证期间发生的转换是增加46个字节,导致检查失败。
不确定这是从哪里来的。有人有什么想法吗?
答案 0 :(得分:0)
这与您的错误没有直接关系,但它会在以后导致失败:您不应该删除或更改SignedInfo元素中的任何内容。最终签名值是在规范化SignedInfo元素的摘要上计算的。因此,如果您在验证签名之前更改了任何内容,那么您肯定会打破它。
至于为什么您的文档摘要无效 - 这可能有多种原因,难以从猜测中分析。如果你可以在这里发布未签名的文件,那么我会告诉你正确的摘要值是什么,至少......
答案 1 :(得分:0)
问题不在于SignedInfo,而在于KeyInfo节点。 KeyInfo节点添加了一个名称空间属性,该属性在应用原始签名时不存在。在检查签名值之前删除KeyInfo属性可以解决我看到的问题。
答案 2 :(得分:0)
简短回答:在原始问题中,在doc.normalizeDocument();
开头添加applySignature
,然后再添加签名。
更多信息:
我知道这是一个老问题,但我有一个类似的问题,任何答案都没有解决。 事实上,我认为其中一个答案是错误的,它说问题出在验证中。但是,我相信它正在签约!我希望将我的经验和解决方案放在这里帮助有类似问题的人。
我正在生成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提供出色的调试输出。此输出显示wh:en验证,以下命名空间已添加到<object>
和<SignedInfo>
:
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正确签名。所以他们都需要这个才能正确签名。