Monday 21 October 2019

File encryption and decryption using ABAP

This blog post is intended to give an overall idea on file encryption and decryption process using a key in ABAP. By end of this blog post, we will be able to learn ‘how to encrypt a text file or data using a key and in the end we will also able know “how to decrypt the encrypted data using the same key”.

You can also try this blog post if you are getting run time error “CX_SEC_SXML_ENCRYPT_ERROR” in class “CL_SEC_SXML_WRITER” during decryption of the data.

Requirement:


Recently I came across a requirement where we have to encrypt the generated file while placing it in application server( t-code – AL11 ). The third party middle-ware reads the file from SAP application server and sends it to the target system. To avoid the misutilization of any sensitive data by the third party middle-ware, we have encoded the file using a key(key is generated every-time, hence the key is unique always) and the key has been shared with the concerned person of the target system via e-mail. I thought to share this so that it can be helpful to someone.

The mechanism mentioned above is called “Symmetric Key Encryption” in Cryptography(as the same key is used in the encryption and decryption process, hence the name “Symmetric”).

We will be using the class “CL_SEC_SXML_WRITER” for encryption and decryption process. The class “CL_SEC_SXML_WRITER” contains 3 algorithm for encryption and decryption. Below are the details.

◈ AES Algorithm 128 Bit
◈ AES Algorithm 192 Bit
◈ AES Algorithm 256 Bit

These are the algorithms been followed universally, so even if the target system is a non-SAP system, the encrypted file from SAP can be decrypted using the correct key in the target system and vice-versa(and yes, the algorithm used during encryption process should be used during decryption process, of course).

So, let’s demonstrate the complete process of encryption and decryption using a key in ABAP. We will be creating 2 different program. Below are the details.

◈ Program 1: We will create a program with name “ZFILE_ENCRYPTION”. In this program we will do the following task:-

     ◈ Creation of the file which needs to be encrypted
     ◈ Generation of the “KEY” which will be used in encryption process
     ◈ Encrypting the file with the generated key
     ◈ Sharing the key with intended person via e-mail

Program 2: We will create another program with name “ZFILE_DECRYPTION” for decrypting  the encrypted file using the generated key. For the scenario that I have worked on(mentioned above) , this program was not required by me as the decryption has been done in the target system. However, this program can be useful if you are in the receiving end of the encoded file. For demonstration purpose, I will be explaining it in this blog post.The program will do the following task:-

     ◈ Reading the encoded file from application server(t-code : AL11)
     ◈ Decrypting the encode file using the key been received on email
     ◈ Downloading the decrypted file(in readable format)

Let’s walks through the both program one after one in details.

Program 1: ZFILE_ENCRYPTION – Encrypting the file using a key


Note: Complete source code has been provided in the end of this blog post.

Step 1: Preparing the text file which needs to be encrypted

This step is pretty straight forward. For demonstration purpose, I will be uploading a text file from presentation server. Below is the sample code for file upload. The data object “LV_DATA” contains the desire data which needs to be encrypted.

You can also generate the file string programmatically instead of uploading it from presentation server.

DATA:
      lt_data   TYPE string_t,
      lv_data   TYPE string,
      lv_str    TYPE string.
    CALL METHOD cl_gui_frontend_services=>gui_upload
      EXPORTING
        filename                = p_path
        filetype                = 'ASC'
      CHANGING
        data_tab                = lt_data
      EXCEPTIONS
        file_open_error         = 1
        file_read_error         = 2
        no_batch                = 3
        gui_refuse_filetransfer = 4
        invalid_type            = 5
        no_authority            = 6
        unknown_error           = 7
        bad_data_format         = 8
        header_not_allowed      = 9
        separator_not_allowed   = 10
        header_too_long         = 11
        unknown_dp_error        = 12
        access_denied           = 13
        dp_out_of_memory        = 14
        disk_full               = 15
        dp_timeout              = 16
        not_supported_by_gui    = 17
        error_no_gui            = 18
        OTHERS                  = 19.
    LOOP AT lt_data INTO lv_str.
      IF lv_data  IS INITIAL.
        lv_data = lv_str.
      ELSE.
        lv_data = |{ lv_data }{ cl_abap_char_utilities=>newline }{ lv_str }|.
      ENDIF.
    ENDLOOP.

Source file is looking like this:

SAP ABAP Certifications, SAP ABAP Learning, SAP ABAP Guides, SAP ABAP Online Exam

Step 2: Generating the key for encryption

We have to generate the “KEY” which will be used in encryption process. “GENERATE_KEY” method of the class “CL_SEC_SXML_WRITER” can be used for that. The method always returns a unique “XSTRING” every-time.

DATA:
  lv_key TYPE xstring.

lv_key = cl_sec_sxml_writer=>generate_key( algorithm = cl_sec_sxml_writer=>co_aes128_algorithm  ).

Important: Please do not use the class “CL_ABAP_CONV_OUT_CE” for generation of the “KEY” for encryption. If you do so, you the program will dump if the encryption and decryption program are different. Below is the screenshot of the dump.

SAP ABAP Certifications, SAP ABAP Learning, SAP ABAP Guides, SAP ABAP Online Exam

Step 3: Encrypting the file using the key

Till now we have prepared the data which needs to be encrypted and the key is also generated which will be used for encryption. For that we have to use the method “ENCRYPT” of class “CL_SEC_SXML_WRITER”. The arguments of the method is the key which been derived in the above step, the data(which needs to be encrypted) in “XSTRING” format and the name of the algorithm which needs to be used. Below is the sample code

DATA:
  lv_data_xstr TYPE xstring,
  lv_msg       TYPE xstring.

"Converting the data - From string format to xstring. You can use any other method or function module which converts the string to xstring format
lv_data_xstr = cl_bcs_convert=>string_to_xstring(
  iv_string = lv_data    " Input data
).

"Encrypt the data using the key
cl_sec_sxml_writer=>encrypt(
  EXPORTING
    plaintext =  v_data
    key       =  v_key
    algorithm =  cl_sec_sxml_writer=>co_aes128_algorithm
  IMPORTING
    ciphertext = lv_msg ).

Step 4: Saving the encrypted file in application server

Now that we have encrypted the file, we have to save the encrypted file in application server(t-code: AL11). But, please note the encrypted file in “XSTRING” format. So, how to transfer the “XSTRING” to application sever. Yes, you are right, just assign the “XSTRING” to a “STRING” data object and transfer the string to the application server. As simple as that. Below is the code snipet.

DATA:
 lv_str TYPE string.

CONSTANTS:
 lc_filepath TYPE string VALUE '\\PGRDEV\sapmnt\trans\file_after_encryption.txt"

lv_str = lv_msg.

"Split at 132 position
CALL FUNCTION 'CONVERT_STRING_TO_TABLE'
  EXPORTING
    i_string         = lv_str
    i_tabline_length = 132
  TABLES
    et_table         = lt_str.

OPEN DATASET lc_filepath IN TEXT MODE FOR OUTPUT ENCODING DEFAULT.

"Loop at all the line
LOOP AT lt_str INTO lv_str.
  TRANSFER lv_str TO lc_filepath.
ENDLOOP.

CLOSE DATASET lc_filepath.

Encrypted file in AL11 looks like below. Now the data is been encrypted using the key.

SAP ABAP Certifications, SAP ABAP Learning, SAP ABAP Guides, SAP ABAP Online Exam

Step 5: Sending generated key with concerned person via email.

There are many blog post and thread available for explaining how to send email in ABAP. So, I am not covering that in this blog post. However, you can find the code in the bottom section of this blog post.

Program 2: ZFILE_DECRYPTION : Decrypting the file using the generated key


Note: Complete source code has been provided in the end of this blog post.

Step 1: Reading the encrypted file from application server

We are reading the encrypted file from application server(t-code: AL11) using “OPEN DATASET”.

DATA:
  lv_str  TYPE string,
  lv_data TYPE string.

CONSTANTS:
 lc_filepath TYPE string VALUE '\\PGRDEV\sapmnt\trans\file_after_encryption.txt'.

OPEN DATASET lc_filepath FOR INPUT IN TEXT MODE ENCODING DEFAULT.

DO.
  READ DATASET lc_filepath INTO lv_str.
  IF sy-subrc NE 0.
    EXIT.
  ENDIF.
  lv_data = |{ lv_data }{ lv_str }|.
ENDDO.

Step 2: Decrypting the file using the key

Now that we have read the encrypted data from application server(t-code: AL11) and we have the received the generated key(the key is received over email – refer to Step 5 of Program 1 above), we have to decrypt the data using method “DECRYPT” of class “CL_SEC_SXML_WRITER” . The methods has the arguments for “encrypted data”, “the key which was used during encryption”, “the algorithm which was used during “encryption”.

The data object “LV_DATA” has the encrypted data(derived from above step). The data type the data object is “string”. We have to assign it to a data object  of type “XSTRING”. Then we can decrypt the data. Below source code for reference

    DATA:
      lt_binary TYPE STANDARD TABLE OF x255.

    DATA:
      lv_message_decrypted TYPE xstring,
      lv_str               TYPE string,
      lv_key               TYPE xstring,
      lv_data_xstr         TYPE xstring,
      lv_data_text         TYPE string, 
      lv_length            TYPE i.

    "Received key - As already discussed the key has been shared with the cocern person. The key has been assign here so that we can decrypt the file
    lv_key = '38451B52187A3A4AECA26B27AE79D147'
    
   "Assign the encrypted data derived in previous step to to xstring data object so that the same can be decrypted
   lv_data_xstr = lv_data.

    "Decrypt message
    cl_sec_sxml_writer=>decrypt(
      EXPORTING
        ciphertext = lv_data_xtr
        key       =  lv_key
        algorithm =  cl_sec_sxml_writer=>co_aes128_algorithm
      IMPORTING
        plaintext =  lv_message_decrypted ).
      
    "LV_MESSAGE_DECRYPTED data object now contains the decrypted data in xstring format. So, to make as human readable, we have to convert the xstring to string.

    "Convert xstring to binary
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        buffer        = lv_message_decrypted
      IMPORTING
        output_length = lv_length
      TABLES
        binary_tab    = lt_binary.

    "Binary to string
    CALL FUNCTION 'SCMS_BINARY_TO_STRING'
      EXPORTING
        input_length = lv_length
      IMPORTING
        text_buffer  = lv_data_text
      TABLES
        binary_tab   = lt_binary
      EXCEPTIONS
        failed       = 1
        OTHERS       = 2.

Now that you have got the decrypted data, you can process it or can download to presentation server as per the requirement. I have download the file to the presentation server for demonstration. Please refer to attached source code file for details.

Decrypted file looks like below

SAP ABAP Certifications, SAP ABAP Learning, SAP ABAP Guides, SAP ABAP Online Exam

Conclusion:


So, in this blog post we are able to encrypt the data/text file using a key(the key was generated during run-time). The key has been shared with the concerned person over e-mail. A separate decryption program has been created which decrypts the data using the key been shared.

Hope this blog post will help someone.

Complete source code has been given below for both the program – encryption and decryption

Prorgram: ZFILE_ENCRYPTION


*&---------------------------------------------------------------------*
*& Report  ZFILE_ENCRYPTION
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT zfile_encryption.

PARAMETERS:
  p_path TYPE string OBLIGATORY LOWER CASE.

*----------------------------------------------------------------------*
*       CLASS lcl_encryption DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_encryption DEFINITION.

  PUBLIC SECTION.

    CLASS-METHODS:
      select_file,
      upload_file
        RAISING cx_bcs,
      encrypt_file,
      send_email.

    CONSTANTS:
      c_filepath TYPE string VALUE '\\PGRDEV\sapmnt\trans\encrypted_file.txt'.

  PRIVATE SECTION.

    CLASS-DATA:
      v_data TYPE xstring,
      v_key  TYPE xstring.

ENDCLASS.                    "lcl_encryption DEFINITION

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_path.

  "Select file
  lcl_encryption=>select_file( ).

START-OF-SELECTION.

  "Upload file
  lcl_encryption=>upload_file( ).

  "Encrypted file
  lcl_encryption=>encrypt_file( ).

*----------------------------------------------------------------------*
*       CLASS lcl_test IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_encryption IMPLEMENTATION.

  METHOD select_file.

    DATA:
      lt_file TYPE filetable,
      ls_file TYPE file_table,
      lv_rc   TYPE i.

    CALL METHOD cl_gui_frontend_services=>file_open_dialog
      CHANGING
        file_table              = lt_file
        rc                      = lv_rc
      EXCEPTIONS
        file_open_dialog_failed = 1
        cntl_error              = 2
        error_no_gui            = 3
        not_supported_by_gui    = 4
        OTHERS                  = 5.

    IF sy-subrc EQ 0.

      READ TABLE lt_file INTO ls_file INDEX 1.

      IF sy-subrc EQ 0.
        p_path = ls_file.
      ENDIF.

    ENDIF.

  ENDMETHOD.                    "upload_file

  METHOD upload_file.

    DATA:
      lt_data   TYPE string_t,
      lv_data   TYPE string,
      lv_str    TYPE string.

    CLEAR v_data.

    CALL METHOD cl_gui_frontend_services=>gui_upload
      EXPORTING
        filename                = p_path
        filetype                = 'ASC'
      CHANGING
        data_tab                = lt_data
      EXCEPTIONS
        file_open_error         = 1
        file_read_error         = 2
        no_batch                = 3
        gui_refuse_filetransfer = 4
        invalid_type            = 5
        no_authority            = 6
        unknown_error           = 7
        bad_data_format         = 8
        header_not_allowed      = 9
        separator_not_allowed   = 10
        header_too_long         = 11
        unknown_dp_error        = 12
        access_denied           = 13
        dp_out_of_memory        = 14
        disk_full               = 15
        dp_timeout              = 16
        not_supported_by_gui    = 17
        error_no_gui            = 18
        OTHERS                  = 19.

    LOOP AT lt_data INTO lv_str.

      IF lv_data  IS INITIAL.
        lv_data = lv_str.
      ELSE.
        lv_data = |{ lv_data }{ cl_abap_char_utilities=>newline }{ lv_str }|.
      ENDIF.

    ENDLOOP.

    "Convert to xstring
    v_data = cl_bcs_convert=>string_to_xstring(
      iv_string = lv_data    " Input data
     ).

  ENDMETHOD.                    "upload_file

  METHOD encrypt_file.

    DATA:
      lt_str TYPE string_t,
      lv_str TYPE string,
      lv_msg TYPE xstring.

    v_key = cl_sec_sxml_writer=>generate_key( algorithm = cl_sec_sxml_writer=>co_aes128_algorithm  ).

    "encrypt using AES256
    cl_sec_sxml_writer=>encrypt(
      EXPORTING
        plaintext =  v_data
        key       =  v_key
        algorithm =  cl_sec_sxml_writer=>co_aes128_algorithm
      IMPORTING
        ciphertext = lv_msg ).

    lv_str = lv_msg.

    "Split at 132 position
    CALL FUNCTION 'CONVERT_STRING_TO_TABLE'
      EXPORTING
        i_string         = lv_str
        i_tabline_length = 132
      TABLES
        et_table         = lt_str.

    OPEN DATASET c_filepath IN TEXT MODE FOR OUTPUT ENCODING DEFAULT.

    "Loop at all the line
    LOOP AT lt_str INTO lv_str.
      TRANSFER lv_str TO c_filepath.
    ENDLOOP.

    CLOSE DATASET c_filepath.

    "Send email
    send_email( ).

    MESSAGE 'File saved in AL11' TYPE 'I'.

  ENDMETHOD.                    "encrypt_file

  METHOD send_email.

    CONSTANTS:
      lc_subject TYPE so_obj_des VALUE 'Encryption key',
      lc_raw     TYPE char03     VALUE 'RAW'.

    DATA:
      lr_send_request  TYPE REF TO cl_bcs,
      lr_bcs_exception TYPE REF TO cx_bcs,
      lr_recipient     TYPE REF TO if_recipient_bcs,
      lr_sender        TYPE REF TO cl_sapuser_bcs,
      lr_document      TYPE REF TO cl_document_bcs.

    DATA:
      lv_mlrec         TYPE so_obj_nam,
      lv_sent_to_all   TYPE os_boolean,
      lv_email         TYPE adr6-smtp_addr,
      lv_subject       TYPE so_obj_des,
      lv_str           TYPE string,
      lv_text          TYPE bcsy_text.

    TRY.

        "Create send request
        lr_send_request = cl_bcs=>create_persistent( ).

        "Email FROM...
        lr_sender = cl_sapuser_bcs=>create( sy-uname ).

        "Add sender to send request
        CALL METHOD lr_send_request->set_sender
          EXPORTING
            i_sender = lr_sender.

        "Email TO...
        lv_email = 'testing@testing.com'.

        lr_recipient = cl_cam_address_bcs=>create_internet_address( lv_email ).

        "Add recipient to send request
        CALL METHOD lr_send_request->add_recipient
          EXPORTING
            i_recipient = lr_recipient
            i_express   = 'X'.

        "Email BODY
        lv_str = |Encryption key :{ v_key }|.

        APPEND lv_str TO lv_text.

        lr_document = cl_document_bcs=>create_document(
            i_type    = lc_raw
            i_text    = lv_text
            i_length  = '12'
            i_subject = lc_subject ).

        "Add document to send request
        CALL METHOD lr_send_request->set_document( lr_document ).

        "Send email
        CALL METHOD lr_send_request->send(
          EXPORTING
            i_with_error_screen = 'X'
          RECEIVING
            result              = lv_sent_to_all ).

        IF lv_sent_to_all = 'X'.
          WRITE 'Email sent!'.
        ENDIF.

        "Commit to send email
        COMMIT WORK.

        "Exception handling
      CATCH cx_bcs INTO lr_bcs_exception.

        WRITE:
          'Error!',
          'Error type:',
          lr_bcs_exception->error_type.

    ENDTRY.

  ENDMETHOD.                    "send_email

ENDCLASS.                    "lcl_test IMPLEMENTATION

Prorgram: ZFILE_DECRYPTION

*&---------------------------------------------------------------------*
*& Report  ZFILE_ENCRYPTION
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT zfile_decryption.

PARAMETERS:
  p_key TYPE string OBLIGATORY LOWER CASE.

*----------------------------------------------------------------------*
*       CLASS lcl_encryption DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_decryption DEFINITION.

  PUBLIC SECTION.

    CLASS-METHODS:
      read_file,
      decrypt_file,
      download_file.

    CONSTANTS:
      c_filepath TYPE string VALUE '\\PGRDEV\sapmnt\trans\encrypted_file.txt'.

  PRIVATE SECTION.

    CLASS-DATA:
      v_data      TYPE xstring,
      v_data_text TYPE string,
      v_key       TYPE xstring.

ENDCLASS.                    "lcl_encryption DEFINITION

START-OF-SELECTION.

  "Upload file
  lcl_decryption=>read_file( ).

  "Decryptfile
  lcl_decryption=>decrypt_file( ).

  "Download file
  lcl_decryption=>download_file( ).

*----------------------------------------------------------------------*
*       CLASS lcl_test IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_decryption IMPLEMENTATION.

  METHOD read_file.

    DATA:
      lv_str  TYPE string,
      lv_data TYPE string.

    v_key = p_key.

    OPEN DATASET c_filepath FOR INPUT IN TEXT MODE ENCODING DEFAULT.

    DO.

      READ DATASET c_filepath INTO lv_str.
      IF sy-subrc NE 0.
        EXIT.
      ENDIF.
      lv_data = |{ lv_data }{ lv_str }|.

    ENDDO.

    v_data = lv_data.

  ENDMETHOD.                    "upload_file

  METHOD decrypt_file.

    DATA:
      lt_binary TYPE STANDARD TABLE OF x255.

    DATA:
      lv_message_decrypted TYPE xstring,
      lv_str               TYPE string,
      lv_length            TYPE i.

    "Decrypt message
    cl_sec_sxml_writer=>decrypt(
      EXPORTING
        ciphertext = v_data
        key       =  v_key
        algorithm =  cl_sec_sxml_writer=>co_aes128_algorithm
      IMPORTING
        plaintext =  lv_message_decrypted ).

    "Convert xstring to binary
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        buffer        = lv_message_decrypted
      IMPORTING
        output_length = lv_length
      TABLES
        binary_tab    = lt_binary.

    "Binary to string
    CALL FUNCTION 'SCMS_BINARY_TO_STRING'
      EXPORTING
        input_length = lv_length
      IMPORTING
        text_buffer  = v_data_text
      TABLES
        binary_tab   = lt_binary
      EXCEPTIONS
        failed       = 1
        OTHERS       = 2.

  ENDMETHOD.                    "upload_file

  METHOD download_file.

    DATA:
      lv_filename TYPE string,
      lv_path     TYPE string,
      lv_fullpath TYPE string,
      lt_string   TYPE string_t.

    "File save dialog
    CALL METHOD cl_gui_frontend_services=>file_save_dialog
      CHANGING
        filename                  = lv_filename
        path                      = lv_path
        fullpath                  = lv_fullpath
      EXCEPTIONS
        cntl_error                = 1
        error_no_gui              = 2
        not_supported_by_gui      = 3
        invalid_default_file_name = 4
        OTHERS                    = 5.

    APPEND v_data_text TO lt_string.

    CALL METHOD cl_gui_frontend_services=>gui_download
      EXPORTING
        filename                = lv_fullpath
        filetype                = 'ASC'
      CHANGING
        data_tab                = lt_string
      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.

  ENDMETHOD.                    "download_file

ENDCLASS.                    "lcl_test IMPLEMENTATION

No comments:

Post a Comment