package fi.csc.emrex.smp;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import javax.xml.bind.DatatypeConverter;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Iterator;
/**
* Created by marko.hollanti on 07/10/15.
*/
@Slf4j
@Setter
@Component
public class SignatureVerifier {
private String certificate;
public boolean verifySignatureWithDecodedData(String certificate, String encodedData, Charset charset) throws Exception {
// Instantiate the document to be signed.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(IOUtils.toInputStream(encodedData, charset));
return doVerifySignature(certificate, doc);
}
public boolean verifySignature(String certificate, String data) throws Exception {
// Instantiate the document to be signed.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
InputStream is = new ByteArrayInputStream(GzipUtil.gzipDecompressBytes(DatatypeConverter.parseBase64Binary(data))); // StandardCharsets.ISO_8859_1
Document doc = dbf.newDocumentBuilder().parse(is);
return doVerifySignature(certificate, doc);
}
private boolean doVerifySignature(String certificate, Document doc) throws Exception {
// Create a DOM XMLSignatureFactory that will be used to generate the enveloped signature.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
// Find Signature element.
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("Cannot find Signature element");
}
X509Certificate cert = getCertificate(certificate);
PublicKey pubKey = cert.getPublicKey();
DOMValidateContext valContext = new DOMValidateContext(pubKey, nl.item(0));
// Unmarshal the XMLSignature.
XMLSignature signature = fac.unmarshalXMLSignature(valContext);
// Validate the XMLSignature.
boolean coreValidity = signature.validate(valContext);
// Check core validation status.
if (coreValidity == false) {
log.error("Signature failed core validation");
boolean sv = signature.getSignatureValue().validate(valContext);
log.error("signature validation status: " + sv);
if (sv == false) {
// Check the validation status of each Reference.
Iterator<?> i = signature.getSignedInfo().getReferences().iterator();
for (int j = 0; i.hasNext(); j++) {
boolean refValid = ((Reference) i.next()).validate(valContext);
log.info("ref[" + j + "] validity status: " + refValid);
}
}
}
return coreValidity;
}
private static X509Certificate getCertificate(String certString) throws IOException, GeneralSecurityException {
InputStream is = new ByteArrayInputStream(certString.getBytes());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
is.close();
return cert;
}
}