on 09-03-2015 8:38 AM
Hello Experts,
I am working on the Integration of SAP PI system with the Bank server. And we are transmitting Payment files over HTTPs to bank server. Bank wants the message to be signed using SHA-1 Algorithm and encode it using Base64 and then send it to their web page.
As they are using HTTP, i cannot use Web-Services Security, as this works only with SOAP envelope and they doesnt seem to accept PGP signing as localejbs/PGPEncryption adds the header field as "BEGIN PGP REQUEST" and also it encodes the message while signing, hence the services at their end is not able to verify the signed message and also they dont seem to accept any other format keys, other than X509 certificates. I was using ASCII armored keys, but they do not accept that.
And now i understand that, format of the signed message they are looking for is Bouncy Castle. Can anybody provide me the java code to do this?
I also tried SSF method for signing and they even dont accept that.
Request your urgent help on this as the Go-live date is very near
Regards,
Nitin Deshpande
Hi,
I found 2 ways to resolve your problem, using "Bouncycastle" and "SSF".
Bouncycastle:
You have to download the following libraries and leave them in PI ( "...\jre\lib\ext" and restart the server 😞
bcprov-jdk15on-152.jar
bcprov-ext-jdk15on-152.jar
bcpkix-jdk15on-152.jar
First read this How to generate PKCS#7 signatures in Java | My Developed World then see the code examples:
CMSSignedDataGenerator (Bouncy Castle Library 1.52 API Specification) (change "gen.generate(msg, false)" for "gen.generate(msg, true)" and you will have an attached signature)
CMSSignedDataParser (Bouncy Castle Library 1.52 API Specification)
The problem is that you have to restart the server and depend on the libraries Bouncycastle.
Code for Bouncycastle:
You'll need the following libraries:
bcpkix-jdk15on-152.jar
bcprov-ext-jdk15on-152.jar
bcprov-jdk15on-152.jar
sap.com~tc~je~keystore_api~API.jar
sap.com~tc~logging~java~impl.jar
sap.com~tc~sec~ssf.jar
sap.com~tc~sec~ume~core~impl.jar
package com.zmc.utils;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.CreateException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import com.sap.aii.af.lib.mp.module.Module;
import com.sap.aii.af.lib.mp.module.ModuleContext;
import com.sap.aii.af.lib.mp.module.ModuleData;
import com.sap.aii.af.lib.mp.module.ModuleException;
import com.sap.aii.af.service.resource.SAPSecurityResources;
import com.sap.aii.security.lib.KeyStoreManager;
import com.sap.aii.security.lib.PermissionMode;
import com.sap.engine.interfaces.messaging.api.Message;
import com.sap.engine.interfaces.messaging.api.MessageDirection;
import com.sap.engine.interfaces.messaging.api.MessageKey;
import com.sap.engine.interfaces.messaging.api.Payload;
import com.sap.engine.interfaces.messaging.api.PublicAPIAccessFactory;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditAccess;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus;
import com.sap.security.api.ssf.ISsfProfile;
import com.sap.tc.logging.Location;
//external import packages here
/**
* @author Colman Maximiliano
*
*/
public class ZMCUtilBean implements SessionBean, Module {
public static final String VERSION_ID = "$Id://tc/aii/30_REL/src/_adapters/_module/yourpackagename/ZMCUtilBean.java#1 $";
static final long serialVersionUID = 7435850550539048631L;
String SIGNATURE = "process(ModuleContext moduleContext, ModuleData inputModuleData)@ZMCUtils";
Location location = null;
AuditAccess audit = null;
MessageKey key = null;
Message msg = null;
byte[] bytes = null;
Object obj = null;
private SessionContext moduleContext;
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext context)
{moduleContext = context;}
public void ejbCreate() throws CreateException {}
public ModuleData process(ModuleContext moduleContext, ModuleData inputModuleData)
throws ModuleException
{
// Create the location always new to avoid serialization/transient of location
try {
location = Location.getLocation(this.getClass().getName());
} catch (Exception t) {
t.printStackTrace();
ModuleException me = new ModuleException("Unable to create trace location", t);
throw me;
}
Object obj = null;
Message msg = null;
key = null;
try {
obj = inputModuleData.getPrincipalData();
msg = (Message) obj;
if (msg.getMessageDirection().equals(MessageDirection.OUTBOUND))
key = new MessageKey(msg.getMessageId(), MessageDirection.OUTBOUND);
else
key = new MessageKey(msg.getMessageId(), MessageDirection.INBOUND);
audit = PublicAPIAccessFactory.getPublicAPIAccess().getAuditAccess();
audit.addAuditLogEntry(key, AuditLogStatus.SUCCESS, "ZMCUtils: Module called");
}
catch (Exception e) {
ModuleException me = new ModuleException(e);
throw me;
}
// Read the channel ID, channel and the module configuration
String keyStorageView=null;
String keyStorageEntry=null;
try {
// CS_GETMODDAT START
keyStorageView = (String) moduleContext.getContextData("keyStorageView");
keyStorageEntry = (String) moduleContext.getContextData("keyStorageEntry");
// CS_GETMODDAT END
if (keyStorageView == null) {
location.debugT(SIGNATURE, "keyStorageView parameter is not set.");
audit.addAuditLogEntry(key, AuditLogStatus.WARNING, "keyStorageView parameter is not set.");
}
location.debugT(SIGNATURE, "keyStorageView is set to {0}", new Object[] {keyStorageView});
audit.addAuditLogEntry(key, AuditLogStatus.WARNING, "keyStorageView is set to {0}", new Object[] {keyStorageView});
if (keyStorageEntry == null) {
location.debugT(SIGNATURE, "keyStorageEntry parameter is not set.");
audit.addAuditLogEntry(key, AuditLogStatus.WARNING, "keyStorageEntry parameter is not set.");
}
location.debugT(SIGNATURE, "keyStorageEntry is set to {0}", new Object[] {keyStorageEntry});
audit.addAuditLogEntry(key, AuditLogStatus.WARNING, "keyStorageEntry is set to {0}", new Object[] {keyStorageEntry});
} catch (Exception e) {
location.catching(SIGNATURE, e);
location.errorT(SIGNATURE, "Cannot read the module context and configuration data");
audit.addAuditLogEntry(key, AuditLogStatus.ERROR, "Cannot read the module context and configuration data");
ModuleException me = new ModuleException(e);
location.throwing(SIGNATURE, me);
throw me;
}
try {
Payload payload = msg.getMainPayload();
byte[] bData = payload.getContent();
byte[] bSignedData = sign(bData,keyStorageView,keyStorageEntry);
payload.setContent(bSignedData);
return inputModuleData;
}
catch (Exception e) {
location.catching(SIGNATURE, e);
location.errorT(SIGNATURE, "Error");
audit.addAuditLogEntry(key, AuditLogStatus.ERROR, "Error");
ModuleException me = new ModuleException(e);
location.throwing(SIGNATURE, me);
throw me;
}
}
public byte[] sign(byte[] inpBytes, String keyStorageView,String keyStorageEntry) throws Exception {
KeyStore keyStore = null;
SAPSecurityResources secRes = SAPSecurityResources.getInstance();
KeyStoreManager manager = secRes.getKeyStoreManager(PermissionMode.SYSTEM_LEVEL, new String[]{"ZMCUtils"});
keyStore = manager.getKeyStore(keyStorageView);
ISsfProfile privKeyProf = manager.getISsfProfile(keyStore, keyStorageEntry, null);
PrivateKey privKey = privKeyProf.getPrivateKey();
X509Certificate pubCert = privKeyProf.getCertificate();
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
List certList = new ArrayList();
CMSTypedData msg = new CMSProcessableByteArray(inpBytes);
certList.add(pubCert);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(sha1Signer, pubCert));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, true);
return sigData.getEncoded();
}
}
Example of Verify:
You'll need the following libraries:
bcpkix-jdk15on-152.jar
bcprov-ext-jdk15on-152.jar
bcprov-jdk15on-152.jar
import java.io.File;
import java.io.FileInputStream;
import java.security.Provider;
import java.security.Security;
import java.util.Collection;
import java.util.Iterator;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataParser;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
public class test {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
FileInputStream fileInputStream=null;
FileInputStream fileInputStream2=null;
File file = new File("C:\\test\\BC_FS_0002\\in\\va20150904-143815-664.txt");
try {
fileInputStream = new FileInputStream(file);
fileInputStream2 = new FileInputStream(file);
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
CMSSignedData signedData = new CMSSignedData(fileInputStream);
CMSProcessable signedContent=signedData.getSignedContent();
CMSSignedDataParser sp = new CMSSignedDataParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), fileInputStream2);
sp.getSignedContent().drain();
Store certStore = sp.getCertificates();
SignerInformationStore signers = sp.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
System.out.println("verify returns: " + signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
System.out.println(new String((byte[])signedContent.getContent()));
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
SSF:
Secure Store and Forward Mechanism (SSF) - Secure Programming - SAP Library
It is a standard library of SAP that allows "the same" (in this case) that bouncycastle.
I've used both and I prefer SSF.
Code for SSF:
You'll need the following libraries:
sap.com~tc~je~keystore_api~API.jar
sap.com~tc~logging~java~impl.jar
sap.com~tc~sec~ssf.jar
sap.com~tc~sec~ume~core~impl.jar
package com.zmc.utils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import javax.ejb.CreateException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import com.sap.aii.af.lib.mp.module.Module;
import com.sap.aii.af.lib.mp.module.ModuleContext;
import com.sap.aii.af.lib.mp.module.ModuleData;
import com.sap.aii.af.lib.mp.module.ModuleException;
import com.sap.aii.af.service.resource.SAPSecurityResources;
import com.sap.aii.security.lib.KeyStoreManager;
import com.sap.aii.security.lib.PermissionMode;
import com.sap.engine.interfaces.messaging.api.Message;
import com.sap.engine.interfaces.messaging.api.MessageDirection;
import com.sap.engine.interfaces.messaging.api.MessageKey;
import com.sap.engine.interfaces.messaging.api.Payload;
import com.sap.engine.interfaces.messaging.api.PublicAPIAccessFactory;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditAccess;
import com.sap.engine.interfaces.messaging.api.auditlog.AuditLogStatus;
import com.sap.security.api.ssf.ISsfProfile;
import com.sap.security.core.server.ssf.SsfDataPKCS7;
import com.sap.security.core.util.Base64;
import com.sap.tc.logging.Location;
//external import packages here
/**
* @author Colman Maximiliano
*
*/
public class ZMCUtilBean implements SessionBean, Module {
public static final String VERSION_ID = "$Id://tc/aii/30_REL/src/_adapters/_module/yourpackagename/ZMCUtilBean.java#1 $";
static final long serialVersionUID = 7435850550539048631L;
String SIGNATURE = "process(ModuleContext moduleContext, ModuleData inputModuleData)@ZMCUtils";
Location location = null;
AuditAccess audit = null;
MessageKey key = null;
Message msg = null;
byte[] bytes = null;
Object obj = null;
private SessionContext moduleContext;
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext context)
{moduleContext = context;}
public void ejbCreate() throws CreateException {}
public ModuleData process(ModuleContext moduleContext, ModuleData inputModuleData)
throws ModuleException
{
// Create the location always new to avoid serialization/transient of location
try {
location = Location.getLocation(this.getClass().getName());
} catch (Exception t) {
t.printStackTrace();
ModuleException me = new ModuleException("Unable to create trace location", t);
throw me;
}
Object obj = null;
Message msg = null;
key = null;
try {
obj = inputModuleData.getPrincipalData();
msg = (Message) obj;
if (msg.getMessageDirection().equals(MessageDirection.OUTBOUND))
key = new MessageKey(msg.getMessageId(), MessageDirection.OUTBOUND);
else
key = new MessageKey(msg.getMessageId(), MessageDirection.INBOUND);
audit = PublicAPIAccessFactory.getPublicAPIAccess().getAuditAccess();
audit.addAuditLogEntry(key, AuditLogStatus.SUCCESS, "ZMCUtils: Module called");
}
catch (Exception e) {
ModuleException me = new ModuleException(e);
throw me;
}
// Read the channel ID, channel and the module configuration
String keyStorageView=null;
String keyStorageEntry=null;
try {
// CS_GETMODDAT START
keyStorageView = (String) moduleContext.getContextData("keyStorageView");
keyStorageEntry = (String) moduleContext.getContextData("keyStorageEntry");
// CS_GETMODDAT END
if (keyStorageView == null) {
location.debugT(SIGNATURE, "keyStorageView parameter is not set.");
audit.addAuditLogEntry(key, AuditLogStatus.WARNING, "keyStorageView parameter is not set.");
}
location.debugT(SIGNATURE, "keyStorageView is set to {0}", new Object[] {keyStorageView});
audit.addAuditLogEntry(key, AuditLogStatus.WARNING, "keyStorageView is set to {0}", new Object[] {keyStorageView});
if (keyStorageEntry == null) {
location.debugT(SIGNATURE, "keyStorageEntry parameter is not set.");
audit.addAuditLogEntry(key, AuditLogStatus.WARNING, "keyStorageEntry parameter is not set.");
}
location.debugT(SIGNATURE, "keyStorageEntry is set to {0}", new Object[] {keyStorageEntry});
audit.addAuditLogEntry(key, AuditLogStatus.WARNING, "keyStorageEntry is set to {0}", new Object[] {keyStorageEntry});
} catch (Exception e) {
location.catching(SIGNATURE, e);
location.errorT(SIGNATURE, "Cannot read the module context and configuration data");
audit.addAuditLogEntry(key, AuditLogStatus.ERROR, "Cannot read the module context and configuration data");
ModuleException me = new ModuleException(e);
location.throwing(SIGNATURE, me);
throw me;
}
try {
Payload payload = msg.getMainPayload();
byte[] bData = payload.getContent();
byte[] bSignedData = sign(bData,keyStorageView,keyStorageEntry);
payload.setContent(bSignedData);
return inputModuleData;
}
catch (Exception e) {
location.catching(SIGNATURE, e);
location.errorT(SIGNATURE, "Error");
audit.addAuditLogEntry(key, AuditLogStatus.ERROR, "Error");
ModuleException me = new ModuleException(e);
location.throwing(SIGNATURE, me);
throw me;
}
}
public byte[] sign(byte[] inpBytes, String keyStorageView,String keyStorageEntry) throws Exception {
boolean res = false;
ByteArrayInputStream is = null;
SsfDataPKCS7 data = null;
byte[] result = null;
KeyStore keyStore = null;
SAPSecurityResources secRes = SAPSecurityResources.getInstance();
KeyStoreManager manager = secRes.getKeyStoreManager(PermissionMode.SYSTEM_LEVEL, new String[]{"ZMCUtils"});
keyStore = manager.getKeyStore(keyStorageView);
ISsfProfile privKeyProf = manager.getISsfProfile(keyStore, keyStorageEntry, null);
is = new ByteArrayInputStream(inpBytes);
data = new SsfDataPKCS7((InputStream)is);
res = data.sign(privKeyProf);
if (!res) {
throw new Exception("Error on Sign");
}else{
result = data.getDataPKCS7();
return result;
}
}
}
Example of Verify:
You'll need the following libraries:
iaik_jce.jar
sap.com~tc~logging~java~impl.jar
sap.com~tc~sec~ssf.jar
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import com.sap.security.core.server.ssf.SsfDataPKCS7;
public class test {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
FileInputStream fileInputStream=null;
File file = new File("C:\\test\\BC_FS_0002\\in\\va20150904-110530-410.txt");
fileInputStream = new FileInputStream(file);
boolean res = false;
SsfDataPKCS7 data = null;
String result = null;
data = new SsfDataPKCS7((InputStream)fileInputStream);
res = data.verify(null,null,null,null);
if (!res) {
throw new Exception("Error on Verify");
}else{
result = new String(data.getDataPKCS7());
System.out.println(result);
}
}
}
To find the libraries in the PI server I recommend you use a UDF function that placing the class file tells you the path of "jar"(I could not find the post in the SCN) :
Example of a scenario file to file:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Nitin,
The bank should have the detailed specification document on how to consume their APIs. Try to get it.
The topic is broad. The below links should help you.
http://www.programcreek.com/java-api-examples/index.php?api=org.bouncycastle.cms.CMSSignedData
regards,
Arun
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
HI Arun,
I have got the sample code and i have implemented it. But unfortunately PI is unable to read a referenced class and i am facing below error -
Unable to display class because it is dependent on class org/bouncycastle/cms/CMSProcessable, and class org/bouncycastle/cms/CMSProcessable is not available in the software component version.
See error logs for details
CMSProcessable class is present in the Bouncy Castle API i have downloaded and the JAR file of the same has been into the Build Path. In NWDS, i do not have any errors and everything is successful. But in PI it shows this error. Can you please help me out with this on an urgent basis?
Regards,
Nitin
User | Count |
---|---|
88 | |
10 | |
10 | |
9 | |
7 | |
7 | |
6 | |
5 | |
4 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.