cancel
Showing results for 
Search instead for 
Did you mean: 

Signing using Bouncy Castle with Certificates - Attached signature to request message

nitindeshpande
Active Contributor
0 Kudos

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

Accepted Solutions (1)

Accepted Solutions (1)

asdasd_asdasd
Active Participant
0 Kudos

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:

nitindeshpande
Active Contributor
0 Kudos

Hello Everyone,

In addition to the Max's code. In the below blog you can find the complete code to sign the message using Bouncy Castle API.

Regards,

Nitin Deshpande

Answers (1)

Answers (1)

arunneerolil
Contributor
0 Kudos

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

http://www.programcreek.com/java-api-examples/index.php?api=org.bouncycastle.jce.provider.BouncyCast...

regards,

Arun

nitindeshpande
Active Contributor
0 Kudos

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