我在使用BouncyCastle检查XMLSignature以验证使用ECDSA的Sigantures时遇到问题。
以下是相关的代码行:
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
//some unrelated code
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM", provider);
在最后一行,抛出以下异常:
javax.xml.crypto.NoSuchMechanismException: java.security.NoSuchAlgorithmException: no such algorithm: DOM for provider SC
如果我将行更改为
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
我得到了
javax.xml.crypto.MarshalException: unsupported SignatureMethod algorithm: http://www.w3.org/2007/05/xmldsig-more#ecdsa-ripemd160
任何人有任何想法是什么导致了这个?
答案 0 :(得分:3)
在第一个错误中,您可以在错误描述中读取“没有这样的算法:提供者SC的DOM”。这很奇怪,因为它表示“提供商SC”(Sun PC / SC提供商)而不是“提供商BC”(BouncyCastle安全提供商)应该如此。看起来你的代码(内部)没有使用Bouncycastle。你应该找出为什么会这样。它可能是关于BC库和类路径(如果您正在使用应用程序服务器)或提供程序订单配置。
第二个错误。您改变了获取XMLSignatureFactory的方法。这个更好,因为如果你没有指定提供者,因为:
此方法使用标准JCA提供程序查找机制进行定位 并实例化所需的XMLSignatureFactory实现 机制类型。它遍历注册安全性列表 提供商,从最受欢迎的提供商开始。一个新的 来自第一个支持的Provider的XMLSignatureFactory对象 返回指定的机制。
但现在,算法不存在。所以为什么?在这里,我会说BC没有被使用。它在那里?查看您的类路径。
列出所有可用提供商可能会有所帮助:
for (Provider p : Security.getProviders()) {
log.debug(p.getName());
log.debug(p.getInfo());
}
答案 1 :(得分:0)
根据您的第二个异常猜测,因为您要验证xml签名, 您可能已经使用了类似以下的代码,这些代码来自https://www.massapi.com/class/xm/XMLSignatureFactory-2.html
// Step 1: Load an XMLSignatureFactory instance. This factory class will
// be responsible for constructing almost all the major objects we need
// in working with XML Signature in JSR-105 APIs, except those related
// to KeyInfo.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
// Step 3 : Find all Xml Signature element into the provided XML
// document (here for sample use only the first)
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("Cannot find Signature element!");
}
// Step 4: Create a DOMValidateContext instance (extract public key from
// the "KeyInfo" bloc using overrided KeySelector impl.)
DOMValidateContext valContext = new DOMValidateContext(new KeyValueKeySelector(), nl.item(0));
// Step 5: Unmarshal the Signature node into an XMLSiganture object.
XMLSignature signature = fac.unmarshalXMLSignature(valContext);
// Step 6 : Validate signature
boolean isValid = signature.validate(valContext);
if (isValid) {
System.out.println("OK");
}
但是xml表示形式类似
<Signature xmlns="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/2007/05/xmldsig-more#ecdsa-ripemd160"/>
<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>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>
</Reference>
</SignedInfo>
...
</Signature>
因此,工厂无法将xml表示形式解组为XMLSignature对象。
fac
实际上是由org.jcp.xml.dsig.internal.dom.XMLDSigRI
提供者提供的,请确保检查$JRE_HOME/lib/security/java.security
。 fac
是DOMXMLSignatureFactory
类的实例。
解组动作将尝试使用Signature
对象的形式,并且必须首先形成子对象,例如CanonicalizationMethod
,然后是SignatureMethod
,因此最后到达引发异常的调用,
在DOMSignedInfo
构造函数DOMSignedInfo(Element var1, XMLCryptoContext var2, Provider var3) throws MarshalException
Element var5 = DOMUtils.getNextSiblingElement(var4, "SignatureMethod");
this.signatureMethod = DOMSignatureMethod.unmarshal(var5);
向DOMSignatureMethod.unmarshal
静态方法又迈进了一步,它使用http://www.w3.org/2007/05/xmldsig-more#ecdsa-ripemd160
查找数字签名算法,
但DOMSignatureMethod
仅支持以下算法,
rsa-sha1
rsa-sha256
rsa-sha384
rsa-sha512
dsa-sha1
dsa-sha256
hmac-sha1
hmac-sha256
hmac-sha384
hmac-sha512
ecdsa-sha1
ecdsa-sha256
ecdsa-sha384
ecdsa-sha512
和解决方案: 将BC提供程序添加到“安全性”中,并使用org.apache.santuario -xmlsec 项目中的 XMLSignature进行验证,
public boolean verify(String signedXML) throws Exception {
Document doc = null;
try (InputStream is = new ByteArrayInputStream(signedXML.getBytes(Charset.forName("utf-8")))) {
doc = MyXMLUtils.read(is, false);
}
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
xpath.setNamespaceContext(new DSNamespaceContext());
String expression = "//ds:Signature[1]";
Element sigElement =
(Element) xpath.evaluate(expression, doc, XPathConstants.NODE);
XMLSignature signature = new XMLSignature(sigElement, "");
KeyInfo ki = signature.getKeyInfo();
if (ki == null) {
throw new RuntimeException("No keyinfo");
}
PublicKey pk = signature.getKeyInfo().getPublicKey();
if (pk == null) {
throw new RuntimeException("No public key");
}
return signature.checkSignatureValue(pk);
}
回购中的更多演示:https://github.com/Honwhy/xml-sec