Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
CarlosRoggan
Product and Topic Expert
Product and Topic Expert
1,474
SAP Cloud Integration (CPI) provides functionality to automatically verify a message with PKCS#7 / CMS compliant signature.
While there’s not much to explain about it, however, this blog post aims to clarify the settings for the so-called detached mode.
A simple tutorial helps to understand the theory in real life.

Quicklinks:
Quick Guide



Content


0. Prerequisites
1. Introduction
2. Verifier Configuration
2.1. Non-detached mode, no Base64 encoding
2.2. Non-detached mode, with Base64 encoding
2.3. Detached mode, no Base64 encoding
2.4. Detached mode, with Base64 encoding
3. Hands-On Example
4. Optional: OpenSSL
Appendix: Groovy Script for Troubleshooting

0. Prerequisites


This blog post build upon the previous blog post which explained the PKCs #7 / CMS Signer.
It is an advantage to understand the PKCS #7 or CMS standard (in my translation).

For remaining open questions I recommend the Security Glossary.

To follow this tutorial, access to a Cloud Integration tenant is required, as well as basic knowledge about creating iFlows.

1. Introduction


Little recap.

I want to send an important document to you via internet.
To make sure that it is not modified on the way, I create a hash value from it (== sign).
I send both the document and the hash value to you.
You take the document and create your own hash value of it
You compare your hash value with mine: they must be identical.
This procedure you’ve done is called verification.

To make things even more safe, I encrypt the hash value with my private key (== digital signature).
So you have to decrypt with my public key, then verify.

For this procedure you need artifacts and information: document, hash value, certificate, algorithms, etc.
To make this procedure succeed, the PKCS #7 standard was defined, later renamed to CMS.
The standard is a guideline how to store all artifacts and info in a well-defined structured way.
As such, to make things compliant and automatic, I send a CMS-based message to you.
It is like a package that contains the document itself, the hash value, etc

This CMS-package is binary, so I can encode it with Base64.
Base64 translates binary content into a string of “normal” characters (numbers, characters, few others).
This doesn’t add a layer of security, it is just for convenient network transfer.

The CMS-standard describes a structure for my message, which I want to sign. This structure type is called SignedData.
The definition of this “SignedData” type describes where to store the information about e.g. which algorithm was used for hashing (e.g. SHA-512).
It also describes which kind is the content that was signed (the "content-type").
Now, this content is usually contained in the CMS-package (message) as well, obviously, as it is the actual interesting part of the message.
However, if the package gets too big, it is possible to separate the content from the CMS-package (the CMS-package is sometimes referred to as Signed Data).
In this case, we talk about the detached mode, or external signature.

In CPI, the PKCS #7 / CMS Signer supports this mode with the checkbox “Include Content in Signed Data”.
If this case, the actual content is remains in the message body and the CMS-package is put into an exchange property called SapCmsSignedData.

About Base64 encoding.
The Signer supports Base64 encoding via a checkbox as well.
We have to distinguish:

In “normal” mode, the message is Base64-encoded.
In “detached mode”, the CMS-package (contained in the property) is Base64-encoded.

Now finally coming to the PKCS #7 / CMS Verifier.
This iFlow step receives a CMS-compliant signed message and it does the verification.
The verifier needs to support the “detached” mode as well.
To support both cases (normal and detached), it has 2 checkboxes.
And this is the reason for some confusion: how to set the checkboxes.
Fortunately, this blog post should clarify any doubts.
We need to configure the Verifier according to the configuration of the Signer.
Below section goes through the possible configuration variants in detail.

Note:
As we’re living in a CMS-standard-compliant world, the message could be created by the Signer in CPI, or any other tool outside CPI.
The scenario with OpenSSL was described here.

Note:
The signature verification process is described in the CMS specification here.

2. Verifier Configuration


We have to distinguish the 4 variants in which a message can reach the Verifier:
First of all there are the 2 variants related to the content:
The "normal" mode, where the actual content is contained in the CMS-package.
The "detached mode", where the actual content is contained in the message body and the CMS-package is put into the exchange property.

In both variants, the Signer allows to apply Base64 encoding.
In “normal” mode, the message is Base64-encoded.
In “detached mode”, only the CMS-package (sent in the property) is Base64-encoded.

As such, we have to look at 4 variants:
"normal mode" with and without encoding
"detached mode" with and without encoding

Each variant requires different configuration of the Verifier.

2.1. Non-detached mode, no Base64 encoding


First we have a look at the default settings:
The signer is configured to include the content in the CMS package.
The signer doesn’t encode the CMS package
This results in a message that does not use the property SapCmsSignedData.
The verifier can be used with default settings, no base64 encoding has to be enabled.



2.2. Non-detached mode, with Base64 encoding


Next, we enable the Base64-encoding for the CMS-package.
This results in a message that has the body encoded. The property is again missing, as it is not used.
The verifier has to be prepared to expect a Base64-encoded message.
As such, we enable the checkbox “Body is Base64 encoded.



2.3. Detached mode, no Base64 encoding


Coming to the interesting settings:
Now we choose the detached mode, so we disable “Include Content…”.
However, we don't select Base64 encoding for this round.

Now the message in the flow will have the property SapCmsSignedData set, and carrying the CMS-package in binary (DER) format.
The body of the message will contain the original content (in our example it is plaintext, as set in the Content Modifier, see hands-on below).

The verifier can be used with default settings, no base64 encoding has to be enabled.
The verifier automatically detects the “detached mode” and finds the CMS-package in the exchange property.



2.4. Detached mode, with Base64 encoding


The last round.
In the Signer, we disable “Include Content…” again, for the detached mode.
This time we enable the Base64 encoding.

As a result, the message will have the property SapCmsSignedData set, and carrying the CMS-package as base64-string.
The body of the message will again contain the original content.

Now the Verifier has to be properly configured to expect the Base64-encoding.
As we’re running the detached mode, it is not the body that is encoded.
So we enable the checkbox of “Header is Base64…”

Now we’re wondering: why header?
The answer can be found in the documentation of the Signer.
It says that in earlier versions of the Signer, the CMS-package was put into the header.
But now, in current versions, the CMS-package is put into the exchange property.
In both cases, the name is: SapCmsSignedData.
Which version exactly?
The header was used in old versions up to 1.2
Since 1.3 the exchange property is used.
How to find out?
If you’re unsure, which version you’re using:
Select the PKCS#7 / CMS Signer step in the editor and from the context icons, click on the “i” for "technical information".
The current version of the Signer today is 1.5

Note:
If you want to be sure what kind of data is being transferred where and how, you can use the Groovy script which can be found in the Appendix.
Just place a Groovy step after the Signer and check the Monitor after the iFlow execution.

Below screenshot shows how to configure Signer and Verifier for the detached mode with Base64-encoding of the CMS-package in the exchange property:



3. Hands-On Example


Let's go through a very simple integration flow, to try it out.
No prerequisites, no tools no adapters required.

3.1. Create Key Pair


We let CPI generate a key pair for us.
This is done in the Keystore of Cloud Integration.
Go to your CPI -> "Operations & Monitoring" -> "Manage Security" -> "Keystore"

Direct link:
<cpi>/itspaces/shell/monitoring/Keystore

Choose "Create" -> "Key Pair"


Enter some values of your choice, e.g. "simplecert" and press “Create”.


The alias is required later, we can take a note of it, or try to remember (or follow my description).

3.2. Create iFlow


To try out the above mentioned settings, we create a simple iFlow.
Although it doesn’t make much sense to place a verifier directly after a signer, it is ok for us as a simplistic approach.


We create an iFlow with the following elements:

  • Start Timer set to “Run Once”.

  • Content Modifier with arbitrary text in Message Body.

  • PKCS#7 Signer with "Private Key Alias" and default settings (“Include Content” is enabled).

  • PKCS#7 Verifier with default settings and "Public Key Alias".

  • Datastore Write operation with arbitrary name.


After deploy and successful execution of the iFlow, we go to our Data Store and have a look at the “body” file of the zip.
It contains the plaintext content that we entered in the Content Modifier step, which means that the verification has been successful.

Now we can go ahead and adjust the settings according to the screenshots of section 2.

3.3. Optional: Negative Flow


Curious to see what would happen in the worst case, if somehow your message was hacked?
As we know, in order to verify a signature, the receiver creates his own hash and compares it to the hash that he has received.
So in such a case, the Verifier would detect that both hashes are different.
In CPI, an Exception is thrown which can be viewed in Monitor Message Processing view.

How can we test such a worst case?
First of all, we adjust our iFlow to use the detached mode, like shown in chapter 2.3. or 2.4.
Now that the original text is separated from the CMS-package, we can easily modify the text.
To do so, we place a new Content Modifier step after the Signer and before the Verifier.
In the message body of this second Content Modifier, we enter any dummy text.
As such, the verifier receives a different content than expected.

After deploy, we can see that no entry is created in the data store.
Instead, in the Monitor Message Processing view, we see a failure and the Error Details says clearly, what we were fearing:

com.sap.esb.camel.security.cms.CmsSignatureInvalidContentHashException: PKCS7/CMS signature verification failed for signer CN=simplecert
Calculated content hash differs from the signed hash value.
Either the message content does not correspond to the signature or the message might be tampered.

4. Optional: OpenSSL


In the OpenSSL-CPI blog post, I’ve described a scenario where we sign a message with OpenSSL in detached mode, then verify it in CPI.
Please find it here.

Summary


In this blog post, we’ve learned how the PKCS #7 / CMS Verifier has to be configured, according to the settings in the Signer.
The interesting scenario is the detached mode with Base64 encoding, in which we have to properly click the checkboxes of the Verifier.
Since version 1.3 of the PKCS #7 / CMS Signer, the detached CMS-package is contained in the exchange property with name SapCmsSignedData.
If the Signer uses “Not include” and “Encode with Base64”, then the Verifier needs to be configured with enabled “Header is Base64” checkbox.

We need to understand the PKCS #7 or CMS standard, to understand the Verifier.
In brief:
A message has been signed according to the CMS specification.
This means that the message contains the original content itself, but also artifacts like the digital signature, certificates and it contains information about used algorithms etc
According to the CMS standard, such a message has the content type “SignedData”.
It is not mandatory that the original content is included in the message, e.g. in case of big size.

SAP Help Portal
Docu for PCKS#7/CMS Verifier
Docu for PCKS#7/CMS Signer
Docu for Message-Level Security

CMS
Understanding PKCS #7 / CMS standard
The CMS spec can be found here or here and even here
Wikipedia: CMS, Cryptographic Message Syntax

OpenSSL
Official list of unofficial binaries download page
Docu home
Docu for cms command

Blogs
Understanding PKCS #7 / CMS Standard
Understanding PKCS #7 / CMS Signer
Security Glossary Blog

Appendix: Groovy Script for Troubleshooting


Below script helps to write the content of body, header and property to the message log.
After viewing the contents, it should be removed.
import com.sap.gateway.ip.core.customdev.util.Message;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;


def Message processData(Message message) {
def messageLog = messageLogFactory.getMessageLog(message);

// print body content (can be binary)
def body = message.getBody(java.lang.String) as String;
messageLog.addAttachmentAsString("body", "Content of message body:\n" + body + "\nEnd of body", "text/plain");


// check headers for SapCmsSignedData
def header = message.getHeaders().get("SapCmsSignedData");
messageLog.addAttachmentAsString("header", "Content of header: " + header, "text/plain");

// check properties for SapCmsSignedData
def prop = message.getProperties().get("SapCmsSignedData");
if (prop != null){
def propAsString = IOUtils.toString(prop, StandardCharsets.UTF_8);
messageLog.addAttachmentAsString("prop", "Value of property:\n" + propAsString + "\nEnd of property value", "text/plain");

// set property to old value
message.setProperty("SapCmsSignedData", null)
ByteArrayInputStream baisStream = new ByteArrayInputStream(propAsString.getBytes());
message.setProperty("SapCmsSignedData", baisStream)
}else{
messageLog.addAttachmentAsString("prop", "Property SapCmsSignedData not found", "text/plain");
}

return message;
}