Skip to content

Instantly share code, notes, and snippets.

@as1an
Created November 22, 2022 20:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save as1an/7318f1a69d838c4aac15486a238c442c to your computer and use it in GitHub Desktop.
Save as1an/7318f1a69d838c4aac15486a238c442c to your computer and use it in GitHub Desktop.
Simple enveloped-signature via santuario
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.xml.security.encryption.XMLCipherParameters;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.Constants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import kz.gov.pki.kalkan.asn1.knca.KNCAObjectIdentifiers;
import kz.gov.pki.kalkan.asn1.pkcs.PKCSObjectIdentifiers;
import kz.gov.pki.kalkan.jce.provider.KalkanProvider;
import kz.gov.pki.kalkan.xmldsig.KncaXS;
public class SimpleEnvelopedXml {
public static void main(String[] args) {
String xmlString = "<root>\n" + "<person id=\"someid\">\n" + "<name>Абай Құнанбайұлы</name>\n"
+ "<iin>123456789012</iin>\n" + "</person>\n" + "</root>\n";
String path = "/tmp/keystore.p12";
char[] password = "123456".toCharArray();
Provider provider = new KalkanProvider();
Security.addProvider(provider);
KncaXS.loadXMLSecurity();
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12", provider);
keyStore.load(new FileInputStream(path), password);
Enumeration<String> aliases = keyStore.aliases();
String alias = aliases.nextElement();
PrivateKey key = (PrivateKey) keyStore.getKey(alias, password);
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
SimpleEnvelopedXml sample = new SimpleEnvelopedXml();
String signedXml = sample.createXmlSignature(key, cert, xmlString);
System.out.println(signedXml);
boolean result = sample.verifyXml(signedXml);
System.out.println("verified: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
public Document getDocument(String xml) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setNamespaceAware(true);
DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
return documentBuilder.parse(new ByteArrayInputStream(xml.getBytes()));
}
public String createXmlSignature(PrivateKey key, X509Certificate cert, String xmlSource)
throws ParserConfigurationException, SAXException, IOException, XMLSecurityException, TransformerException {
Document document = getDocument(xmlSource);
String signMethod;
String digestMethod;
String sigAlgOid = cert.getSigAlgOID();
if (sigAlgOid.equals(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId())) {
signMethod = Constants.MoreAlgorithmsSpecNS + "rsa-sha256";
digestMethod = XMLCipherParameters.SHA256;
} else if (sigAlgOid.equals(KNCAObjectIdentifiers.gost34311_95_with_gost34310_2004.getId())) {
signMethod = Constants.MoreAlgorithmsSpecNS + "gost34310-gost34311";
digestMethod = Constants.MoreAlgorithmsSpecNS + "gost34311";
} else if (sigAlgOid.equals(KNCAObjectIdentifiers.gost3411_2015_with_gost3410_2015_512.getId())) {
signMethod = "urn:ietf:params:xml:ns:pkigovkz:xmlsec:algorithms:gostr34102015-gostr34112015-512";
digestMethod = "urn:ietf:params:xml:ns:pkigovkz:xmlsec:algorithms:gostr34112015-512";
} else {
throw new IllegalArgumentException("Incorrect algorithm: " + sigAlgOid);
}
Transforms transforms = new Transforms(document);
transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
XMLSignature xmlSignature = new XMLSignature(document, "", signMethod);
document.getFirstChild().appendChild(xmlSignature.getElement());
xmlSignature.addDocument("", transforms, digestMethod);
xmlSignature.addKeyInfo(cert);
xmlSignature.sign(key);
return getXmlString(document);
}
private String getXmlString(Document document) throws TransformerException, IOException {
try (StringWriter os = new StringWriter()) {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(document), new StreamResult(os));
return os.toString();
}
}
public boolean verifyXml(String xmlString) throws ParserConfigurationException, SAXException, IOException,
XMLSignatureException, XMLSecurityException {
Document doc = getDocument(xmlString);
Element sigElement = null;
Element rootEl = (Element) doc.getFirstChild();
NodeList list = rootEl.getElementsByTagName("ds:Signature");
if (list.getLength() == 0) {
throw new IllegalStateException("ds:Signature not found");
}
Node sigNode = list.item(0);
sigElement = (Element) sigNode;
XMLSignature signature = new XMLSignature(sigElement, "");
KeyInfo ki = signature.getKeyInfo();
X509Certificate cert = ki.getX509Certificate();
if (cert == null) {
throw new IllegalStateException("Certificate not found");
}
return signature.checkSignatureValue(cert);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment