Like SOAPUI I am able to sign the SOAP XML with Key-Identifier as X509SubjectKeyIdentifier
and I have compare the xml's mostly they are identical.
By using the following function WS_Security_signature_KeyIdentifier(SOAPMessage)
I am generating the xml as follows.
Even though XML's are same, I am getting Incorrect message signing
. I think their is a problem in the signing function. Please help me to solve the issue.
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>Incorrect message signing</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:tem2="http://tempuri.org/">
<soapenv:Header>
<tem:Add xmlns:tem="http://tempuri.org/">
<tem:intA>3</tem:intA>
<tem:intB>4</tem:intB>
</tem:Add>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ds:InclusiveNamespaces xmlns:ds="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soapenv tem tem2" />
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<ds:Reference URI="#MsgBody">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ds:InclusiveNamespaces xmlns:ds="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="tem tem2" />
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue> *** Digest Encode Value *** </ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue> *** Signature Value *** </ds:SignatureValue>
<ds:KeyInfo>
<wsse:SecurityTokenReference>
<wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier"> *** X509v3SubjectKeyIdentifier_CertEncoded ***</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</soapenv:Header>
<soapenv:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="MsgBody">
<tem2:Add>
<tem2:intA>3</tem2:intA>
<tem2:intB>4</tem2:intB>
</tem2:Add>
</soapenv:Body>
</soapenv:Envelope>
static final String SOAP_PROTOCOL = SOAPConstants.SOAP_1_1_PROTOCOL;
static String certEncodedID_KeyIdentifier_WsuID = "X509Token", timeStampID = "Timestamp", signedBodyID = "MsgBody";
static boolean inclusiveNamespaceCanonicalization = true, inclusiveNamespaceTransform = true, useTimeStamp = false;
static String transformPrefixListName = "v1 v11";
public static SOAPMessage WS_Security_signature_KeyIdentifier(SOAPMessage soapMsg) throws Exception {
// A new SOAPMessage object contains: •SOAPPart object •SOAPEnvelope object •SOAPBody object •SOAPHeader object
SOAPPart soapPart = soapMsg.getSOAPPart();
SOAPEnvelope soapEnv = soapPart.getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader(); // soapMessage.getSOAPHeader();
SOAPBody soapBody = soapEnv.getBody(); // soapMessage.getSOAPBody()
soapBody.addAttribute(soapEnv.createName("Id", MessageConstants.WSU_PREFIX, MessageConstants.WSU_NS), signedBodyID);
// Adding NameSpaces to the Envelope
soapEnv.addNamespaceDeclaration(MessageConstants.WSSE_PREFIX, MessageConstants.WSSE_NS);
soapEnv.addNamespaceDeclaration(MessageConstants.WSU_PREFIX, MessageConstants.WSU_NS);
soapEnv.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
soapEnv.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
// <wsse:Security> element adding to Header Part
SOAPElement securityElement = soapHeader.addChildElement("Security", MessageConstants.WSSE_PREFIX, MessageConstants.WSSE_NS);
//securityElement.addNamespaceDeclaration("wsu", WSU_NS);
/** SecurityTokenReference (Start) */
// Add signature element - <wsse:Security> <ds:Signature> <ds:KeyInfo> <wsse:SecurityTokenReference>
SOAPElement securityTokenReference = securityElement.addChildElement("SecurityTokenReference", MessageConstants.WSSE_PREFIX);
SOAPElement reference = securityTokenReference.addChildElement("KeyIdentifier", MessageConstants.WSSE_PREFIX);
reference.setAttributeNS(null, "EncodingType", MessageConstants.BASE64_ENCODING_NS);
reference.setAttributeNS(null, "ValueType", MessageConstants.X509SubjectKeyIdentifier_NS);
reference.addTextNode( getX509v3SubjectKeyIdentifier_CertEncoded(loadPublicKeyX509) );
/** SecurityTokenReference (End) */
// <ds:SignedInfo>
String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
//Digest method - <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
javax.xml.crypto.dsig.DigestMethod digestMethod = xmlSignatureFactory.newDigestMethod(digestMethodAlog, null);
ArrayList<Transform> transformList = new ArrayList<Transform>();
//Transform - <ds:Reference URI="#Body">
Transform envTransform = null;
if (inclusiveNamespaceTransform) {
List<String> prefixList = new ArrayList<String>();
String[] split = transformPrefixListName.split(" ");
for (String string : split) {
System.out.println("Transform InclusiveNamespaces Prefix:"+string);
prefixList.add(string); // How to add these prefix values dynamically excluding soapenv.
}
ExcC14NParameterSpec excC14NParameterSpec = new ExcC14NParameterSpec(prefixList);
envTransform = xmlSignatureFactory.newTransform(MessageConstants.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, excC14NParameterSpec);
transformList.add(envTransform);
} else {
envTransform = xmlSignatureFactory.newTransform(MessageConstants.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, (TransformParameterSpec) null);
transformList.add(envTransform);
}
//References <ds:Reference URI="#Body">
ArrayList<Reference> refList = new ArrayList<Reference>();
Reference refBody = xmlSignatureFactory.newReference("#"+signedBodyID, digestMethod, transformList, null, null);
refList.add(refBody);
if (useTimeStamp) {
Reference refTS = xmlSignatureFactory.newReference("#"+timeStampID, digestMethod, transformList, null, null);
refList.add(refTS);
}
// <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
javax.xml.crypto.dsig.CanonicalizationMethod cm;
if (inclusiveNamespaceCanonicalization) {
List<String> prefixList = new ArrayList<String>();
Iterator namespacePrefixes = soapEnv.getNamespacePrefixes();
for (Iterator iterator = namespacePrefixes; iterator.hasNext();) {
String prefix = (String) iterator.next();
System.out.println("CanonicalizationMethod InclusiveNamespaces Prefix:"+prefix);
prefixList.add(prefix);
}
ExcC14NParameterSpec excC14NParameterSpec = new ExcC14NParameterSpec(prefixList);
cm = xmlSignatureFactory.newCanonicalizationMethod(canonicalizationMethod_Algo, excC14NParameterSpec);
} else {
cm = xmlSignatureFactory.newCanonicalizationMethod(canonicalizationMethod_Algo, (C14NMethodParameterSpec) null);
}
//javax.xml.crypto.dsig.CanonicalizationMethod cm = xmlSignatureFactory.newCanonicalizationMethod(canonicalizationMethodAlog_INCLUSIVE, (C14NMethodParameterSpec) null);
javax.xml.crypto.dsig.SignatureMethod sm = xmlSignatureFactory.newSignatureMethod(signatureMethod_Algo, null);
SignedInfo signedInfo = xmlSignatureFactory.newSignedInfo(cm, sm, refList);
DOMSignContext signContext = new DOMSignContext(privateKey, securityElement);
signContext.setDefaultNamespacePrefix(MessageConstants.DSIG_PREFIX);
signContext.putNamespacePrefix(MessageConstants.DSIG_NS, MessageConstants.DSIG_PREFIX);
signContext.putNamespacePrefix(MessageConstants.WSU_NS, MessageConstants.WSU_PREFIX);
signContext.setIdAttributeNS(soapBody, MessageConstants.WSU_NS, "Id");
if (useTimeStamp ) {
SOAPElement timeStamp = getTimeStamp(soapEnv, securityElement);
signContext.setIdAttributeNS(timeStamp, MessageConstants.WSU_NS, "Id");
}
KeyInfoFactory keyFactory = KeyInfoFactory.getInstance();
DOMStructure domKeyInfo = new DOMStructure(securityTokenReference);
javax.xml.crypto.dsig.keyinfo.KeyInfo keyInfo = keyFactory.newKeyInfo(java.util.Collections.singletonList(domKeyInfo));
javax.xml.crypto.dsig.XMLSignature signature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo);
signContext.setBaseURI("");
signature.sign(signContext);
return soapMsg;
}
public static String getX509v3SubjectKeyIdentifier_CertEncoded(X509Certificate cert) throws IOException, CertificateEncodingException {
// https://github.com/mulderbaba/webservices-osgi/blob/7090b58bd4cdf5fab4af14d54cb20bb45c074de2/com/sun/xml/wss/core/reference/X509SubjectKeyIdentifier.java#L108
String SUBJECT_KEY_IDENTIFIER_OID = "2.5.29.14", Authority_KEY_IDENTIFIER_OID = "2.5.29.35";
byte[] subjectKeyIdentifier = cert.getExtensionValue(SUBJECT_KEY_IDENTIFIER_OID); // org.bouncycastle.asn1.x509.Extension.subjectKeyIdentifier.getId()
System.out.println("X509SubjectKeyIdentifier ExtensionValue #"+subjectKeyIdentifier);
if (subjectKeyIdentifier == null) {
getCertInfo(cert);
System.err.println("PKIX Certificate: Certificate is Self-Signed (or) CAs MUST mark this extension as non-critical. https://tools.ietf.org/html/rfc5280#page-28");
// https://stackoverflow.com/a/31183447/5081877
byte[] extensionValue = cert.getExtensionValue(Authority_KEY_IDENTIFIER_OID);
System.out.println("Authority Key Identifier ExtensionValue #"+extensionValue);
//byte[] octets = DEROctetString.getInstance(extensionValue).getOctets();
//AuthorityKeyIdentifier authorityKeyIdentifier23 = AuthorityKeyIdentifier.getInstance(octets);
//byte[] keyIdentifier = authorityKeyIdentifier23.getKeyIdentifier();
throw new NullPointerException("SubjectKeyIdentifier OBJECT IDENTIFIER Value is Empty.");
}
sun.security.util.DerValue derVal = new sun.security.util.DerValue(
new sun.security.util.DerInputStream(subjectKeyIdentifier).getOctetString());
sun.security.x509.KeyIdentifier keyId = new sun.security.x509.KeyIdentifier(derVal.getOctetString());
byte[] keyIDF = keyId.getIdentifier();
String encodeToString = java.util.Base64.getEncoder().encodeToString(keyIDF);
System.out.println("Subject Key Identifier Encoded Val: "+encodeToString );
return encodeToString;
}
Any help will be appreciated to solve my issue. Full code is available at this Github Gist page.
Input Soap XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:temp="http://tempuri.org/"
xmlns:temp2="http://tempuri.org/">
<soapenv:Header>
<temp:Add>
<temp:intA>3</temp:intA>
<temp:intB>4</temp:intB>
</temp:Add>
</soapenv:Header>
<soapenv:Body>
<temp2:Add>
<temp2:intA>3</temp2:intA>
<temp2:intB>4</temp2:intB>
</temp2:Add>
</soapenv:Body>
</soapenv:Envelope>
SOAP XML file to SOAPMessage java Object
SOAPMessage soapMessageEnv = getSoapMessage_File(fileLocation_SoapTemplate);
public static SOAPMessage getSoapMessage_File(String sopaEnvelopFile) throws Exception {
Document doc = getDocument(sopaEnvelopFile, false); // SOAP MSG removing comment elements
String docStr = toStringDocument(doc);
//docStr=docStr.replaceAll("\\<\\?xml(.+?)\\?\\>", "").trim();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(docStr.getBytes());
MimeHeaders mimeHeaders = new MimeHeaders();
// SOAPMessage message = MessageFactory.newInstance().createMessage(null, fileInputStream);
SOAPMessage message = MessageFactory.newInstance(SOAP_PROTOCOL).createMessage(mimeHeaders, byteArrayInputStream);
return message;
}