Nordea PlusGirot PG HMAC SAP ABAP

License

This code is currently in beta.

Give it a testspin.

Idea for usage.

Store the key in a file at a USB stick with encryption.

Store the key at yet another USB stick with encryption as a backup.

Attach the USB stick to the pc.

Makue sure the files to be sent to the bank can be accessed from the pc being used to execute the sap gui.

Typically the files to be sent to the bank are written to the file system of the SAP server.

There are a number of options to access the files:

- ftp/sftp client to download the files from the SAP server.

- Access the files using common internet file system (CIFS)

- expose the fileshare at SAP server using samba if on unix or as a windows file share.

- let the sap server write the files to a windows file share (if on linux a file system mounted as cifs)

- use zapcmd, a norton comander clone for SAP to download the files

- use transaction CG3Y to download the file

Use the ZBGC_HMAC report to tamper protect (seal) the files.

Veryfy that the files are correct after adding the seal but before sending the files to the bank.

A VPN to the bank should be established, to send the files to the bank using a ftp client at the pc.

When done, store the USB sticks in a safe.

If you need an automated solution, consultants can help to customize to the specific requirements.

*&---------------------------------------------------------------------*

*& Report ZPG_HMAC

*&

*&---------------------------------------------------------------------*

*&

*&

*&---------------------------------------------------------------------*

REPORT zpg_hmac.

* Author: Otto Frost @ Nuport

TYPE-POOLS abap.

SELECTION-SCREEN COMMENT /1(50) comm1 MODIF ID mg1.

SELECTION-SCREEN ULINE.

SELECTION-SCREEN SKIP.

PARAMETERS p_inf TYPE file_name LOWER CASE.

PARAMETERS p_keyf TYPE file_name LOWER CASE.

PARAMETERS p_outf TYPE file_name LOWER CASE.

PARAMETERS p_trans TYPE c AS CHECKBOX.

PARAMETERS p_test TYPE c AS CHECKBOX.

DATA i_filetable TYPE filetable.

DATA w_rc TYPE i.

DATA w_pathfd TYPE string.

DATA w_pathd TYPE string.

DATA w_filenamed TYPE string.

AT SELECTION-SCREEN OUTPUT.

comm1 = 'Nordea PlusGirot PG HMAC'.

LOOP AT SCREEN.

IF screen-group1 = 'MG1'.

screen-intensified = '1'.

MODIFY SCREEN.

ENDIF.

ENDLOOP.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_inf.

REFRESH i_filetable.

CALL METHOD cl_gui_frontend_services=>file_open_dialog

* EXPORTING

* WINDOW_TITLE =

* DEFAULT_EXTENSION =

* DEFAULT_FILENAME =

* FILE_FILTER =

* WITH_ENCODING =

* INITIAL_DIRECTORY =

* MULTISELECTION =

CHANGING

file_table = i_filetable

rc = w_rc

* USER_ACTION =

* FILE_ENCODING =

* EXCEPTIONS

* FILE_OPEN_DIALOG_FAILED = 1

* CNTL_ERROR = 2

* ERROR_NO_GUI = 3

* NOT_SUPPORTED_BY_GUI = 4

* others = 5

.

IF sy-subrc <> 0.

* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

ENDIF.

READ TABLE i_filetable INDEX 1 INTO p_inf.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_keyf.

REFRESH i_filetable.

CALL METHOD cl_gui_frontend_services=>file_open_dialog

* EXPORTING

* WINDOW_TITLE =

* DEFAULT_EXTENSION =

* DEFAULT_FILENAME =

* FILE_FILTER =

* WITH_ENCODING =

* INITIAL_DIRECTORY =

* MULTISELECTION =

CHANGING

file_table = i_filetable

rc = w_rc

* USER_ACTION =

* FILE_ENCODING =

* EXCEPTIONS

* FILE_OPEN_DIALOG_FAILED = 1

* CNTL_ERROR = 2

* ERROR_NO_GUI = 3

* NOT_SUPPORTED_BY_GUI = 4

* others = 5

.

IF sy-subrc <> 0.

* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

ENDIF.

READ TABLE i_filetable INDEX 1 INTO p_keyf.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_outf.

w_pathfd = p_outf.

CALL METHOD cl_gui_frontend_services=>file_save_dialog

* EXPORTING

* WINDOW_TITLE =

* DEFAULT_EXTENSION =

* DEFAULT_FILE_NAME =

* WITH_ENCODING =

* FILE_FILTER =

* INITIAL_DIRECTORY =

* PROMPT_ON_OVERWRITE = 'X'

CHANGING

filename = w_filenamed

path = w_pathd

fullpath = w_pathfd

* USER_ACTION = W_USER_ACTION

* FILE_ENCODING =

* EXCEPTIONS

* CNTL_ERROR = 1

* ERROR_NO_GUI = 2

* NOT_SUPPORTED_BY_GUI = 3

* others = 4

.

IF sy-subrc <> 0.

* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

ENDIF.

p_outf = w_pathfd.

INITIALIZATION.

START-OF-SELECTION.

IF p_test = abap_true.

PERFORM test.

ELSE.

PERFORM pg_hmac.

ENDIF.

*----------------------------------------------------------------------*

* CLASS zcl_pg_hmac DEFINITION

*----------------------------------------------------------------------*

*

*----------------------------------------------------------------------*

CLASS zcl_pg_hmac DEFINITION.

PUBLIC SECTION.

METHODS test.

METHODS createsignature IMPORTING

value(keyfile) TYPE string

value(infile) TYPE string

value(outfile) TYPE string

value(date) TYPE string

value(ls) TYPE string

value(transmission) TYPE string.

PROTECTED SECTION.

* package section.

PRIVATE SECTION.

TYPES t_linex(512) TYPE x.

CONSTANTS co_empty TYPE string VALUE ''.

METHODS normalize IMPORTING value(s) TYPE string RETURNING value(r) TYPE string.

METHODS pg_hmac IMPORTING value(indata) TYPE string value(keyhex) TYPE string RETURNING value(r) TYPE string.

METHODS transmissionheader IMPORTING value(date) TYPE string RETURNING value(r) TYPE string.

METHODS transmissiontrailer RETURNING value(r) TYPE string.

METHODS fileheader IMPORTING value(date) TYPE string RETURNING value(r) TYPE string.

METHODS filetrailer IMPORTING value(kvv) TYPE string value(mac) TYPE string value(date) TYPE string RETURNING value(r) TYPE string.

METHODS xtab2xstring IMPORTING value(tab) TYPE ANY TABLE value(length) TYPE i RETURNING value(r) TYPE xstring.

ENDCLASS. "zcl_pg_hmac DEFINITION

*----------------------------------------------------------------------*

* CLASS zcl_pg_hmac IMPLEMENTATION

*----------------------------------------------------------------------*

*

*----------------------------------------------------------------------*

CLASS zcl_pg_hmac IMPLEMENTATION.

METHOD normalize.

DATA l_s TYPE string.

DATA l_notallowedchars TYPE string.

DATA cc TYPE REF TO cl_abap_conv_in_ce.

DATA l_repl_char TYPE char1.

DATA l_external_codepage TYPE string VALUE 'ISO-8859-1'.

DATA l_sap_codepage TYPE cpcodepage.

DATA l_abap_encoding TYPE abap_encoding.

DATA l_space TYPE string.

CONCATENATE space space INTO l_space SEPARATED BY space .

CALL FUNCTION 'SCP_CODEPAGE_BY_EXTERNAL_NAME'

EXPORTING

external_name = l_external_codepage

* KIND = 'H'

IMPORTING

sap_codepage = l_sap_codepage

* EXCEPTIONS

* NOT_FOUND = 1

* OTHERS = 2

.

IF sy-subrc <> 0.

* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

ENDIF.

l_abap_encoding = l_sap_codepage.

l_s = s.

REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf+0(1) IN l_s WITH co_empty. " remove cr

REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf+1(1) IN l_s WITH co_empty. " remove lf

* l_s = me->replace( s = l_s find = 'C9' repl = '40' ). " É --> @

* l_s = me->replace( s = l_s find = 'C5' repl = '5D' ). " Å --> ]

* l_s = me->replace( s = l_s find = 'D6' repl = '5C' ). " Ö --> \

* l_s = me->replace( s = l_s find = 'C4' repl = '5B' ). " Ä --> [

* l_s = me->replace( s = l_s find = 'DC' repl = '5E' ). " Ü --> ^

* l_s = me->replace( s = l_s find = 'E9' repl = '60' ). " é --> `

* l_s = me->replace( s = l_s find = 'E4' repl = '7B' ). " ä --> {

* l_s = me->replace( s = l_s find = 'F6' repl = '7C' ). " ö --> |

* l_s = me->replace( s = l_s find = 'E4' repl = '7D' ). " å --> }

* l_s = me->replace( s = l_s find = 'FC' repl = '7E' ). " ü --> ~

REPLACE ALL OCCURRENCES OF 'É' IN l_s WITH '@'. " C9 40

REPLACE ALL OCCURRENCES OF 'é' IN l_s WITH '`'. " x60 - in addition to BGC

REPLACE ALL OCCURRENCES OF 'Å' IN l_s WITH ']'. " C5 5D

REPLACE ALL OCCURRENCES OF 'Ö' IN l_s WITH '\'. " D6 5C

REPLACE ALL OCCURRENCES OF 'Ä' IN l_s WITH '['. " C4 5B

REPLACE ALL OCCURRENCES OF 'Ü' IN l_s WITH '^'. " DC 5E

REPLACE ALL OCCURRENCES OF 'é' IN l_s WITH '`'. " E9 60

REPLACE ALL OCCURRENCES OF 'ä' IN l_s WITH '{'. " E4 7B

REPLACE ALL OCCURRENCES OF 'ö' IN l_s WITH '|'. " F6 7C

REPLACE ALL OCCURRENCES OF 'å' IN l_s WITH '}'. " E4 7D

REPLACE ALL OCCURRENCES OF 'ü' IN l_s WITH '~'. " FC 7E

l_notallowedchars = '[' && " // start of range

'^' && " // not

l_space && " // space

'\!' && " // exclamation mark

'\"' && " // double quote

'\#' && " // hash

'\$' && " // dollar

'\%' && " // percent

'\&' && " // ampersand

'\''' && " // single quote

'\(' && " // left parenthesis

'\)' && " // right parenthesis

'\*' && " // star

'\+' && " // plus

'\,' && " // comma

'\-' && " // minus

'\.' && " // dot

'\/' && " // slash

'0-9' && " // range 0-9

'\:' && " // colon

'\;' && " // semicolon

'\<' && " // less than

'\=' && " // equals

'\>' && " // greater than

'\?' && " // question mark

'\@' && " // at

'É' && " // E with apostrophe

'A-Z' && " // range A-Z

'\[' && " // left bracket

'\\' && " // back slash

'\]' && " // right bracket

'\^' && " // caret

'_' && " // under score

'\`' && " // back quote

'a-z' && " // range a-z

'\{' && " // left curly brace

'\|' && " // vertical bar

'\}' && " // right curly brace

'\~' && " // tilde

']'. " // end of range

cc = cl_abap_conv_in_ce=>create( input = 'C3' encoding = l_abap_encoding ).

cc->read( IMPORTING data = l_repl_char ).

* WRITE:/ l_notallowedchars.

* WRITE:/ 's ' , space, l_s.

REPLACE ALL OCCURRENCES OF REGEX l_notallowedchars IN l_s WITH l_repl_char.

* WRITE:/ 's ', space, l_s.

r = l_s.

ENDMETHOD. "normalize

METHOD pg_hmac.

DATA lw_alg TYPE string VALUE 'SHA256'. " sha1

DATA lw_key TYPE xstring.

DATA l_x TYPE xstring.

DATA l_conv_out TYPE REF TO cl_abap_conv_out_ce.

DATA l_external_codepage TYPE string VALUE 'ISO-8859-1'.

DATA l_sap_codepage TYPE cpcodepage.

DATA l_abap_encoding TYPE abap_encoding.

CALL FUNCTION 'SCP_CODEPAGE_BY_EXTERNAL_NAME'

EXPORTING

external_name = l_external_codepage

* KIND = 'H'

IMPORTING

sap_codepage = l_sap_codepage

EXCEPTIONS

not_found = 1

OTHERS = 2.

IF sy-subrc <> 0.

MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno

WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.

ENDIF.

l_abap_encoding = l_sap_codepage.

lw_key = keyhex.

l_conv_out = cl_abap_conv_out_ce=>create( encoding = l_abap_encoding ).

l_conv_out->write( data = indata ).

l_x = l_conv_out->get_buffer( ).

cl_abap_hmac=>calculate_hmac_for_raw( EXPORTING if_algorithm = lw_alg if_key = lw_key if_data = l_x

IMPORTING ef_hmacstring = r ).

r = r+0(32).

ENDMETHOD. "pg_hmac

METHOD transmissionheader.

DATA l_date TYPE string.

l_date = date.

DATA l_delivnode TYPE string.

DATA l_password TYPE string.

DATA l_inddeliv TYPE string VALUE '0'.

DATA l_filetype TYPE string.

DATA l_extref TYPE string.

l_extref = date.

DATA l_free TYPE string.

CONCATENATE space space INTO l_free SEPARATED BY space.

DATA l_zero TYPE string VALUE '0'.

WHILE ( strlen( l_delivnode ) < 10 ).

l_delivnode = l_delivnode && l_free.

ENDWHILE.

WHILE ( strlen( l_password ) < 6 ).

l_password = l_password && l_free.

ENDWHILE.

WHILE ( strlen( l_filetype ) < 3 ).

l_filetype = l_filetype && l_free.

ENDWHILE.

r = '%001'

&& l_delivnode

&& l_password

&& l_inddeliv

&& l_filetype

&& l_extref

&& l_free

&& l_zero.

WHILE ( strlen( r ) < 80 ).

r = r && l_free.

ENDWHILE.

ENDMETHOD. "transmissionheader

METHOD transmissiontrailer.

r = '%002'.

ENDMETHOD. "transmissiontrailer

METHOD fileheader.

DATA l_space TYPE string.

DATA l_date TYPE string.

l_date = date.

DATA l_dstnode TYPE string.

DATA l_srcnode TYPE string.

DATA l_extref1 TYPE string.

l_extref1 = l_date.

DATA l_noitems TYPE string.

DATA l_extref2 TYPE string. " customer number

CONCATENATE space space INTO l_space SEPARATED BY space.

WHILE ( strlen( l_dstnode ) < 10 ).

l_dstnode = l_dstnode && l_space.

ENDWHILE.

WHILE ( strlen( l_srcnode ) < 10 ).

l_srcnode = l_srcnode && l_space.

ENDWHILE.

WHILE ( strlen( l_extref1 ) < 7 ).

l_extref1 = l_extref1 && l_space.

ENDWHILE.

WHILE ( strlen( l_noitems ) < 7 ).

l_noitems = l_noitems && l_space.

ENDWHILE.

WHILE ( strlen( l_extref2 ) < 10 ).

l_extref2 = l_extref2 && l_space.

ENDWHILE.

r = '%020' && l_dstnode && l_srcnode && l_extref1 && l_noitems && l_extref2.

* record should be 80 chars padded with space

WHILE ( strlen( r ) < 80 ).

r = r && l_space.

ENDWHILE.

ENDMETHOD. "fileheader

METHOD filetrailer.

DATA l_space TYPE string.

CONCATENATE space space INTO l_space SEPARATED BY space.

DATA l_noitems TYPE string.

WHILE ( strlen( l_noitems ) < 7 ).

l_noitems = l_noitems && l_space.

ENDWHILE.

r = '%022' && l_noitems && kvv && mac.

* record should be 80 chars padded with space

WHILE ( strlen( r ) < 80 ).

r = r && l_space.

ENDWHILE.

ENDMETHOD. "filetrailer

METHOD createsignature.

DATA l_date TYPE string.

DATA l_length TYPE i.

DATA li_data TYPE STANDARD TABLE OF t_linex.

DATA l_linex TYPE t_linex.

DATA l_x TYPE xstring.

DATA l_s TYPE string.

DATA l_bytes TYPE i.

DATA l_conv_in TYPE REF TO cl_abap_conv_in_ce.

DATA l_conv_out TYPE REF TO cl_abap_conv_out_ce.

DATA l_key TYPE string.

DATA l_keyx TYPE xstring.

DATA l_external_codepage TYPE string VALUE 'ISO-8859-1'.

DATA l_sap_codepage TYPE cpcodepage.

DATA l_abap_encoding TYPE abap_encoding.

DATA l_data TYPE string.

DATA l_filedata TYPE string.

DATA l_line_separator_exists TYPE boolean VALUE abap_false.

DATA l_filedata_normalized TYPE string.

DATA l_mac TYPE string.

DATA l_kvv TYPE string.

DATA l_tamperprotectionrecord TYPE string.

DATA l_line_separator TYPE string.

DATA l_fileheader TYPE string.

DATA l_filetrailer TYPE string.

IF date IS INITIAL.

l_date = sy-datum+2(6).

ELSE.

l_date = date.

ENDIF.

REFRESH li_data.

CLEAR l_length.

cl_gui_frontend_services=>gui_upload(

EXPORTING

filename = keyfile

filetype = 'BIN'

has_field_separator = space

read_by_line = abap_false

IMPORTING

filelength = l_length

CHANGING

data_tab = li_data ).

l_x = me->xtab2xstring( tab = li_data length = l_length ).

l_conv_in = cl_abap_conv_in_ce=>create(

encoding = 'UTF-8' input = l_x ).

* the key is hexencoded

* so remove any cr/lf

l_conv_in->read( IMPORTING data = l_s ).

REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf+0(1) IN l_s WITH co_empty. " remove cr

REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf+1(1) IN l_s WITH co_empty. " remove lf

l_key = l_s.

l_keyx = l_key.

REFRESH li_data.

CLEAR l_length.

cl_gui_frontend_services=>gui_upload(

EXPORTING

filename = infile

filetype = 'BIN'

has_field_separator = space

read_by_line = abap_false

IMPORTING

filelength = l_length

CHANGING

data_tab = li_data ).

l_x = me->xtab2xstring( tab = li_data length = l_length ).

CALL FUNCTION 'SCP_CODEPAGE_BY_EXTERNAL_NAME'

EXPORTING

external_name = l_external_codepage

* KIND = 'H'

IMPORTING

sap_codepage = l_sap_codepage

EXCEPTIONS

not_found = 1

OTHERS = 2.

IF sy-subrc <> 0.

* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

ENDIF.

l_abap_encoding = l_sap_codepage.

l_conv_in = cl_abap_conv_in_ce=>create(

encoding = l_abap_encoding input = l_x ).

l_conv_in->read( IMPORTING data = l_s ).

l_data = l_s.

l_fileheader = me->fileheader( date = l_date ).

* l_filedata = me->sealopeningrecord( date = l_date ) && ls && l_data.

l_filedata = l_data.

IF ( substring( val = l_filedata off = ( strlen( l_filedata ) - 1 ) len = 1 ) = cl_abap_char_utilities=>cr_lf+0(1) )

OR ( substring( val = l_filedata off = ( strlen( l_filedata ) - 1 ) len = 1 ) = cl_abap_char_utilities=>cr_lf+1(1) ).

l_line_separator_exists = abap_true.

ENDIF.

l_filedata_normalized = me->normalize( l_filedata ).

* l_conv_out = cl_abap_conv_out_ce=>create( encoding = l_abap_encoding ).

* l_conv_out->write( data = l_filedata_normalized ).

* l_x = l_conv_out->get_buffer( ).

l_mac = me->pg_hmac( indata = l_filedata_normalized keyhex = l_key ).

l_kvv = me->pg_hmac( indata = '00000000' keyhex = l_key ).

* l_tamperprotectionrecord = me->tamperprotectionrecord( kvv = l_kvv mac = l_mac date = l_date ).

l_filetrailer = me->filetrailer( kvv = l_kvv mac = l_mac date = l_date ).

IF l_line_separator_exists = abap_false.

l_line_separator = cl_abap_char_utilities=>cr_lf.

ENDIF.

l_filedata = l_fileheader && ls && l_filedata && l_line_separator && l_filetrailer && ls.

IF transmission = abap_true.

l_filedata = me->transmissionheader( l_date ) && ls && l_filedata && me->transmissiontrailer( ) && ls.

ENDIF.

* make sure to have cr/lf as line separator this is what bgc expects

REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf+0(1) IN l_filedata WITH co_empty. " remove all cr

REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf+1(1) IN l_filedata WITH cl_abap_char_utilities=>cr_lf. " add cr in front of lf

l_conv_out = cl_abap_conv_out_ce=>create( encoding = l_abap_encoding ).

l_conv_out->write( data = l_filedata ).

l_x = l_conv_out->get_buffer( ).

CALL FUNCTION 'LXE_COMMON_XSTRING_FILE_EXPORT'

EXPORTING

xstring = l_x

filename = outfile.

*

*CALL METHOD cl_gui_frontend_services=>gui_download

* EXPORTING

* bin_filesize =

* filename =

* filetype = 'BIN'

** append = SPACE

** write_field_separator = SPACE

** header = '00'

** trunc_trailing_blanks = SPACE

** write_lf = 'X'

** col_select = SPACE

** col_select_mask = SPACE

** dat_mode = SPACE

** confirm_overwrite = SPACE

** no_auth_check = SPACE

** codepage = SPACE

** ignore_cerr = ABAP_TRUE

** replacement = '#'

** write_bom = SPACE

** trunc_trailing_blanks_eol = 'X'

** wk1_n_format = SPACE

** wk1_n_size = SPACE

** wk1_t_format = SPACE

** wk1_t_size = SPACE

** show_transfer_status = 'X'

** fieldnames =

** write_lf_after_last_line = 'X'

** IMPORTING

** filelength =

* changing

* data_tab =

** EXCEPTIONS

** file_write_error = 1

** no_batch = 2

** gui_refuse_filetransfer = 3

** invalid_type = 4

** no_authority = 5

** unknown_error = 6

** header_not_allowed = 7

** separator_not_allowed = 8

** filesize_not_allowed = 9

** header_too_long = 10

** dp_error_create = 11

** dp_error_send = 12

** dp_error_write = 13

** unknown_dp_error = 14

** access_denied = 15

** dp_out_of_memory = 16

** disk_full = 17

** dp_timeout = 18

** file_not_found = 19

** dataprovider_exception = 20

** control_flush_error = 21

** not_supported_by_gui = 22

** error_no_gui = 23

** others = 24

* .

*IF sy-subrc <> 0.

** MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

** WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

*ENDIF.

*

ENDMETHOD. "tamperprotectionrecord

METHOD xtab2xstring.

DATA l_linex TYPE t_linex.

DATA l_x TYPE xstring.

DATA l_bytes TYPE i.

DATA l_length TYPE i.

l_length = length.

LOOP AT tab INTO l_linex.

IF l_length > 512.

l_bytes = 512.

ELSE.

l_bytes = l_length.

ENDIF.

CONCATENATE l_x l_linex(l_bytes) INTO l_x IN BYTE MODE.

l_length = l_length - 512.

ENDLOOP.

r = l_x.

ENDMETHOD. "xtab2xstring

METHOD test.

DATA indata TYPE string.

DATA key TYPE string VALUE '1234567890ABCDEF1234567890ABCDEF'.

DATA mac TYPE string.

DATA l_s TYPE string.

DATA l_normalized TYPE string.

DATA l_space TYPE string.

CONCATENATE space space INTO l_space SEPARATED BY space.

indata = 'AAAÅÄÖÜåäöü'.

indata = me->normalize( indata ).

**********

* Test 1A

**********

mac = me->pg_hmac( indata = '00000000' keyhex = key ).

IF to_upper( mac ) = 'FF365893D899291C3BF505FB3175E880' .

WRITE:/ 'Test 1A ok' .

ELSE .

WRITE:/ 'Test 1A err' .

l_s = to_upper( mac ).

WRITE:/ l_s.

WRITE:/ 'FF365893D899291C3BF505FB3175E880'.

ENDIF.

**********

* Test 1B

**********

indata = '00000' && cl_abap_char_utilities=>cr_lf &&

'0000' && cl_abap_char_utilities=>cr_lf &&

'000' && cl_abap_char_utilities=>cr_lf &&

'00' && cl_abap_char_utilities=>cr_lf && '0' && cl_abap_char_utilities=>cr_lf.

l_normalized = me->normalize( indata ).

* WRITE:/ '1B', space, indata.

* WRITE:/ '1B', space, l_normalized.

mac = me->pg_hmac( indata = l_normalized keyhex = key ).

IF to_upper( mac ) = '9BA94363739D45256DF4B6FA3B9DE1CD'.

WRITE:/ 'Test 1B ok'.

ELSE.

WRITE:/ 'Test 1B err'.

ENDIF.

**********

* Test 1C

**********

indata =

'110009912346071215LEVERANTZRSBETALNINGAR 2'

&& cl_abap_char_utilities=>cr_lf

&& '10000000C0000000000000000000000000000000000000000000000000000000000000002'

&& cl_abap_char_utilities=>cr_lf

&& '10000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002'

&& cl_abap_char_utilities=>cr_lf

&& '10000000C002'

&& cl_abap_char_utilities=>cr_lf.

l_normalized = me->normalize( indata ).

* WRITE:/ '1C', space, indata.

* WRITE:/ '1C', space, l_normalized.

mac = me->pg_hmac( indata = l_normalized keyhex = key ).

IF to_upper( mac ) = '826CA6CBA33F7E1D3CC9161A0956A35B'.

WRITE:/ 'Test 1C ok'.

ELSE.

WRITE:/ 'Test 1C err'.

ENDIF.

**********

* Test 2A

**********

indata = l_space && '!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~' && cl_abap_char_utilities=>cr_lf.

l_normalized = me->normalize( indata ).

* WRITE:/ '2A', space, indata.

* WRITE:/ '2A', space, l_normalized.

* DATA x TYPE string.

* x = l_space && '!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.

* IF x = l_normalized.

* WRITE:/ '2A normalized ok'.

* ELSE.

* WRITE:/ '2A normalized not ok'.

* ENDIF.

mac = me->pg_hmac( indata = l_normalized keyhex = key ).

* WRITE:/ mac.

IF to_upper( mac ) = '9473EDFCAA8CD2434D6D76ABFFC991BD'.

WRITE:/ 'Test 2A ok'.

ELSE.

WRITE:/ 'Test 2A err'.

ENDIF.

**********

* Test 2B

**********

indata = 'õÕ^~@`!"#$%&''(' && cl_abap_char_utilities=>horizontal_tab && ')*+,-./§½' && cl_abap_char_utilities=>cr_lf.

l_normalized = me->normalize( indata ).

* WRITE:/ '2B', space, indata.

* WRITE:/ '2B', space, l_normalized.

* DATA x TYPE string.

* x = l_space && '!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.

* IF x = l_normalized.

* WRITE:/ '2A normalized ok'.

* ELSE.

* WRITE:/ '2A normalized not ok'.

* ENDIF.

mac = me->pg_hmac( indata = l_normalized keyhex = key ).

IF mac = to_upper( '20956E44B404C4085446139B2B952D77' ).

WRITE:/ 'Test 2B ok'.

ELSE.

WRITE:/ 'Test 2B err'.

ENDIF.

**********

* Test 2C

**********

indata = 'ÄÖÅäöå' && cl_abap_char_utilities=>cr_lf.

l_normalized = me->normalize( indata ).

mac = me->pg_hmac( indata = l_normalized keyhex = key ).

IF mac = to_upper( '515704694958361678194D51850FF157' ).

WRITE:/ 'Test 2C ok'.

ELSE.

WRITE:/ 'Test 2C err'.

ENDIF.

ENDMETHOD. "test

ENDCLASS. "zcl_pg_hmac IMPLEMENTATION

*&---------------------------------------------------------------------*

*& Form test

*&---------------------------------------------------------------------*

* text

*----------------------------------------------------------------------*

FORM test.

DATA: lo_hmac TYPE REF TO cl_abap_hmac.

DATA: lf_hmac_string TYPE string.

DATA: lw_alg TYPE string VALUE 'sha256'. " sha1

DATA: lw_key TYPE xstring VALUE 'AAAA'.

lo_hmac = cl_abap_hmac=>get_instance( if_algorithm = 'sha256' if_key = lw_key ).

" update HMAC with input

lo_hmac->update( if_data = '010203' ).

" finalise hmac

lo_hmac->final( ).

" String

lf_hmac_string = lo_hmac->to_string( ).

* WRITE:/ lf_hmac_string .

" Base64 representation

lf_hmac_string = lo_hmac->to_base64( ).

* WRITE:/ lf_hmac_string .

DATA l_pg_hmac TYPE REF TO zcl_pg_hmac.

CREATE OBJECT l_pg_hmac.

l_pg_hmac->test( ).

ENDFORM. "test

*&---------------------------------------------------------------------*

*& Form pg_hmac

*&---------------------------------------------------------------------*

* text

*----------------------------------------------------------------------*

FORM pg_hmac.

DATA l_pg_hmac TYPE REF TO zcl_pg_hmac.

DATA l_keyfile TYPE string.

DATA l_outfile TYPE string.

DATA l_infile TYPE string.

DATA l_date TYPE string.

DATA l_ls TYPE string.

DATA l_transmission TYPE string.

l_keyfile = p_keyf.

l_infile = p_inf.

l_outfile = p_outf.

l_date = sy-datum+2(6).

l_ls = cl_abap_char_utilities=>cr_lf.

l_transmission = p_trans.

CREATE OBJECT l_pg_hmac.

l_pg_hmac->createsignature( keyfile = l_keyfile infile = l_infile outfile = l_outfile date = l_date ls = l_ls transmission = l_transmission ).

ENDFORM. "pg_hmac

* methods replace importing value(s) type string value(find) type xstring value(repl) type xstring returning value(r) type string.

*method replace.

* data l_s type string.

* DATA cc TYPE REF TO cl_abap_conv_in_ce.

* DATA l_find_char TYPE char1.

* DATA l_repl_char TYPE char1.

* l_s = s.

* cc = cl_abap_conv_in_ce=>create( input = find encoding = 'UTF-8' ).

* cc->read( IMPORTING data = l_find_char ).

* cc = cl_abap_conv_in_ce=>create( input = repl encoding = 'UTF-8' ).

* cc->read( IMPORTING data = l_repl_char ).

* REPLACE ALL OCCURRENCES OF l_find_char IN l_s WITH l_repl_char.

* r = l_s.

*endmethod.