Skip to Content

All of you, once in your PI career would have worked on the integration with Bank scenarios. I have worked on 3 scenarios in a career of 3 years and trust me Bank guys are very stubborn and expecting an slightest of change also from them is like asking someone to lend 1 million dollars 🙂

 

Intention of writing this blog post is to help those people who are looking for a signing method which can be done in mapping and who specifically look for CMS standards and here i am signing the message using Bouncy Castle API

 

There are many other standard methods available for signing the request message like the below ones –

 

1. PGP Encryption method, where you can turn off the Encryption part and just sign the message using your private key and the same message can be verified at the receiver end using the Public key. You can find below the blog from Shabarish Vijaya kumar and William Li for the detailed steps to perform PGP signing.

 

PGPEncryption Module: A Simple How to Guide

 

Using PGP in Process Integration

 

2. Signing using WSSE – This is the standard Web service security provided in SOAP receiver channel to sign the message. Even in this method we can either go for only signing or only encryption or both signing and encryption. The detailed steps have been listed out very nicely by Rajendra in the below blog. You can refer it if you are looking for this method of signing.

 

http://people/rajendra.badi/blog/2011/08/24/configuring-wsse-digital-signing-and-encryption-using-sap-pi-711-aae-soap-ad…

 

In my case, i tried all the above mentioned methods and they were working perfectly and without any issue. But the issue came up when Bank came up into the picture, they started rejecting the above methods for the reason they don’t use web-service at their end and they do not expect SOAP envelope in the message. Signing using WSSE works only with SOAP envelope. Hence 2nd method was rejected. For the 1st method, i used ASCII armored keys (.asc) and Bank told we want you to sign only using a private key and we can verify it only using your public i.e, your X509 certificates.

 

Hence, i came up with the Idea of writing a Java code for the Signing and additional part was to encode the signing using Base64 encoding.

 

Here you can find the complete code of signing your request message using Bouncy Castle API. Please note i have used Private key (.pfx or .p12) of the SAP PI system to sign the message.

 

And i am not so great at Java, so please excuse me for redundancy or irregular method declaration or unnecessary import of libraries 🙂 Trust me this is a very simple code because an amateur like me in Java could write this code 😉

 

 

Java code for Signing and encoding of PI message payload

package com.javamapping.signing;

 

 

 

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

import java.security.KeyStore;

import java.security.PrivateKey;

import java.security.Security;

import java.security.cert.CertStore;

import java.security.cert.CollectionCertStoreParameters;

import java.security.cert.X509Certificate;

import java.util.ArrayList;

import java.util.Enumeration;

 

 

import org.apache.commons.codec.binary.Base64;

import org.bouncycastle.cms.CMSProcessableByteArray;

import org.bouncycastle.cms.CMSSignedData;

import org.bouncycastle.cms.CMSSignedDataGenerator;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

 

 

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.aii.utilxi.core.io.IOUtil;

 

 

 

 

public class SigningBouncyCastle extends AbstractTransformation {

 

public void transform(TransformationInput input, TransformationOutput output)

throws StreamTransformationException {

String finalString = “”;

String pass = “”;

 

try {

 

 

InputStream ins = input.getInputPayload().getInputStream();

 

 

 

 

String input_data = IOUtil.copyToString(ins, “UTF-8”);

finalString = signRequest(input_data, pass);

 

 

output.getOutputPayload().getOutputStream().write(

finalString.getBytes());

 

} catch (Exception ie) {

// do nothing

}

}

 

private String signRequest(String strPaymentRequest, String strPassword) {

 

 

X509Certificate cert = null;

PrivateKey priv = null;

 

try {

// Below we are using BouncyCastle classes to sign the Request Message.

Security.addProvider(new BouncyCastleProvider());

 

 

 

String pass = “Your Private Key Password”;

File file = new File(“Path of the SAP Application where Private key is stored”);

InputStream stream = new FileInputStream(file);

KeyStore store = KeyStore.getInstance(“PKCS12”);

store.load(stream, pass.toCharArray());

PrivateKey key = (PrivateKey)store.getKey(“Your Private Key File name”, pass.toCharArray());

 

 

 

Enumeration e = store.aliases();

String name = “”;

 

 

if (e != null) {

while (e.hasMoreElements()) {

String n = (String) e.nextElement();

if (store.isKeyEntry(n)) {

name = n;

}

}

}

 

 

// Get the private key and the certificate

priv = key;

cert = (X509Certificate) store.getCertificate(name);

 

 

java.security.cert.Certificate[] certChain = store.getCertificateChain(name);

 

 

ArrayList certList = new ArrayList();

CertStore certs = null;

for (int i = 0; i < certChain.length; i++)

certList.add(certChain[i]);

certs = CertStore.getInstance(“Collection”,new CollectionCertStoreParameters(certList), “BC”);

 

 

// Encrypt data

CMSSignedDataGenerator sgen = new CMSSignedDataGenerator();

 

 

// What digest algorithm i must use? SHA1? MD5? RSA?…

// In our case we are using SHA1 algorithm

// CMSSignedDataGenerator.DIGEST_SHA1 = “1.3.14.3.2.26”

sgen.addSigner(priv, (X509Certificate) cert,CMSSignedDataGenerator.DIGEST_SHA1);

 

 

sgen.addCertificatesAndCRLs(certs);

 

 

// Convert the message to UTF8 encoding

 

 

byte[] utf8 = strPaymentRequest.getBytes(“UTF-8”);

 

 

// Initialize signer object using UTF8 encoded string, detached =

// true, and //Bouncy Castle provider (BC).

// The 2nd parameter need to be true (detached form) we need to

// attach //original message to signed message

CMSSignedData csd = sgen.generate(new CMSProcessableByteArray(utf8), true, “BC”);

// Get signed message

byte[] signedData = csd.getEncoded();

// Get base 64 representation of signed message

byte[] signedDataB64 = Base64.encodeBase64(signedData);

String str = new String(signedDataB64);

//output.getOutputPayload().getOutputStream().write(str.getBytes());

// Write Base64 encoded message to file – If needed

// FileWriter fw = new FileWriter(“Base64Encoded.txt”, false);

// fw.write(signedDataB64);

// fw.flush();

// fw.close();

// FileOutputStream out = new FileOutputStream(“Signed.txt”);

// out.write(signedData);

// out.close();

 

 

return str;

} catch (Exception ex) {

System.out.println(“Error signing payment request. Please verify the certificate.”);

ex.printStackTrace();

return “”;

}

 

}

}

 

You can also test the above code by writing simple few lines in your Main class –

 

Main Class

public static void main(String[] args) throws FileNotFoundException {

 

try {

InputStream in = new FileInputStream(new File(“Your Input File Path”)); //Make Sure this is the Workspace path where your Java code is running.

 

 

OutputStream out = new FileOutputStream(new File(“Your Output File Path”));

 

SigningBouncyCastle bouncyCastle = new SigningBouncyCastle();

 

 

bouncyCastle.transform(in, out);

} catch (Exception e) {

e.printStackTrace();

}

}

 

Once you are done with the testing. Successfully build your Java code and export the java code as JAR file and import it as Imported Archive and use it in your Operation Mapping.

 

 

One more point where i faced difficulty was in searching for the right jar files. Getting the matching JDK versions JAR file was an uphill task. I have attached all the Jar files used in this Java Mapping to make it easy for you. Please note the JAR files attached are for JDK version 1.6.

 

 

This is my first blog on Java Mapping, i would like to know the feedback from all the SCN members out there..

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Eng Swee Yeoh

    Hi Nitin

    Thanks for sharing your experience with the community.

    I noticed from your code that you are accessing the key from the file system. If I’m not mistaken, it is possible to import an X.509 cert or PKCS#12 key pair directly into NWA. You can then possibly access the KeyStore using the KeyStoreManager mentioned in the following thread.

    Java Mapping for Inserting digital signature using keystore in PI 7.1

    Just a thought for further improvement because NWA keystore would be a more suitable place compared to the file system, because you can view and manage the certs, and see if any will be expiring soon (yellow) or expired (red).

    Rgds

    Eng Swee

    (0) 
    1. Nitin Deshpande Post author

      Hello Eng,

      Thanks a lot for your comments. I will soon update the blog with the code for accessing the Private key from Keystore.

      Regards,

      Nitin Deshpande

      (0) 
  2. Anant Verekar

    Hi Nitin,

    First of all thanks for the nice blog.

    I wanted to implement the same in my scenario and I couldn’t find the .jar file mentioned in your blog.
    Also did you get time to update the blog with code for accessing private key from key store.

    Kindly reply.
    Br,
    Anant

    (0) 
    1. Nitin Deshpande Post author

      Hello Anant,

      I had uploaded the jar files, i guess they have been missed during the migration from SCN to this new portal and in this new portal, i do not see the option to upload the attachment.

      Do you know where is the option to upload the attachment? Or any other community member can help me out here?

      Regards,
      Nitin

      (0) 

Leave a Reply