Question

I have a Problem with checking an XMLSignature using BouncyCastle for verifying Sigantures that use ECDSA.

Here are the relevant lines of code:

BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);   
//some unrelated code
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM", provider);

At the last line, the following Exception in thrown:

javax.xml.crypto.NoSuchMechanismException: java.security.NoSuchAlgorithmException: no such algorithm: DOM for provider SC

If I change the line to

XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");

I get

javax.xml.crypto.MarshalException: unsupported SignatureMethod algorithm: http://www.w3.org/2007/05/xmldsig-more#ecdsa-ripemd160

Anyone got any Ideas what causes this?

Was it helpful?

Solution

In your first error, you can read in the error description that "no such algorithm: DOM for provider SC". That is weird, because it says "provider SC" (Sun PC/SC provider) and not "provider BC" (BouncyCastle Security Provider) as it should. It looks like you code (internally) is not using Bouncycastle as you want. You should find out why this is happening. It could be something regarding BC library and classpath (if you are working with an application server) or the providers order configuration.

Second error. You change your approach getting XMLSignatureFactory. This one is better, because if you do not specify the provider, because:

This method uses the standard JCA provider lookup mechanism to locate and instantiate an XMLSignatureFactory implementation of the desired mechanism type. It traverses the list of registered security Providers, starting with the most preferred Provider. A new XMLSignatureFactory object from the first Provider that supports the specified mechanism is returned.

But now, algorithm is not there. so, why? Here, I would say that BC is not being used. It is there? Review your classpath.

It may help to list all provider available:

for (Provider p : Security.getProviders()) {

    log.debug(p.getName());
    log.debug(p.getInfo());
}

OTHER TIPS

guess from your second exception, since you want to validate the xml signature, you may have use some code like the following, code from 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");
}

but the xml representation has something like

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

therefore, factory failed to unmarshall the xml representation to XMLSignature object. actually fac was provided by the org.jcp.xml.dsig.internal.dom.XMLDSigRI Provider, check $JRE_HOME/lib/security/java.security for sure. fac is an instance of DOMXMLSignatureFactory class. the unmarshall actions will try form Signature object, and must form child object first, such as CanonicalizationMethod and then SignatureMethod, so at last reach the invocation that raise exception, at DOMSignedInfo constructor DOMSignedInfo(Element var1, XMLCryptoContext var2, Provider var3) throws MarshalException

Element var5 = DOMUtils.getNextSiblingElement(var4, "SignatureMethod");
this.signatureMethod = DOMSignatureMethod.unmarshal(var5);

just one more step to DOMSignatureMethod.unmarshal static method, it use http://www.w3.org/2007/05/xmldsig-more#ecdsa-ripemd160 to look up Digital Signature Algorithm, but DOMSignatureMethod only support the following algorithm,

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

and the solution: add BC provider to Security and use XMLSignature from org.apache.santuario -xmlsec project to verify,

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);
}

more demos from repo: https://github.com/Honwhy/xml-sec

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top