Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
nitindeshpande
Active Contributor


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 :smile:

 

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.vijayakumar2 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-sa...

 

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 :smile: Trust me this is a very simple code because an amateur like me in Java could write this code :wink:

 

 











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..

7 Comments
Labels in this area