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/open?id=1qlhF9EGT-IH1xzBrZj4uC1jkkqrRSZn8
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
Hi, you simply saved my day! was strugling with a bank file signing, and used a good old signing java we created years back. It was not giving the expected SignatureValue - but your code did! Thanks a lot!
Hello,
Do you know if it is possible to retrieve Public Key from keystore and use it for fields enryption in UDF in the mapping? I have some issues with retrieving Public Key from loaded certificate in keystore.
Thanks for this post.
I am having a problem when executing the JavaMapping, it returns the following error:
does not implement the required interface com.sap.aii.mapping.api.StreamTransformation or does not enhance the class com.sap.aii.mapping.api.AbstractTransformation
It has been compiled with the versions that the PO Server had.
Would you know the reason for the error, or how it can be solved?
Thanks and best regards.
Hello, someone guides me how to perform the same operation, but now decrypting with x509.
thank you very much