
ABAP Cloud - Hashes
Do you want to create a hash in ABAP Cloud? Which classes are available for this purpose, and how can you use them effectively?
Table of contents
In this article, we'll look at two classes for creating hashes and how we can use them more easily in our development.
Introduction
Hashes are used in computer science to, for example, check whether a text, a file, or other content has changed. This allows you to calculate a unique hash from a string. If a character in the string changes, you get a completely different hash. This prevents manipulation of content. This method is often used to securely store passwords, since you can no longer derive the original value from a hash, but you can still compare two pieces of content to see if they are the same.
Libraries
In ABAP Cloud, there are various shared classes that you can use to determine a hash. In this chapter, we will look at three variants.
CL_ABAP_MESSAGE_DIGEST
The first shared class we use is for converting strings and XStrings to hashes. Various output formats are provided. We then perform the conversion using the algorithm.
TRY.
cl_abap_message_digest=>calculate_hash_for_char( EXPORTING if_algorithm = 'SHA256'
if_data = value
IMPORTING ef_hashstring = result
ef_hashxstring = DATA(hashxstring)
ef_hashb64string = DATA(hashb64string) ).
CATCH cx_abap_message_digest.
ENDTRY.
In addition to the conversion methods, the class also contains various helper methods, such as converting String to XString and generating a digest from an instance. The class also uses a factory method, and you can work with instance methods in some cases.
CL_ABAP_HMAC
The second class is structured very similarly to the first. The main methods have slight name differences, but the helper methods are also present. The difference, however, is that in addition to a simple hash, we can also generate an HMAC. For this, we use a key to encrypt the string. The other side has the same key and can use it to check whether the string has been manipulated. Often used in the transmission of messages.
TRY.
DATA(converted_key) = cl_abap_hmac=>string_to_xstring( key ).
cl_abap_hmac=>calculate_hmac_for_char( EXPORTING if_algorithm = 'SHA256'
if_data = value
if_key = converted_key
IMPORTING ef_hmacstring = result
ef_hmacxstring = DATA(hmacxstring)
ef_hmacb64string = DATA(hmacb64string) ).
CATCH cx_abap_message_digest.
ENDTRY.
XCO_CP_STRING
In the XCO area, it is also possible to generate a hash using various algorithms. We start with a String object, first converting it to XString and using a format from the XCO_CP_HASH class. Unfortunately, there is only an object for SHA1; for the other algorithms, we have to resort to a magic constant again.
xco_cp=>string( value )->as_xstring( xco_cp_hash=>algorithm->for( 'SHA256' ) )->value.
Comparison
Let's run the different methods for verification using the same strings. First, we generate a hash, then an HMAC without a password, with a password, and finally, we use the XCO class.
All calculated SHA256 hashes are identical except for the one where we used a custom key. This also allows us to generate hashes using the CL_ABAP_HMAC class by simply leaving the key empty.
Algorithms
What is the difference between the various algorithms? Currently, the classes support various calculation algorithms, such as MD5, SHA1, SHA256, SHA384, and SH512. MD5 was often used in the past and is still sometimes used today, but it is outdated from a security perspective and is no longer considered secure. With the length of characters, a unique hash can no longer be guaranteed. Two different strings can produce the same hash, which can be exploited for various attacks. The same now applies to SHA1 as well. Therefore, one of the other algorithms should be used to create hashes.
What is the difference between SHA256, SHA384, and SH512? The methods use different lengths, and the encryption is more computationally intensive because more rounds are performed by the algorithm, each with a corresponding length.
| MD5 | SHA-1 | SHA-256 | SHA-384 | SHA-512 | |
|---|---|---|---|---|---|
| Hash Length | 128 Bits | 160 Bit | 256 bits | 384 bits | 512 bits |
| Character length | 16 | 20 | 32 | 48 | 64 |
| Security | Very Low | Low | High | Very High | Extreme High |
| Block size | 512 bits | 512 bits | 512 bits | 1024 bits | 1024 bits |
| Rounds | 64 | 80 | 64 | 80 | 80 |
Open Source
Currently, the two classes have some minor design flaws, which we can correct with a new facade. Currently missing and/or unclear points are:
- Magic Constants - The hash algorithms used and supported are not clearly defined. There is no indication of algorithms to be avoided (MD5, SHA1).
- Instantiation - The GET_INSTANCE method accepts the algorithm, but this is only used in some of the methods; others must be populated manually.
- Methods - Numerous import and export parameters are used. Likewise, error handling is necessary, which significantly increases the code size.
- Testability - Testability with the classes is not possible without additional effort.
Facade
The facade is a development pattern that takes existing functionalities, bundles them, and exposes them through a unified interface. This allows us to take the existing code and make it available again in an improved form.
Constants
We create an ENUM for the supported algorithms. This relieves us of the need to check the supported formats and returns an error message at runtime if incorrect values are passed. In this case, we also have the option of removing MD5 and SHA1, as they have long been considered unreliable and collision-free.
TYPES: BEGIN OF ENUM algorithms STRUCTURE algorithm,
sha256,
sha384,
sha512,
END OF ENUM algorithms STRUCTURE algorithm.
Testability
To establish testability, we create a factory and an injector. The factory allows us, among other things, to define different instances of the interface. In this case, we can pass the algorithm and a key. If the key is empty, we only generate hashes; otherwise, we use the HMAC class internally.
Design
To avoid implementing the same functions in both implementations, we use an abstract class. Thus, we can map some of the functions, such as the algorithm or string conversion, in the abstract class and use them easily later.
Usage
To use it, we create a new instance via the factory and choose an algorithm. SHA512 is used by default if no other algorithm is specified. We can pass the key as a string or XString. Internally, the XString is always used, and the conversion is performed directly.
DATA(hash) = zcl_hash_factory=>create_hash( algorithm = zif_hash=>algorithm-sha256
key_plain = key ).
Using the GET_HASH_FOR_STRING method, we can then generate a hash and receive various types back. Here, we use the HASHSTRING field as the result and return it to the caller or use it directly.
hash->get_hash_for_string( value )-hashstring.
Complete Example
Here you will find the example from above, so you can recreate it on your system. The open-source project can be found on our GitHub repsository if you need further inspiration or want to use the classes as well.
CLASS zcl_bs_demo_hash DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
METHODS create_hash
IMPORTING !value TYPE string
RETURNING VALUE(result) TYPE string.
METHODS create_hmac
IMPORTING !value TYPE string
!key TYPE string
RETURNING VALUE(result) TYPE string.
METHODS create_xco
IMPORTING !value TYPE string
RETURNING VALUE(result) TYPE string.
METHODS create_open_source
IMPORTING !value TYPE string
!key TYPE string
RETURNING VALUE(result) TYPE string.
ENDCLASS.
CLASS zcl_bs_demo_hash IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(hash) = create_hash( '123456' ).
out->write( hash ).
DATA(hmac_without) = create_hmac( value = '123456'
key = `` ).
out->write( hmac_without ).
DATA(hmac_with) = create_hmac( value = '123456'
key = `ABC` ).
out->write( hmac_with ).
DATA(xco_hash) = create_xco( '123456' ).
out->write( xco_hash ).
ENDMETHOD.
METHOD create_hash.
TRY.
cl_abap_message_digest=>calculate_hash_for_char( EXPORTING if_algorithm = 'SHA256'
if_data = value
IMPORTING ef_hashstring = result
ef_hashxstring = DATA(hashxstring)
ef_hashb64string = DATA(hashb64string) ).
CATCH cx_abap_message_digest.
CLEAR result.
ENDTRY.
ENDMETHOD.
METHOD create_hmac.
TRY.
DATA(converted_key) = cl_abap_hmac=>string_to_xstring( key ).
cl_abap_hmac=>calculate_hmac_for_char( EXPORTING if_algorithm = 'SHA256'
if_data = value
if_key = converted_key
IMPORTING ef_hmacstring = result
ef_hmacxstring = DATA(hmacxstring)
ef_hmacb64string = DATA(hmacb64string) ).
CATCH cx_abap_message_digest.
CLEAR result.
ENDTRY.
ENDMETHOD.
METHOD create_xco.
RETURN xco_cp=>string( value )->as_xstring( xco_cp_hash=>algorithm->for( 'SHA256' ) )->value.
ENDMETHOD.
METHOD create_open_source.
DATA(hash) = zcl_hash_factory=>create_hash( algorithm = zif_hash=>algorithm-sha256
key_plain = key ).
RETURN hash->get_hash_for_string( value )-hashstring.
ENDMETHOD.
ENDCLASS.
Conclusion
Generating hashes is crucial for controlling and ensuring against manipulation. If you want to securely store information so you can compare it later, you can also use hashes. However, if you want to have the information back in a readable format later, this is the wrong way to securely store it.

