/*
* This file is part of rockframework.
*
* rockframework is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* rockframework is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>;.
*/
package br.net.woodstock.rockframework.security.sign.impl;
import java.io.ByteArrayInputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import br.net.woodstock.rockframework.io.ByteArrayWriter;
import br.net.woodstock.rockframework.security.Identity;
import br.net.woodstock.rockframework.security.sign.SignatureParameters;
import br.net.woodstock.rockframework.security.sign.SignerException;
import br.net.woodstock.rockframework.util.Assert;
public class XMLSigner extends AbstractSigner {
private static final String SIGNATURE_FACTORY = "DOM";
private static final String REFERENCE_URI = "";
private static final String SIGNATURE_ELEMENT = "Signature";
private DocumentBuilderFactory documentBuilderFactory;
private XMLSignatureFactory xmlSignatureFactory;
private SignedInfo signedInfo;
private KeyInfo keyInfo;
private SignatureParameters parameters;
public XMLSigner(final SignatureParameters parameters) {
super();
Assert.notNull(parameters, "parameters");
this.parameters = parameters;
this.xmlSignatureFactory = XMLSignatureFactory.getInstance(XMLSigner.SIGNATURE_FACTORY);
this.documentBuilderFactory = DocumentBuilderFactory.newInstance();
this.documentBuilderFactory.setNamespaceAware(true);
}
@Override
public byte[] sign(final byte[] data) {
Assert.notEmpty(data, "data");
try {
Identity[] identities = this.parameters.getIdentities();
byte[] currentData = data;
for (Identity identity : identities) {
PrivateKey privateKey = identity.getPrivateKey();
Certificate[] chain = identity.getChain();
X509Certificate certificate = (X509Certificate) chain[0];
PublicKey publicKey = certificate.getPublicKey();
DigestMethod digestMethod = this.xmlSignatureFactory.newDigestMethod(DigestMethod.SHA1, null);
List<Transform> transforms = Collections.singletonList(this.xmlSignatureFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null));
Reference reference = this.xmlSignatureFactory.newReference(XMLSigner.REFERENCE_URI, digestMethod, transforms, null, null);
CanonicalizationMethod canonicalizationMethod = this.xmlSignatureFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, (C14NMethodParameterSpec) null);
SignatureMethod signatureMethod = this.xmlSignatureFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null);
this.signedInfo = this.xmlSignatureFactory.newSignedInfo(canonicalizationMethod, signatureMethod, Collections.singletonList(reference));
KeyInfoFactory keyInfoFactory = this.xmlSignatureFactory.getKeyInfoFactory();
KeyValue keyValue = keyInfoFactory.newKeyValue(publicKey);
this.keyInfo = keyInfoFactory.newKeyInfo(Collections.singletonList(keyValue));
ByteArrayInputStream inputStream = new ByteArrayInputStream(currentData);
Document document = this.documentBuilderFactory.newDocumentBuilder().parse(inputStream);
DOMSignContext signContext = new DOMSignContext(privateKey, document.getDocumentElement());
XMLSignature signature = this.xmlSignatureFactory.newXMLSignature(this.signedInfo, this.keyInfo);
signature.sign(signContext);
ByteArrayWriter writer = new ByteArrayWriter();
StreamResult streamResult = new StreamResult(writer);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(new DOMSource(document), streamResult);
currentData = writer.toByteArray();
}
return currentData;
} catch (Exception e) {
throw new SignerException(e);
}
}
@Override
public boolean verify(final byte[] data, final byte[] signature) {
Assert.notEmpty(data, "data");
Assert.notEmpty(signature, "signature");
try {
Identity[] identities = this.parameters.getIdentities();
boolean valid = true;
for (Identity identity : identities) {
Certificate[] chain = identity.getChain();
X509Certificate certificate = (X509Certificate) chain[0];
PublicKey publicKey = certificate.getPublicKey();
ByteArrayInputStream inputStream = new ByteArrayInputStream(signature);
Document document = this.documentBuilderFactory.newDocumentBuilder().parse(inputStream);
NodeList nodeList = document.getElementsByTagNameNS(XMLSignature.XMLNS, XMLSigner.SIGNATURE_ELEMENT);
if ((nodeList != null) && (nodeList.getLength() > 0)) {
Node node = nodeList.item(0);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
DOMValidateContext domValidateContext = new DOMValidateContext(KeySelector.singletonKeySelector(publicKey), element);
XMLSignature xmlSignature = this.xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);
valid = xmlSignature.getSignatureValue().validate(domValidateContext);
if (!valid) {
break;
}
}
} else {
valid = false;
break;
}
}
return valid;
} catch (Exception e) {
throw new SignerException(e);
}
}
}