Last active
April 7, 2016 02:04
-
-
Save BHSDuncan/55ebe8b37fd01db1e05f to your computer and use it in GitHub Desktop.
Class for generating an XML Digital Signature according to the W3C spec (https://www.w3.org/TR/xmldsig-core/). Note that the only part of this that requires JAXB is the output, which can then be assigned to the appropriate location in a SOAP envelope/message.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.whatever.service; | |
import java.io.ByteArrayInputStream; | |
import java.io.ByteArrayOutputStream; | |
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.math.BigInteger; | |
import java.security.InvalidAlgorithmParameterException; | |
import java.security.KeyStore; | |
import java.security.KeyStoreException; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.Provider; | |
import java.security.UnrecoverableEntryException; | |
import java.security.cert.CertificateException; | |
import java.security.cert.X509Certificate; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.List; | |
import javax.xml.bind.JAXBContext; | |
import javax.xml.bind.JAXBException; | |
import javax.xml.bind.Marshaller; | |
import javax.xml.bind.Unmarshaller; | |
import javax.xml.crypto.MarshalException; | |
import javax.xml.crypto.XMLStructure; | |
import javax.xml.crypto.dsig.XMLSignature; | |
import javax.xml.crypto.dsig.XMLSignatureException; | |
import javax.xml.crypto.dsig.XMLSignatureFactory; | |
import javax.xml.crypto.dsig.dom.DOMSignContext; | |
import javax.xml.crypto.dsig.keyinfo.X509IssuerSerial; | |
import javax.xml.crypto.dsig.spec.TransformParameterSpec; | |
import javax.xml.transform.Transformer; | |
import javax.xml.transform.TransformerConfigurationException; | |
import javax.xml.transform.TransformerException; | |
import javax.xml.transform.TransformerFactory; | |
import javax.xml.transform.dom.DOMResult; | |
import javax.xml.transform.dom.DOMSource; | |
import javax.xml.transform.stream.StreamResult; | |
import org.w3._2000._09.xmldsig_.Signature; // NOTE: This package is auto-generated by JAXB! | |
import org.w3c.dom.Document; | |
import org.w3c.dom.Element; | |
import org.xmlsoap.schemas.soap.envelope.Envelope; | |
public class XMLDSigGenerator { | |
public Signature generateSignature(Envelope envelope, String idForReference) { | |
DOMResult domResult = new DOMResult(); | |
try { | |
JAXBContext context = JAXBContext.newInstance(Envelope.class); | |
Marshaller marshaller = context.createMarshaller(); | |
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); | |
marshaller.marshal(envelope, domResult); | |
} catch (JAXBException e) { | |
// log here and bail out | |
} | |
String providerName = System.getProperty("jsr105Provider", | |
"org.jcp.xml.dsig.internal.dom.XMLDSigRI"); | |
XMLSignatureFactory fac = null; | |
try { | |
fac = XMLSignatureFactory.getInstance("DOM", | |
(Provider) Class.forName(providerName).newInstance()); | |
} catch (InstantiationException e2) { | |
// log here and bail out | |
} catch (IllegalAccessException e2) { | |
// log here and bail out | |
} catch (ClassNotFoundException e2) { | |
// log here and bail out | |
} | |
javax.xml.crypto.dsig.Reference reference = null; | |
try { | |
String refId = idForReference == null ? "" : idForReference; | |
reference = fac.newReference(refId, | |
fac.newDigestMethod(javax.xml.crypto.dsig.DigestMethod.SHA1, null), | |
Collections.singletonList | |
(fac.newTransform ("http://www.w3.org/2001/10/xml-exc-c14n#", | |
(TransformParameterSpec) null)), null, null); | |
} catch (NoSuchAlgorithmException e2) { | |
// log here and bail out | |
} catch (InvalidAlgorithmParameterException e2) { | |
// log here and bail out | |
} | |
javax.xml.crypto.dsig.SignedInfo signedInfo = null; | |
try { | |
signedInfo = fac.newSignedInfo(fac.newCanonicalizationMethod( | |
javax.xml.crypto.dsig.CanonicalizationMethod.EXCLUSIVE, (XMLStructure) null), fac | |
.newSignatureMethod(javax.xml.crypto.dsig.SignatureMethod.RSA_SHA1, null), | |
Collections.singletonList(reference)); | |
} catch (NoSuchAlgorithmException e2) { | |
// log here and bail out | |
} catch (InvalidAlgorithmParameterException e2) { | |
// log here and bail out | |
} | |
javax.xml.crypto.dsig.keyinfo.KeyInfoFactory keyInfoFactory = fac.getKeyInfoFactory(); | |
List x509Content = new ArrayList(); | |
KeyStore keyStore = null; | |
KeyStore.PrivateKeyEntry keyEntry = null; | |
X509Certificate cert = null; | |
try { | |
keyStore = KeyStore.getInstance("JKS"); | |
keyStore.load(new FileInputStream(System.getProperty("JKS_FILE")), System.getProperty("JKS_PWD").toCharArray()); | |
keyEntry = | |
(KeyStore.PrivateKeyEntry) keyStore.getEntry | |
(System.getProperty("JKS_CERT"), new KeyStore.PasswordProtection(System.getProperty("JKS_CPWD").toCharArray())); | |
cert = (X509Certificate) keyEntry.getCertificate(); | |
} catch (KeyStoreException e3) { | |
// log here and bail out | |
} catch (NoSuchAlgorithmException e) { | |
// log here and bail out | |
} catch (CertificateException e) { | |
// log here and bail out | |
} catch (FileNotFoundException e) { | |
// log here and bail out | |
} catch (IOException e) { | |
// log here and bail out | |
} catch (UnrecoverableEntryException e) { | |
// log here and bail out | |
} | |
x509Content.add(cert.getIssuerX500Principal().getName()); | |
x509Content.add(cert); | |
String dn = cert.getIssuerDN().toString(); | |
BigInteger sn = cert.getSerialNumber(); | |
X509IssuerSerial xd = keyInfoFactory.newX509IssuerSerial(dn, sn); | |
javax.xml.crypto.dsig.keyinfo.KeyInfo keyInfo = keyInfoFactory.newKeyInfo(Collections.singletonList(keyInfoFactory.newX509Data(Collections.singletonList(xd)))); | |
Document doc = (Document) domResult.getNode(); | |
// this block is necessary as the Document class doesn't seem to properly register the id attribute of any elements after being marshalled by JAXB; | |
// this is only important if we are creating a Reference element that references a specific section of the XML being signed | |
// note that the element referenced is entirely dependent upon what we're actually trying to reference so you may need to change the first line | |
// if you're referening another element (in our case, it's the SOAP "Body" element in our envelope) | |
// | |
// TODO: Possibly modify the method to have an XPath parameter that will be used in place of the hardcoded DOM traversal below | |
Element el = (Element)doc.getFirstChild().getFirstChild().getNextSibling(); | |
el.setIdAttribute("id", true); | |
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), | |
doc.getDocumentElement()); | |
XMLSignature xmlSignature = fac.newXMLSignature(signedInfo, keyInfo); | |
try { | |
xmlSignature.sign(dsc); | |
} catch (MarshalException e2) { | |
// log here and bail out | |
} catch (XMLSignatureException e2) { | |
// log here and bail out | |
} | |
// we need to get at the signed result and move it into the appropriate section of the SOAP message, so let's grab that section | |
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |
TransformerFactory tf = TransformerFactory.newInstance(); | |
Transformer trans = null; | |
try { | |
trans = tf.newTransformer(); | |
trans.transform(new DOMSource(doc.getDocumentElement().getFirstChild().getNextSibling().getNextSibling()), new StreamResult(os)); | |
} catch (TransformerConfigurationException e1) { | |
// log here and bail out | |
} catch (TransformerException e) { | |
// log here and bail out | |
} | |
// time to unmarshal that section into the appropriate place in the SOAP message | |
// ******* | |
// IMPORTANT NOTE: Be sure to suppress the name space prefixes for the Signature block of XML. Since we're using JAXB, the best way to do this | |
// is to make sure that the Signature schema has been generated into its own package and then set the namespace prefix used to an empty string in | |
// package-info.java. | |
// ******* | |
ByteArrayInputStream bis = new ByteArrayInputStream(os.toByteArray()); | |
String s = new String(os.toByteArray()); | |
Signature signature = null; | |
try { | |
JAXBContext context = JAXBContext.newInstance(Signature.class); | |
Unmarshaller unmarshaller = context.createUnmarshaller(); | |
signature = (Signature) unmarshaller.unmarshal(bis); | |
} catch (JAXBException e) { | |
// log here and bail out | |
} | |
return signature; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment