Skip to Content
Technical Articles

SAP PI/PO XML X509 signature by certificate

The Challenge

A few weeks ago, my team was challenged to send a soap message with digital signature (X509) to receiver side using a digital certificate (with our private key) existent in Java NetWeaver (SAP PI NWA).

I thought this kind of signature could be so complicate but after I researched at other blog posts and in another doubts from others SCN professionals I could create a simple way using java mapping to do this.

 

First steps

We need import the digital certificate X509 with your private key imported in NWA in the respective Key view and Key Entry. (this can be bought by your company for example).

After we receive this file, we need import this (or our basis team) in the NWA Certificate repository. We can use the blog post below to do this step.

https://blogs.sap.com/2013/06/17/adding-certificates-to-pi/

After we finish the import certificate step, we need create an operation mapping in our repository with 3 parameters to be used in our java mapping. In my case I create the parameters with the names below:

  • KeyView: the name of View where we create to save our entry to certificate with private key inside the NWA.
  • key Entry: the name of certificate what we imported inside the key view
  • password: if necessary, we can inform the password

 

This Operation Mapping will be the object where we will import the java program what we will create in the SAP PI Repository (if the java mapping is a new thing for you follow the link https://help.sap.com/viewer/0b9668e854374d8fa3fc8ec327ff3693/7.3.19/en-US/4bf40fddc0c33de4e10000000a42189e.html )

Second step

We need acquire the jar lib files what we need use in our java class mapping program. You can download in the link below.

 

LIB: https://drive.google.com/drive/folders/1VJ6myaNLRZHhkfaWDZSK7fMIIwygBy5e?usp=sharing

 

Third Step

Copy the source code below (please, attention in the java class name, if you create your class with the different name, change for the name what you create)

lass name, if you create your class with the diferent name, change for the name what you create)

 

import java.io.IOException;
import java.io.OutputStream;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
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.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
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.w3c.dom.Document;
import org.xml.sax.SAXException;
import javax.xml.crypto.dsig.*;
import com.sap.aii.mapping.api.AbstractTrace;
import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;
import com.sap.security.api.ssf.ISsfProfile;
import com.sap.aii.af.service.resource.*;
import com.sap.aii.security.lib.*;


public class XmlDigitalX509Signature extends AbstractTransformation {
	static AbstractTrace log = null;
	
	@Override
	public void transform(TransformationInput inptS, TransformationOutput outS) throws StreamTransformationException {
		log = this.getTrace();
		log.addDebugMessage("@@@@ START JVN");
		
		String keyView = inptS.getInputParameters().getString("keyView");
		String keyEntry = inptS.getInputParameters().getString("keyEntry");
		String passwd = inptS.getInputParameters().getString("password");
		
		try {
			
			ISsfProfile isP = getCertProfile(keyView,keyEntry,passwd);
			

			XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
			
			// Create a Reference to the enveloped document (in this case,
			// you are signing the whole document, so a URI of "" signifies
			// that, and also specify the SHA1 digest algorithm and
			// the ENVELOPED Transform.
			Reference ref = fac.newReference
			 ("", fac.newDigestMethod(DigestMethod.SHA1, null),
			  Collections.singletonList
			   (fac.newTransform
			    (Transform.ENVELOPED, (TransformParameterSpec) null)),
			     null, null);
			
			// Create the SignedInfo.
			SignedInfo si = fac.newSignedInfo
			 (fac.newCanonicalizationMethod
			  (CanonicalizationMethod.INCLUSIVE,
			   (C14NMethodParameterSpec) null),
			    fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
			     Collections.singletonList(ref));
			
			// Create the KeyInfo containing the X509Data.
			KeyInfoFactory kif = fac.getKeyInfoFactory();
			List x509Content = new ArrayList();
			x509Content.add(isP.getCertificate().getSubjectX500Principal().getName());
			x509Content.add(isP.getCertificate());
			X509Data xd = kif.newX509Data(x509Content);
			KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
			
			
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			dbf.setNamespaceAware(true);
			Document doc = dbf.newDocumentBuilder().parse(inptS.getInputPayload().getInputStream());
			DOMSignContext dsc = new DOMSignContext
				    (getCertProfile(keyView,keyEntry,passwd).getPrivateKey(), doc.getDocumentElement());
			
			
			// Create the XMLSignature, but don't sign it yet.
			XMLSignature signature = fac.newXMLSignature(si, ki);
			
			signature.sign(dsc);
			
			
			// Output the resulting document.
			OutputStream os = outS.getOutputPayload().getOutputStream();
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer trans = tf.newTransformer();
			trans.transform(new DOMSource(doc), new StreamResult(os));
			
			//data.writeTo(outS.getOutputPayload().getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();

		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MarshalException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (XMLSignatureException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	private static ISsfProfile getCertProfile(String alias, String entry,String pwd) throws StreamTransformationException {

		KeyStoreManager managerPriviliged;

		try {

			managerPriviliged = (KeyStoreManager) SAPSecurityResources.getInstance().getKeyStoreManager(com.sap.aii.security.lib.PermissionMode.SYSTEM_LEVEL);

		} catch (KeyStoreException e) {
			throw new StreamTransformationException("SAPSecurityResources", e);
		}

		KeyStore keyStore;

		try {
			keyStore = managerPriviliged.getKeyStore(alias);

		} catch (KeyStoreException e) {
			throw new StreamTransformationException("managerPriviliged.getKeyStore " + alias, e);
		}

		ISsfProfile profile = null;
		try {
			
			profile = (ISsfProfile)managerPriviliged.getISsfProfile(keyStore, entry, null);

		} catch (KeyStoreException e) {
			throw new StreamTransformationException(
					"Failed to load SsfProfileKeyStore " + alias + " " + entry, e);
		}
		return profile;
	}

}

 

Last Steps

Export your jar file and import in ESR at Imported Archive Object and fill the name of your java mapping class at the Operation mapping what you were create.

Finish the configuration and activate all the objects and do the tests.

I expect this simple tutorial in this blog could help a lot of persons what have a challenge in his career like me.

 

See you in the next blog post

Be the first to leave a comment
You must be Logged on to comment or reply to a post.