Skip to content

CL_ABAP_HMAC / CL_ABAP_MESSAGE_DIGEST

Compute HMAC signatures and cryptographic hash digests (SHA-256, SHA-1, MD5) for API authentication and data integrity checks.

Purpose

Two related classes handle cryptographic hashing in ABAP:

Class Use for
CL_ABAP_HMAC HMAC (keyed hash) — API request signing, webhook verification
CL_ABAP_MESSAGE_DIGEST Plain hash (no key) — checksums, fingerprints, integrity checks

Supported algorithms

'SHA256', 'SHA384', 'SHA512', 'SHA1', 'MD5'

MD5 and SHA-1 are deprecated for security

MD5 and SHA-1 are cryptographically broken. Use 'SHA256' or higher for any security-relevant purpose.

These are instance methods — use GET_INSTANCE factory

Both classes must be instantiated via get_instance( ). UPDATE, GET_DIGEST, TO_STRING, TO_BASE64 etc. are called on the instance. There is no FINAL method on CL_ABAP_MESSAGE_DIGEST — use GET_DIGEST( ) or TO_STRING( ) to retrieve the result.

CL_ABAP_HMAC — keyed hash (API signing)

REPORT z_hmac_demo.

START-OF-SELECTION.

  DATA: lv_key      TYPE xstring,
        lv_data     TYPE xstring,
        lv_hmac_b64 TYPE string,
        lv_hmac_raw TYPE xstring.

  " Convert key and message to xstring using instance method
  DATA(lo_out_key) = cl_abap_conv_out_ce=>create( encoding = 'UTF-8' ).
  lo_out_key->write( data = 'my-secret-key' ).
  lv_key = lo_out_key->get_buffer( ).

  DATA(lo_out_data) = cl_abap_conv_out_ce=>create( encoding = 'UTF-8' ).
  lo_out_data->write( data = 'message to sign' ).
  lv_data = lo_out_data->get_buffer( ).

  TRY.
      DATA(lo_hmac) = cl_abap_hmac=>get_instance(
                        if_algorithm = 'SHA256'
                        if_key       = lv_key ).

      lo_hmac->update( if_data = lv_data ).

      " Get result as Base64 string
      lo_hmac->final( IMPORTING ef_hmacb64string = lv_hmac_b64 ).

      " Or as raw xstring:
      " lo_hmac->final( IMPORTING ef_hmac = lv_hmac_raw ).

    CATCH cx_abap_message_digest INTO DATA(lx).
      MESSAGE lx->get_text( ) TYPE 'E'.
  ENDTRY.

  WRITE: / 'HMAC-SHA256 (Base64):', lv_hmac_b64.

CL_ABAP_MESSAGE_DIGEST — plain hash (no key)

For a single string input, CALCULATE_HASH_FOR_CHAR does everything in one call — no instance needed:

REPORT z_digest_static.

START-OF-SELECTION.

  DATA lv_hash_hex TYPE string.
  DATA lv_hash_b64 TYPE string.

  TRY.
      cl_abap_message_digest=>calculate_hash_for_char(
        EXPORTING
          if_algorithm     = 'SHA256'
          if_data          = 'text to hash'
        IMPORTING
          ef_hashstring    = lv_hash_hex
          ef_hashb64string = lv_hash_b64 ).
    CATCH cx_abap_message_digest INTO DATA(lx).
      MESSAGE lx->get_text( ) TYPE 'E'.
  ENDTRY.

  WRITE: / 'SHA-256 (hex):', lv_hash_hex.
  WRITE: / 'SHA-256 (b64):', lv_hash_b64.

Use the instance API when hashing data in multiple chunks via repeated UPDATE calls:

REPORT z_digest_instance.

START-OF-SELECTION.

  " STRING_TO_XSTRING is a static helper on the same class
  DATA(lx_data) = cl_abap_message_digest=>string_to_xstring( 'text to hash' ).

  TRY.
      DATA(lo_digest) = cl_abap_message_digest=>get_instance(
                          if_algorithm = 'SHA256' ).

      lo_digest->update( if_data = lx_data ).

      " Retrieve result — to_string returns hex, to_base64 returns Base64
      DATA(lv_hash_hex) = lo_digest->to_string( ).
      DATA(lv_hash_b64) = lo_digest->to_base64( ).

    CATCH cx_abap_message_digest INTO DATA(lx).
      MESSAGE lx->get_text( ) TYPE 'E'.
  ENDTRY.

  WRITE: / 'SHA-256 (hex):', lv_hash_hex.
  WRITE: / 'SHA-256 (b64):', lv_hash_b64.

Verify a webhook signature

REPORT z_webhook_verify.

START-OF-SELECTION.

  " Simulate incoming webhook header: X-Signature: sha256=<base64value>
  DATA lv_header_value TYPE string VALUE 'sha256=abc123base64value=='.
  DATA lv_raw_body     TYPE string VALUE 'webhook payload body'.
  DATA lv_secret       TYPE string VALUE 'my-webhook-secret'.

  " Strip 'sha256=' prefix (7 characters) from received signature
  DATA(lv_received_sig) = lv_header_value+7.

  " Compute HMAC on the raw body
  DATA(lo_out_key) = cl_abap_conv_out_ce=>create( encoding = 'UTF-8' ).
  lo_out_key->write( data = lv_secret ).
  DATA(lx_key) = lo_out_key->get_buffer( ).

  DATA(lo_out_body) = cl_abap_conv_out_ce=>create( encoding = 'UTF-8' ).
  lo_out_body->write( data = lv_raw_body ).
  DATA(lx_body) = lo_out_body->get_buffer( ).

  DATA lv_computed_sig TYPE string.

  TRY.
      DATA(lo_hmac) = cl_abap_hmac=>get_instance(
                        if_algorithm = 'SHA256'
                        if_key       = lx_key ).
      lo_hmac->update( if_data = lx_body ).
      lo_hmac->final( IMPORTING ef_hmacb64string = lv_computed_sig ).

    CATCH cx_abap_message_digest INTO DATA(lx).
      MESSAGE lx->get_text( ) TYPE 'E'.
      RETURN.
  ENDTRY.

  IF lv_computed_sig = lv_received_sig.
    WRITE: / 'Signature valid — process webhook'.
  ELSE.
    WRITE: / 'Signature mismatch — reject request'.
    WRITE: / 'Received: ', lv_received_sig.
    WRITE: / 'Computed: ', lv_computed_sig.
  ENDIF.

See also

Comments