SAP Web Service

SAP Web Service Authentifizierung mittels ABAP

Bei der Kommunikation über ein Netzwerk jeglicher Art ist Sicherheit das A und O. Dieser Artikel von Quanto Solutions befasst sich damit, eine RSA-SHA Signatur für einen SAP Web Service zu erstellen, um einen externen API Call sicherer zu machen.

Es spielt nicht nur die Verschlüsselung der ausgetauschten Nachrichten bei einem API Call, sondern auch eine sachgemäße Authentifizierung und Autorisierung von Benutzer-Anfragen eine kritische Rolle. Viele Web-APIs und Services verlassen sich deshalb auf Verfahren wie OAuth oder ähnliche Authentifizierungsmodelle, um den Zugriff auf bereitgestellte Ressourcen zu sichern.

In einem üblichen Szenario gibt es neben der öffentlichen Web-Schnittstelle einen zusätzlichen Authentifizierungs-Server, welcher bei erfolgreicher Authentifizierung des Clients einen Access Token bereitstellt, mit welchem nachfolgende Anfragen an die Ziel-Schnittstelle authentifiziert werden.

Die Prämisse ist einfach: Kein gültiger Token, kein Zugriff auf die API!

Doch auch der Zugriff auf den Authentifizierungs-Server selbst sollte gewisse Sicherheitsstandards implementieren. SSL und HTTPS sind eine Selbstverständlichkeit, doch darüber hinaus wird oftmals ein Signierungsverfahren für eingehende Web-Requests implementiert. Die Idee ist hier, dass der HTTP-Anfrage eine Signatur beigefügt wird, die üblicherweise durch eine Kombination aus dem RSA-Verschlüsselungsalgorithmus und einer SHA-Hashfunktion erzeugt wird (RSA-SHA). Unterschiedliche SHA-Implementierungen, wie SHA-1 oder SHA-256 können hier zum Einsatz kommen. Dem Client wird in der Regel ein Zertifikat zur Verfügung gestellt, mit dessen Private Key die RSA-Verschlüsselung vorgenommen wird. Kann der Client keine gültige Signatur liefern, wird die Anfrage abgelehnt.

Praxis-Szenario: RSA-SHA Signatur für einen SAP Web Service erstellen

In einem Praxis-Beispiel soll nun erläutert werden, wie die Generierung einer RSA-SHA Signatur für den Abruf eines Access Tokens aus einer SAP®-Umgebung heraus realisiert werden kann. Kürzlich befanden wir uns für ein Kundenprojekt in der Situation, aus dem SAP®-System des Kunden heraus einen SAP Web Service zu erstellen, welcher wie oben beschrieben über einen Authentifizierungs-Server gesichert war. Dieser Server erwartete eine Signatur im Header jedes Requests mit folgenden Vorgaben:

Signatur-Algorithmus: RSA mit SHA-1

Spezifikation: PKCS 1-v1.5

Zu signierender Input:
{ Zeitstempel der Anfrage } + { Zeitstempel Ablauf der Anfrage } + { Client Zertifikat }

Wir wollen uns in den nachfolgenden Abschnitten anschauen, wie eine erfolgreiche Realisierung dieser Anforderungen in SAP® aussehen kann.

1. Anlegen der RFC-Destination in SM59, um einen SAP Web Service zu erstellen

Die Erstellung einer RFC-Destination ist bekanntermaßen die Grundvoraussetzung für den Konsum externer Web Services in SAP®. Die Transaktion SM59 ist den meisten SAP®-Entwicklern ein Begriff und stellt ein Tool für die Verwaltung externer Endpoints in SAP Systemen zur Verfügung, die dann im ABAP-Code programmatisch angesprochen werden können.

Es muss also zunächst eine RFC-Destination für den Authentifizierungs-Server angelegt werden.

SAP Web Service: Konfiguration von RFC Verbindungen
RFC Verbindungen von Web Services
SM-59 – Anlegen einer HTTP-Verbindung

Selbstverständlich müssen bei einer SSL-gesicherten HTTPS-Verbindung die entsprechenden Konfigurationen unter „Anmeldung und Sicherheit“ vorgenommen werden. Soll ein SSL-Zertifikat verwendet werden, so muss dieses zunächst über die Transaktion STRUST als PSE hinterlegt werden. Dazu mehr in den folgenden Abschnitten. In unserem Beispiel ist eine SSL-gesicherte RFC-Destination bereits gegeben. Wichtig ist, dass es sich bei dem Zertifikat für die Generierung der Signatur und dem Zertifikat für die SSL-Verbindung nicht um dasselbe Zertifikat handeln muss.

2. Erstellung einer SSF-Applikation

Um Signaturen für einen bestimmten Anwendungsfall erzeugen zu können, ist es notwendig, einen entsprechenden Eintrag in der Transaktion SSFA anzulegen. SSF (Secure Store and Forward Mechanism) lässt uns Parameter für digitale Signaturverfahren festlegen, und ist somit genau das richtige Werkzeug für den Job!

In der Tabelle SSFAPPLIC muss zunächst ein Eintrag angelegt werden, welchen wir dann als Konfigurations-Basis in der SSFA-Transaktion verwenden können. Über die SE16 rufen wir uns also die Tabelle SSFAPPLIC auf und fügen eine neue Zeile hinzu.

Data Browser Webservice
Einen neuen Customizingeintrag für SSFA erzeugen.

Im Anschluss können wir einen korrespondierenden Eintrag in der SSFA-Transaktion vornehmen.

SSF Parameter ändern für SAP Web Service
Anwendungsspezifische SSFA-Parameter ändern.
SSF Anwendung RSA Signatur
Auswahl des Signaturalgorithmus
RSA Signatur für Webservice Einstellungen
Detailansicht des neuen SSFA-Eintrags

Je nach Use-Case können hier verschiedene Parameter Anwendung finden. In unserem Fall benötigen wir SHA1 als Hashalgorithmus und wählen für die Verschlüsselung AES128-CBC. Entscheidend ist auch das SSF-Format. Die auswählbaren Formate sind hier unter anderem abhängig von der verwendeten Version der SAP® Cryptographic Library. Sollte das gewünschte Format nicht verfügbar sein, so muss eine Aktualisierung der SAP® Cryptographic Library vorgenommen werden. Dies geschieht über die Kommandozeile und muss durch die SAP®-Basis durchgeführt werden.

SAP Sicherheits Einstellung
Auswahl Public-Key Cryptography Standards

In der SAP®-Umgebung unseres Kunden steht die geforderte PKCS Version (1-V1.5) wie oben aufgeführt zur Verfügung und wird für diesen Use-Case gewählt.

3. Erstellung einer PSE für den Private-Key mittels STRUST

Wie bei jeder Verschlüsselung benötigen wir für die Anwendung des RSA-Algorithmus einen Key. In diesem Fall wird der Private Key unseres Client-Zertifikats benötigt. Um diesen Key sicher im SAP®-System zu hinterlegen, verwenden wir die Transaktion STRUST.

Die STRUST erlaubt uns, sensible Daten wie Schlüsselpaare und Zertifikate in Form von PSEs (Personal Security Environment) sicher in der SAP®-Umgebung zu hinterlegen. Für die von uns angelegte SSF-Applikation können wir nun einen neuen PSE-Eintrag erzeugen und unseren Private Key hinterlegen.

Wir begeben uns also in die Transaktion STRUST und rechtsklicken die von uns angelegte SSFA.

STRUST Transaktion
Transaktion STRUST

In dem Dialogfenster nehmen wir noch die für unseren Anwendungsfall benötigen Einstellungen vor. Die Schlüssellänge sollte 2048 betragen. Für den Algorithmus wählen wir RSA mit SHA-1, so wie es der Authentifizierungs-Server erwartet.

RSA-SHA Signatur
Detailansicht STRUST

Nun müssen wir noch unser Client-Zertifikat, welches auch den private Key enthält, in die angelegte PSE importieren. Das Client-Zertifikat liegt üblicherweise im .PEM-Format vor.

Wir selektieren also unsere SSF Applikation in STRUST und wählen „PSE -> Importieren“ im Menüband.

STRUST auswählen für den Webservice
Import Personal Security Environment (PSE)

Die importierte PSE wird dann unter dem Punkt „Datei“ bzw. „File“ aufgelistet. Wir selektieren diese und wählen dann „PSE->Sichern als“. In dem sich daraufhin öffnenden Dialogfenster muss nun die zuvor angelegte SSF-Applikation ausgewählt werden.

PSE speichern
Speichern der PSE mit den Parametern aus Punkt 2.

Somit ist das Client-Zertifikat, welches für die Signatur benötigt wird, auf sichere Weise im SAP®-System hinterlegt und kann aus einem ABAP-Programm heraus abgerufen werden.

4. Erzeugung der Signatur und Aufruf des Web Service in ABAP

Zur Veranschaulichung der programmatischen Lösung in ABAP, erstellen wir einen Test-Report. In diesem gilt es, die folgenden Schritte auszuführen:

  1. Auslesen des Client-Zertifikates aus der PSE
  2. Erzeugung der Zeitstempel
  3. Generierung der Signatur mittels Zeitstempel und Zertifikat
  4. Setzen der Header im HTTP-Request
  5. Aufruf des Authentifizierungs-Web Service

Für das Auslesen des Zertifikats steht uns der Funktionsbaustein „SSFP_GETAPPCERTIFICATE“ zur Verfügung. Dieser liefert das Zertifikat über einen Exporting-Parameter als Byte-Folge (Typ XSTRING) zurück. Wichtig ist, für den Parameter „application“ den Namen unserer SSF-Applikation, wie zuvor in der SSFAPPLIC Tabelle angelegt, zu übergeben.

CALL FUNCTION 'SSFP_GETAPPCERTIFICATE'
  EXPORTING
    application             = 'RSASIG'
    client                  =  sy-mandt
  IMPORTING
    certxstring             = lv_xcertificate
  EXCEPTIONS
    ssf_parameter_not_found = 1
    ssf_krn_error           = 2
    ssf_nocertificate       = 3
    ssf_parsing_error       = 4
    ssf_unknown_error       = 5
    OTHERS                  = 6.
IF sy-subrc <> 0.
* Sachgemäßes Error-Handling
ENDIF.

In unserem Fall soll die RSA-SHA1 Signatur über 3 Werte gebildet werden: einen Zeitstempel, der das Erstellungsdatum des Requests beschreibt, einen weiteren Zeitstempel, der das Ablaufdatum des Requests darstellt und schließlich das Client-Zertifikat. Für das Ablaufdatum addieren wir 20 Sekunden auf den aktuellen Zeitstempel.

Die Zeitstempel erzeugen wir zunächst im erwarteten Format (yyyyMMddThhmmss.000Z) als String, konkatenieren diese und wandeln den erzeugten String schließlich in einen XSTRING um.

DATA l_request_created TYPE string.
DATA l_request_expires TYPE string.
Data lv_xtimestamps type xstring.

GET TIME STAMP FIELD DATA(l_request_created).

l_request_created = lv_ts_created.
l_request_created = |{ l_request_created(8) }T{ l_request_created(6) }.000Z|.

DATA(lv_ts_expires) =  cl_abap_tstmp=>add( tstmp = lv_ts_created secs  = 20 ).
l_request_expires = lv_ts_expires.
l_request_expires = |{ l_request_expires(8) }T{ l_request_expires+8(6) }.000Z|.


DATA(l_time_interval) = |{ me->request_created }{ me->request_expires }|.

CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
  EXPORTING
    text   = l_time_interval
  IMPORTING
    buffer = lv_xtimestamps.

Nun liegen uns das Client-Zertifikat (lv_xcertificate) sowie die beiden verketteten Zeitstempel (lv_xtimestamps) als XSTRINGs vor. Diese können wir wiederum verketten und als Input für den Signatur-Algorithmus verwenden. Hier kommt der wichtigste Funktionsbaustein zum Einsatz: „SSF_KRN_SIGN“. Dieser ist zuständig für das Generieren von Signaturen gemäß den Konfigurationen aus der SSFA-Transaktion.

DATA lt_input_bin TYPE STANDARD TABLE OF ssfbin.
DATA lt_output_bin TYPE STANDARD TABLE OF ssfbin.
DATA lv_input_length TYPE ssflen.
DATA lv_output_length TYPE ssflen.
DATA lv_output_crc TYPE ssfreturn.
DATA lt_signer TYPE STANDARD TABLE OF ssfinfo.
DATA lv_unix_iat TYPE string.

lv_xinput = |{ lv_timestamps }{ lv_xcertificate }|.

CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
  EXPORTING
    text     = lv_xinput
  TABLES
    binary_tab   = lt_input_bin.

lt_signer = VALUE #( ( id = '<implicit>' profile = 'SAPRSASIG800.pse' result = 28 ) ).

lv_input_length = xstrlen( lv_xinput ).

CALL FUNCTION 'SSF_KRN_SIGN'
  EXPORTING
    str_format         = 'PKCS1-V1.5'
    b_inc_certs        = abap_false
    b_detached         = abap_false
    b_inenc            = abap_false
    ostr_input_data_l  = lv_input_length
    str_hashalg        = 'SHA1'
  IMPORTING
    ostr_signed_data_l = lv_output_length
    crc                = lv_output_crc    " SSF Return code
  TABLES
    ostr_input_data    = lt_input_bin
    signer             = lt_signer
    ostr_signed_data   = lt_output_bin

Bei den Übergabe-Parametern ist hier einiges zu beachten. Als Hash-Algorithmus übergeben wir in unserem Fall „SHA1“ und für das Format geben wir „PKCS1-V1.5“ an. Diese Spezifikationen wurden im Vorfeld festgelegt und werden so vom angesprochenen Endpoint erwartet. Sie variieren somit je nach Anwendungsfall.

Die zu übergebene Tabelle „signer“ spezifiziert das SSFA-Profil, welches im Vorfeld über die SSFA angelegt wurde. Hier ist der festgelegte SSF-Profilname anzugeben.

Der Funktionsbaustein nimmt den zu signierenden Input als Binär-Tabelle entgegen. Daher nutzen wir vorab den Funktionsbaustein „SCMS_XSTRING_TO_BINARY“, um den zu signierenden XSTRING entsprechend zu konvertieren.

Im Erfolgsfall gibt der Funktionsbaustein die erzeugte Signatur ebenfalls als Binär-Tabelle zurück (Parameter „ostr_signed_data“). Tritt ein Fehler auf, wird über den Parameter „crc“ ein Error-Code zurückgeben, der für eine angemessene Fehlerbehandlung dienlich ist.

Im Binär-Format lässt sich die Signatur natürlich schwer an den Authentifizierungs-Server übergeben. Erwartet wird diese im URL-konformen Base64-Format. Um die Signatur in dieses Format zu bringen, muss sie erst wieder in einen String konvertiert werden. Dafür nutzen wir den folgenden Funktionsbaustein:

Data lv_signature type string.

CALL FUNCTION 'SCMS_BINARY_TO_STRING'
  EXPORTING
    input_length = iv_output_length
  IMPORTING
    text_buffer  = lv_signature
  TABLES
    binary_tab   = lt_output_bin
  EXCEPTIONS
    failed       = 1
    OTHERS       = 2.
IF sy-subrc <> 0.
  "Angemessene Fehlerbehandlung
ENDIF.

Der generierte String kann dann über die Methode cl_http_utility=>encode_base64 ins Base64-Format gebracht werden. Die URL-Konformität lässt sich herstellen, indem wir den Base64-codierten String nun durch folgende Zeichenersetzungen modifizieren.

REPLACE ALL OCCURRENCES OF '=' IN lv_signature WITH ''.
REPLACE ALL OCCURRENCES OF '+' IN lv_signature WITH '-'.
REPLACE ALL OCCURRENCES OF '/' IN lv_signature WITH '_'.

Die Variable lv_signature enthält jetzt unsere endgültige Signatur für die erfolgreiche Authentifizierung. Der Server erwartet jedoch außerdem das Client-Zertifikat selbst als Header in der HTTP-Anfrage. Also kodieren wir das Zertifikat auf dieselbe Weise.

Data(l_cert_base64) = cl_http_utillity=>encode_x_base64( unencoded = lv_xcertificate ).

REPLACE ALL OCCURRENCES OF '=' IN l_cert_base64 WITH ''.
REPLACE ALL OCCURRENCES OF '+' IN l_cert_base64 WITH '-'.
REPLACE ALL OCCURRENCES OF '/' IN l_cert_base64 WITH '_'.

Es liegen uns nun alle Werte vor, die wir für ein erfolgreiches Request an den Authentifizierungs-Server benötigen. Für das Absetzen von HTTP-Requests kann die ABAP-Klasse „CL_HTTP_CLIENT“ genutzt werden. Eine Instanz lassen wir uns über die statische Methode „CREATE_BY_DESTINATION“erzeugen. Dabei ist es essenziell, den Namen der RFC-Destination zu spezifizieren.

CALL METHOD cl_http_client=>create_by_destination
    EXPORTING
        destination              = 'Auth-Token-Server'
    IMPORTING
        client                   = DATA(lo_client_api)
    EXCEPTIONS
        argument_not_found       = 1
        destination_not_found    = 2
        destination_no_authority = 3
        plugin_not_active        = 4
        internal_error           = 5
        OTHERS                   = 6.

Zuletzt müssen noch die erforderlichen Request-Header erstellt und dem abzusetzenden HTTP-Request hinzugefügt werden. Die Klasse CL_HTTP_CLIENT stellt entsprechende Instanz-Methoden zur Verfügung.

DATA it_header_fields TYPE tihttpnvp.
DATA str_header_fields TYPE ihttpnvp.

str_header_fields-name = 'requestCreated'.
str_header_fields-value = l_request_created.
APPEND str_header_fields TO it_header_fields.

str_header_fields-name = 'requestExpires'.
str_header_fields-value = l_request_expires.
APPEND str_header_fields TO it_header_fields.

str_header_fields-name = 'certificate'.
str_header_fields-value = l_cert_base64.
APPEND str_header_fields TO it_header_fields.

str_header_fields-name = 'signature'.
str_header_fields-value = lv_signature.
APPEND str_header_fields TO it_header_fields.

str_header_fields-name = 'Accept'.
str_header_fields-value = 'application/json'.
APPEND str_header_fields TO it_header_fields.

cl_http_utility=>set_request_uri(
      request = lo_client_api->request
      uri = '/base/'
).

lo_rest_http_client->if_rest_client~set_request_headers( it_header_fields )

lo_client_api->request->set_method( method = if_http_request=>co_request_method_post ).

lo_client_api->send( ).

lo_client_api->receive( 
   EXCEPTIONS
     http_communication_failure = 1
     http_invalid_state = 2
     http_processing_failed = 3 
).

Data(l_data) = lo_client_api->response->get_data( ).

In unserem Anwendungsfall erwartet der Authentifizierungs-Server ein POST-Request und liefert den generierten Token im Body der HTTP-Response zurück. Wir fügen den Header „accept“ mit dem Wert „application/json“ an, da die zurückgelieferte Nachricht im JSON-Format erwartet wird. Ist alles korrekt, können wir nun die Antwort des Authentifizierungs-Servers über die Methode „GET_DATA“ auslesen. Das hierin enthaltene JSON-Objekt kann mithilfe der Utility-Methode „/ui2/cl_json=>deserialize“ in eine ABAP-Struktur deserialisiert werden. Schauen wir uns die Komponenten dieser Struktur an, finden wir schließlich den Access Token.

ABAP Struktur Web Service
Token als ABAP Struktur

SAP Web Service: Fazit

Mit dem richtigen Know-How ist die Anwendung von unterschiedlichen Verschlüsselungsverfahren in SAP kein Problem. So können auch Endpunkte angebunden werden, deren Integration komplexere Anforderungen stellt als das Erstellen einer HTTP-Verbindung in der SM59. Die Buzzwords lauten hier: STRUST, SSFA und PSE. Wir hoffen, dass Sie nun die nötigen Kenntnisse haben, eine RSA-SHA Signatur für einen SAP Web Service selbst zu erstellen.

Ein erfahrener SAP Berater/in wie Quanto Solutions bringt die nötigen Kenntnisse für diese Tools mit.

Autoren: Peter Ritz & Alexander Herrfurth

Tags