Skip to content

Commit

Permalink
add support for ecdsa-ripemd160
Browse files Browse the repository at this point in the history
  • Loading branch information
honwhy.wang committed Sep 10, 2018
1 parent 950c7b1 commit 4e23f53
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 8 deletions.
41 changes: 41 additions & 0 deletions src/main/java/com/honey/xmlsec/BcEcdsaSignatureAlgorithm.java
@@ -0,0 +1,41 @@
package com.honey.xmlsec;

import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.XMLSignatureException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.security.NoSuchAlgorithmException;
import java.security.Signature;

/**
* for SignatureMethod, baseURI=http://www.w3.org/2007/05/xmldsig-more#ecdsa-ripemd160
*/
public class BcEcdsaSignatureAlgorithm extends BcSignatureAlgorithm{
public BcEcdsaSignatureAlgorithm(Document doc, String algorithmURI) throws XMLSecurityException {
super(doc, algorithmURI);
}

public BcEcdsaSignatureAlgorithm(Document doc, String algorithmURI, int hmacOutputLength) throws XMLSecurityException {
super(doc, algorithmURI, hmacOutputLength);
}

public BcEcdsaSignatureAlgorithm(Element element, String baseURI) throws XMLSecurityException {
super(element, baseURI);
}

public BcEcdsaSignatureAlgorithm(Element element, String baseURI, boolean secureValidation) throws XMLSecurityException {
super(element, baseURI, secureValidation);
}

@Override
protected void initEngine() throws XMLSecurityException {
try {
JCEMapper.setProviderId(provider.getName());
engine = Signature.getInstance("RIPEMD160withECDSA", provider);
} catch (NoSuchAlgorithmException e) {
throw new XMLSignatureException(e);
}
}
}
8 changes: 4 additions & 4 deletions src/main/java/com/honey/xmlsec/MyUtil.java
Expand Up @@ -143,8 +143,8 @@ public String signWithCertEcdsa(String sourceXml, PrivateKey privateKey, X509Cer
null, Constants._ATT_ALGORITHM, Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS
);

AbstractSignatureAlgorithm signatureAlgorithm =
new BcEcdsaSignatureAlgorithm(doc, XMLSignature.ALGO_ID_SIGNATURE_ECDSA_RIPEMD160);
SignatureAlgorithm signatureAlgorithm =
new SignatureAlgorithm(doc, XMLSignature.ALGO_ID_SIGNATURE_ECDSA_RIPEMD160);
XMLSignature sig =
new XMLSignature(doc, null, signatureAlgorithm.getElement(), canonElem);

Expand All @@ -158,8 +158,8 @@ public String signWithCertEcdsa(String sourceXml, PrivateKey privateKey, X509Cer
x509data.addCertificate(signingCert);

sig.getKeyInfo().add(x509data);
//sig.sign(privateKey);
signatureAlgorithm.doSign(privateKey,sig.getSignedInfo());
sig.sign(privateKey);
//signatureAlgorithm.doSign(privateKey,sig.getSignedInfo());
ByteArrayOutputStream bos = new ByteArrayOutputStream();

XMLUtils.outputDOMc14nWithComments(doc, bos);
Expand Down
207 changes: 207 additions & 0 deletions src/test/java/com/honey/xmlsec/XPointerResourceResolver.java
@@ -0,0 +1,207 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.honey.xmlsec;

import org.apache.xml.security.signature.XMLSignatureInput;
import org.apache.xml.security.utils.resolver.ResourceResolverContext;
import org.apache.xml.security.utils.resolver.ResourceResolverException;
import org.apache.xml.security.utils.resolver.ResourceResolverSpi;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* An implementation of a resource resolver, which evaluates xpointer expressions.
*
*/
public class XPointerResourceResolver extends ResourceResolverSpi {
private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(XPointerResourceResolver.class);

private static final String XP_OPEN = "xpointer(";
private static final String XNS_OPEN = "xmlns(";

private Node baseNode;

public XPointerResourceResolver(Node baseNode) {
this.baseNode = baseNode;
}

@Override
public boolean engineCanResolveURI(ResourceResolverContext context) {
String v = context.uriToResolve;

if (v == null || v.length() <= 0) {
return false;
}

if (v.charAt(0) != '#') {
return false;
}

String xpURI;
try {
xpURI = URLDecoder.decode(v, "utf-8");
} catch (UnsupportedEncodingException e) {
LOG.warn("utf-8 not a valid encoding",e);
return false;
}

String parts[] = xpURI.substring(1).split("\\s");

// plain ID reference.
if (parts.length == 1 && !parts[0].startsWith(XNS_OPEN)) {
return true;
}

int i = 0;
for (; i < parts.length - 1; ++i) {
if (!parts[i].endsWith(")") || !parts[i].startsWith(XNS_OPEN)) {
return false;
}
}

if (!parts[i].endsWith(")") || !parts[i].startsWith(XP_OPEN)) {
return false;
}

LOG.debug("xpURI = " + xpURI);
LOG.debug("BaseURI = " + context.baseUri);

return true;
}

@Override
public XMLSignatureInput engineResolveURI(ResourceResolverContext context)
throws ResourceResolverException {
String v = context.uriToResolve;

if (v.charAt(0) != '#') {
return null;
}

String xpURI;
try {
xpURI = URLDecoder.decode(v, "utf-8");
} catch (UnsupportedEncodingException e) {
LOG.warn("utf-8 not a valid encoding ", e);
return null;
}

String parts[] = xpURI.substring(1).split("\\s");

int i = 0;
Map<String, String> namespaces = new HashMap<>();

if (parts.length > 1) {

for (; i < parts.length - 1; ++i) {
if (!parts[i].endsWith(")") || !parts[i].startsWith(XNS_OPEN)) {
return null;
}

String mapping = parts[i].substring(XNS_OPEN.length(), parts[i].length() - 1);

int pos = mapping.indexOf('=');

if (pos <= 0 || pos >= mapping.length() - 1) {
throw new ResourceResolverException(
"malformed namespace part of XPointer expression", context.uriToResolve, context.baseUri
);
}

namespaces.put(
mapping.substring(0, pos),
mapping.substring(pos + 1)
);
}
}

try {
Node node = null;
NodeList nodes = null;

// plain ID reference.
if (i == 0 && !parts[i].startsWith(XP_OPEN)) {
node = this.baseNode.getOwnerDocument().getElementById(parts[i]);
} else {
if (!parts[i].endsWith(")") || !parts[i].startsWith(XP_OPEN)) {
return null;
}

String xpathExpr = parts[i].substring(XP_OPEN.length(), parts[i].length() - 1);

XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
DSNamespaceContext namespaceContext =
new DSNamespaceContext(namespaces);
xpath.setNamespaceContext(namespaceContext);

nodes =
(NodeList) xpath.evaluate(
xpathExpr, this.baseNode, XPathConstants.NODESET
);

if (nodes.getLength() == 0) {
return null;
}
if (nodes.getLength() == 1) {
node = nodes.item(0);
}
}

XMLSignatureInput result = null;

if (node != null) {
result = new XMLSignatureInput(node);
} else if (nodes != null) {
Set<Node> nodeSet = new HashSet<>(nodes.getLength());

for (int j = 0; j < nodes.getLength(); ++j) {
nodeSet.add(nodes.item(j));
}

result = new XMLSignatureInput(nodeSet);
} else {
return null;
}

result.setMIMEType("text/xml");
result.setExcludeComments(true);
result.setSourceURI((context.baseUri != null) ? context.baseUri.concat(v) : v);

return result;
} catch (XPathExpressionException e) {
throw new ResourceResolverException(
e, context.uriToResolve, context.baseUri, "Problem evaluating XPath expression"
);
}
}

}
47 changes: 43 additions & 4 deletions src/test/java/com/honey/xmlsec/XmlSecApplicationTests.java
@@ -1,16 +1,22 @@
package com.honey.xmlsec;

import org.apache.xml.security.Init;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.X509Certificate;

Expand All @@ -29,6 +35,8 @@ public void contextLoads() {
private static KeyPair keyPair;
static {
try {
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
Init.init();
} catch (NoSuchAlgorithmException e) {
Expand Down Expand Up @@ -79,6 +87,8 @@ public static void main(String[] args) throws Exception{
X509Certificate x509 = (X509Certificate)keyStore.getCertificate("ECDSA");
String signed3 = myUtil.signWithCertEcdsa(body, privateKey2, x509);
System.err.println(signed3);
boolean validate3 = myUtil.verify(signed3);
System.err.println(validate3);
/** close streams */
fis.close();
//fis2.close();
Expand All @@ -99,4 +109,33 @@ public static String getFileContent(
return sb.toString();
}
}

private static boolean doVerify(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, "");

signature.addResourceResolver(new XPointerResourceResolver(sigElement));

KeyInfo ki = signature.getKeyInfo();
if (ki == null) {
throw new RuntimeException("No keyinfo");
}
X509Certificate cert = signature.getKeyInfo().getX509Certificate();

if (cert == null) {
throw new RuntimeException("No certificate");
}
return signature.checkSignatureValue(cert);
}
}
Binary file added src/test/resources/ecdsa.jks
Binary file not shown.

0 comments on commit 4e23f53

Please sign in to comment.