Friday, January 8, 2016

Accessing Secured SOAP WEBSERVICE in Java


When Endpoint Service is protected that SOAP request XML Header should have SSL Certificate agreed (between client and provider) and User Name/Password, then below approach can help to achieve in accessing highly secured service.

How to add Security tag and Digital Signature to SOAP request header:

JAX-WS (JAVA API for XML Webservice) provides the API to handle the SOAP request XML and add security tag with digital signature to SOAP request XML before sending it over in http to endpoint.

javax.xml.ws.Binding class has API to set custom Handler, and Binding object can be fetched from javax.xml.ws.BindinigProvider class object.


 BindingProvider bindingProvider = (BindingProvider)servicePort;
 List handlerChain = bindingProvider.getBinding().getHandlerChain();  
 handlerChain.add(new SOAPSecurityHandler();  
 bindingProvider.getBinding().setHandlerChain(handlerChain);  

SOAPSecurity handler class will be extending SOAPHandler interface and implementing the functionality for method handleMessage() method to intercept the SOAP message before sending it to Webservice provider.  

Example:

public class SOAPSecurityHandler implements javax.xml.ws.handler.soap.SOAPHandler {

public Set getHeaders() {
return Collections.emptySet();
}

public boolean handleMessage(SOAPMessageContext context) {
try {

if (Boolean.TRUE.equals(context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY))) {
// Add User Name Token to Security Context
addSecurityHeader(context);

// Add Digital Signature from Keystore.
addSignatureToMessageHeader(context);
}
} catch (Exception e) {
LOGGER.error("Error in SOAPSecurity Handler Handle Message", e);
}
return true;
}

public boolean handleFault(SOAPMessageContext context) {
return false;
}

public void close(MessageContext context) {
}



private void addSecurityHeader(SOAPMessageContext messageContext) throws SOAPException {
SOAPFactory sf = SOAPFactory.newInstance();
SOAPHeader header = messageContext.getMessage().getSOAPPart().getEnvelope().getHeader();
if (header == null) {
header = messageContext.getMessage().getSOAPPart().getEnvelope().addHeader();
}
Name securityName = sf.createName(WSSE_SECURITY_LNAME, WSSE_NS_PREFIX, WSSE_NS);
SOAPHeaderElement securityElem = header.addHeaderElement(securityName);
securityElem.setMustUnderstand(mustUnderstand);
Name usernameTokenName = sf.createName("UsernameToken", WSSE_NS_PREFIX, WSSE_NS);
SOAPElement usernameTokenMsgElem = sf.createElement(usernameTokenName);
Name usernameName = sf.createName("Username", WSSE_NS_PREFIX, WSSE_NS);
SOAPElement usernameMsgElem = sf.createElement(usernameName);
usernameMsgElem.addTextNode(username);
usernameTokenMsgElem.addChildElement(usernameMsgElem);
Name passwordName = sf.createName("Type", WSSE_NS_PREFIX, WSSE_NS);
SOAPElement passwordMsgElem = sf.createElement("Password", WSSE_NS_PREFIX, WSSE_NS);
passwordMsgElem.addAttribute(passwordName, PASSWORD_TEXT_TYPE);
passwordMsgElem.addTextNode(password);
usernameTokenMsgElem.addChildElement(passwordMsgElem);
securityElem.addChildElement(usernameTokenMsgElem);
}

/**
* Method to add Digital Signature to SOAPP Header

* @param messageContext
* @throws SOAPException
* @throws NoSuchAlgorithmException
* @throws InvalidAlgorithmParameterException
* @throws KeyStoreException
* @throws IOException
* @throws CertificateException
* @throws UnrecoverableEntryException
* @throws MarshalException
* @throws XMLSignatureException
*/
private void addSignatureToMessageHeader(SOAPMessageContext messageContext)
throws SOAPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyStoreException,
IOException, CertificateException, UnrecoverableEntryException, MarshalException, XMLSignatureException {
/// Add Signature
Source source = new DOMSource(messageContext.getMessage().getSOAPHeader());
Node root = null;
root = ((DOMSource) source).getNode();
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA256, null),
Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null,
null);
SignedInfo signedInfo = fac.newSignedInfo(
fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null),
fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
// Load the KeyStore and get the signing key and certificate.
KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(KEYSTORE_ALIAS,
new KeyStore.PasswordProtection(KEYSTORE_PWD.toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
// Create the KeyInfo containing the X509Data.
KeyInfoFactory kif2 = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
x509Content.add(cert.getSubjectX500Principal().getName());
x509Content.add(cert);
X509Data xd = kif2.newX509Data(x509Content);
KeyInfo keyInfo = kif2.newKeyInfo(Collections.singletonList(xd));
Element header = DOMUtils.getFirstChildElement(root);
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), (Node) header);
XMLSignature signature = fac.newXMLSignature(signedInfo, keyInfo);
signature.sign(dsc);
}
}

No comments: