SOAP Message Sign with Private Key
Sign using bouncycastle library
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.components.crypto.Merlin;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.openssl.PEMException;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import javax.xml.crypto.dsig.Reference;
import javax.xml.soap.*;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.nio.file.Files;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
// Function to set error response
void setErrorResponse(String code, String desc) {
requestErrorMessageToTargetAPI = """<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<soap:Fault>
<faultCode>${code}</faultCode>
<faultString>${desc}</faultString>
</soap:Fault>
</soap:Body>
</soap:Envelope>""";
statusCodeToTargetAPI = 401;
}
try {
PrivateKey privateKey = environment_privateKeyMap.get("Server Private Key");
if ( privateKey == null) {
setErrorResponse("ERR-01", "Server private key was not found."!");
return;
}
X509Certificate certificate = environment_certificateMap.get("Server Certificate");
if ( certificate == null) {
setErrorResponse("ERR-02", "Server certificate was not found."!");
return;
}
String username="alias";
String password="123456";
Properties cryptoProps = new Properties();
cryptoProps.put("org.apache.ws.security.crypto.provider", "org.apache.ws.security.components.crypto.Merlin");
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, null); // Initialize the keystore
keyStore.setKeyEntry(username, privateKey, password.toCharArray(), new X509Certificate[]{certificate});
Merlin crypto = (Merlin) CryptoFactory.getInstance(cryptoProps);
crypto.setKeyStore(keyStore);
SOAPMessage soapMessage = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL)
.createMessage(null, new ByteArrayInputStream(responseBodyTextToClient.getBytes()));
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration("ws24", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
SOAPHeader header = envelope.getHeader();
if (header == null) {
header = envelope.addHeader();
}
WSSecHeader secHeader = new WSSecHeader();
secHeader.setMustUnderstand(true);
secHeader.insertSecurityHeader(soapPart);
org.apache.xml.security.Init.init();
/* optional
WSSecTimestamp timestamp = new WSSecTimestamp();
timestamp.setTimeToLive(600);
timestamp.build(soapPart, secHeader); */
WSSConfig wssConfig = new WSSConfig();
wssConfig.setWsiBSPCompliant(false);
WSSecSignature sign = new WSSecSignature(wssConfig);
sign.setUserInfo(username, password);
sign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
sign.setSigCanonicalization("http://www.w3.org/2001/10/xml-exc-c14n#");
sign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
sign.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
sign.setUseSingleCertificate(true);
sign.prepare(soapPart, crypto, secHeader);
Vector<WSEncryptionPart> signParts = new Vector<>();
signParts.add(new WSEncryptionPart("Body", SOAPConstants.URI_NS_SOAP_ENVELOPE, "Content"));
List<Reference> referenceList = sign.addReferencesToSign(signParts, secHeader);
sign.computeSignature(referenceList, true, null);
StringWriter sw = new StringWriter();
TransformerFactory.newInstance().newTransformer().transform(new DOMSource(soapPart), new StreamResult(sw));
requestBodyTextToTargetAPI = sw.toString();
} catch (Exception e) {
setErrorResponse("ERR-03", "Error during sign operation: " + e.getMessage());
return;
}
GROOVY
Sign using apache library
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.StringWriter;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.UUID;
final String SOAP_NS = "http://schemas.xmlsoap.org/soap/envelope/";
final String WSSE_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
final String WSU_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
final String DS_NS = "http://www.w3.org/2000/09/xmldsig#";
final String EC_NS = "http://www.w3.org/2001/10/xml-exc-c14n#";
// Function to set error response
void setErrorResponse(String code, String desc) {
requestErrorMessageToTargetAPI = """<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<soap:Fault>
<faultCode>${code}</faultCode>
<faultString>${desc}</faultString>
</soap:Fault>
</soap:Body>
</soap:Envelope>""";
statusCodeToTargetAPI = 401;
}
try {
PrivateKey privateKey = environment_privateKeyMap.get("Server Private Key");
if ( privateKey == null) {
setErrorResponse("ERR-01", "Server private key was not found."!");
return;
}
X509Certificate certificate = environment_certificateMap.get("Server Certificate");
if ( certificate == null) {
setErrorResponse("ERR-02", "Server certificate was not found."!");
return;
}
org.apache.xml.security.Init.init();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream( requestBodyTextFromClient .getBytes("UTF-8")));
// Add ID attribute to body element
NodeList bodyList = doc.getElementsByTagNameNS(SOAP_NS, "Body");
Element bodyElement = (Element) bodyList.item(0);
String bodyId = "id-" + UUID.randomUUID().toString();
bodyElement.setAttributeNS(WSU_NS, "wsu:Id", bodyId);
bodyElement.setIdAttributeNS(WSU_NS, "Id", true);
bodyElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:wsu", WSU_NS);
XMLSignature signature = new XMLSignature(doc, "",
XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256,
Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
Element soapHeader = getOrCreateSoapHeader(doc);
Element wsseHeader = doc.createElementNS(WSSE_NS, "wsse:Security");
wsseHeader.setAttributeNS(SOAP_NS, "soapenv:mustUnderstand", "1");
wsseHeader.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:wsse", WSSE_NS);
wsseHeader.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:wsu", WSU_NS);
wsseHeader.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ds", DS_NS);
wsseHeader.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ec", EC_NS);
soapHeader.appendChild(wsseHeader);
wsseHeader.appendChild(signature.getElement());
Transforms transforms = new Transforms(doc);
transforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
//Add Body as reference
signature.addDocument("#" + bodyId, transforms, MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256);
Element bst = doc.createElementNS(WSSE_NS, "wsse:BinarySecurityToken");
bst.setAttributeNS(WSU_NS, "wsu:Id", "X509-" + UUID.randomUUID().toString());
bst.setAttributeNS(null, "EncodingType",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
bst.setAttributeNS(null, "ValueType",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
bst.setTextContent(Base64.getEncoder().encodeToString(cert.getEncoded()));
wsseHeader.insertBefore(bst, signature.getElement());
signature.addKeyInfo(cert);
signature.sign(privateKey);
final StringWriter sw = new StringWriter();
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.transform(new DOMSource(doc), new StreamResult(sw));
requestBodyTextToTargetAPI = sw.toString();
} catch (Exception e) {
setErrorResponse("ERR-03", "Error during sign operation: " + e.getMessage());
return;
}
GROOVY