Skip to Content
0
Jul 26, 2023 at 07:26 PM

SAP ABAP: TOTP Generation (Time-based one-time password algorithm)

209 Views Last edit Jul 27, 2023 at 03:01 PM 3 rev

Dear Guru's,

I hope you are well.

We have requirement to generate TOTP using ABAP and I got stuck on the requirement to generate TOTP tokens from SAP ABAP. This is very easy and doable in JAVA but challenging in ABAP.

Based on my analysis this can be doable using Class: CL_ABAP_HMAC.

I am trying to convert Java Code to ABAP but I am stuck on some of the places where byte Array in Java Code and not able to make it result like java code.

image.png

Also I have gone through : https://datatracker.ietf.org/doc/html/rfc6238 and based on this I was trying code for JAVA and ABAP.

I am attaching both codes for your reference.

It would be helpful if you can advise. How can I get results like in the Java class?

Thank you

ABAP Code:

<br>DATA: im_date TYPE sy-datum.<br>DATA: im_time       TYPE sy-uzeit,<br>      im_msec       TYPE numc7,<br>      l_xstring_key TYPE xstring,<br>      l_key         TYPE string,<br>      ex_timestamp  TYPE string.<br><br><br>DATA: l_date TYPE sy-datum.<br><br>DATA: l_days_timestamp TYPE timestampl.<br><br>DATA: l_secs_timestamp TYPE timestampl.<br><br>DATA: l_i_days TYPE i.<br><br>DATA: l_i_sec TYPE i.<br><br>DATA: l_timestamp TYPE timestampl.<br><br>DATA: l_dummy TYPE string.<br><br>DATA: ts2(25) TYPE c,<br>      ts      TYPE timestampl.<br>GET TIME STAMP FIELD ts.<br>DATA: dat TYPE date,<br>      tim TYPE time,<br>      tz  TYPE timezone.<br>MOVE ts TO ts2.<br>CONDENSE ts2.<br>DATA: t1(10),t2(13) TYPE c.<br>CONVERT TIME STAMP ts TIME ZONE sy-zonlo INTO DATE dat TIME tim.<br>CONCATENATE dat+0(4) dat+4(2) dat+6(2) INTO t1.<br><br>im_date =  dat.<br><br>CONCATENATE tim+0(2)  tim+2(2)  tim+4(2)   INTO t2.<br>im_time = tim.<br>im_msec = ts2+15(7).<br><br><br><br>*-----------------------------------------------------------------------<br><br>* milliseconds for the days since January 1, 1970, 00:00:00 GMT<br><br>* one day has 86400 seconds<br><br>l_date = '19700101'.<br><br>l_i_days = im_date - l_date.<br><br>* timestamp for days past in seconds<br><br>l_days_timestamp = l_i_days * 86400.<br><br>l_i_sec = im_time.<br><br>* timestamp for time at present day<br><br>l_secs_timestamp = l_i_sec.<br><br>l_timestamp = ( l_days_timestamp + l_secs_timestamp ) * 1000.<br><br>ex_timestamp = l_timestamp.<br><br>SPLIT ex_timestamp AT '.' INTO ex_timestamp l_dummy.<br><br>ex_timestamp = ex_timestamp + im_msec.<br><br>SHIFT ex_timestamp RIGHT DELETING TRAILING space.<br><br>SHIFT ex_timestamp LEFT DELETING LEADING space.<br>DATA(l) = ex_timestamp / 1000.<br><br>ex_timestamp = round( val = ( ( round( val = l  dec = 0 ) - 0 ) / 30 ) dec = 0 ).<br><br><br><br><br>TYPES t_long_int(16) TYPE p DECIMALS 0.<br>DATA: number TYPE t_long_int,<br>      result TYPE string.<br><br>number = ex_timestamp.<br>PERFORM convert_base10_to_base16 USING    number<br>                                 CHANGING result.<br>WRITE: / number, '-->', result.<br><br><br><br>TRY.<br>    l_key  = '3132333435363738393031323334353637383930'.<br>    l_xstring_key  = cl_abap_hmac=>string_to_xstring( l_key ).<br>  CATCH cx_abap_message_digest.<br><br>ENDTRY.<br><br>clear : result.<br>number = ex_timestamp.<br>PERFORM  gettotp USING    number<br>                          l_xstring_key<br>                          CHANGING result.<br><br>  write /       result.<br><br>FORM gettotp USING    timeindex TYPE t_long_int<br>                      key TYPE xstring<br>                     CHANGING result TYPE string.<br><br><br>*TRY.<br>CALL METHOD cl_abap_hmac=>get_instance<br>  EXPORTING<br>    if_algorithm = 'SHA1'<br>    if_key       = key<br>  receiving<br>    ro_object    = DATA(lo_hmac)<br>    .<br>* CATCH cx_abap_message_digest .<br>*ENDTRY.<br><br><br><br><br>data(l_string) = CONV string( timeindex ).<br>  TRY.<br>      CALL METHOD lo_hmac->final<br>        EXPORTING<br>          if_data = cl_abap_hmac=>string_to_xstring( l_string  )<br>      if_offset        = 0<br>      if_length        = strlen( l_string )<br>    IMPORTING<br>      ef_hmacstring    = result<br>*    ef_hmacxstring   =<br>*    ef_hmacb64string =<br>      .<br>    CATCH cx_abap_message_digest .<br>  ENDTRY.<br><br>*TRY.<br>CALL METHOD lo_hmac->to_base64<br>  RECEIVING<br>    er_hmacb64string = data(l_base)<br>    .<br>* CATCH cx_abap_message_digest .<br>*ENDTRY.<br><br>    endform.<br><br>FORM convert_base10_to_base16 USING    number TYPE t_long_int<br>                              CHANGING result TYPE string.<br><br>  DATA:<br>    rest  TYPE t_long_int,<br>    digit TYPE c.<br><br>  rest = number MOD 16.<br>  CASE rest.<br>    WHEN 0 OR 1 OR 2 OR 3 OR 4 OR 5 OR 6 OR 7 OR 8 OR 9.<br>      WRITE rest TO digit LEFT-JUSTIFIED NO-SIGN.<br>    WHEN 10.<br>      digit = 'A'.<br>    WHEN 11.<br>      digit = 'B'.<br>    WHEN 12.<br>      digit = 'C'.<br>    WHEN 13.<br>      digit = 'D'.<br>    WHEN 14.<br>      digit = 'E'.<br>    WHEN 15.<br>      digit = 'F'.<br>  ENDCASE.<br>  CONCATENATE digit result INTO result.<br><br>  rest = number DIV 16.<br>  IF rest > 0.<br>    PERFORM convert_base10_to_base16 USING    rest<br>                                     CHANGING result.<br>  ENDIF.<br><br>ENDFORM.

Java Code:

package testJavaCode;<br>import java.lang.reflect.UndeclaredThrowableException;<br>import java.security.GeneralSecurityException;<br>import java.security.NoSuchAlgorithmException;<br>import java.text.DateFormat;<br>import java.text.SimpleDateFormat;<br>import java.util.Date;<br>import javax.crypto.Mac;<br>import javax.crypto.spec.SecretKeySpec;<br>import java.math.BigInteger;<br>import java.nio.ByteBuffer;<br>import java.util.TimeZone;<br>public class TOTP2 {<br><br>    <br><br><br>    /**<br>     * This method converts HEX string to Byte[]<br>     *<br>     * @param hex   the HEX string<br>     *<br>     * @return      A byte array<br>     */<br>    private static byte[] hexStr2Bytes(String hex){<br>        // Adding one byte to get the right conversion<br>        // values starting with "0" can be converted<br>        byte[] bArray = new BigInteger("10" + hex,16).toByteArray();<br><br>        // Copy all the REAL bytes, not the "first"<br>        byte[] ret = new byte[bArray.length - 1];<br>        for (int i = 0; i < ret.length ; i++)<br>            ret[i] = bArray[i+1];<br>        return ret;<br>    }<br><br><br>    private static final int[] DIGITS_POWER<br>    // 0 1  2   3    4     5      6       7        8<br>    = {1,10,100,1000,10000,100000,1000000,10000000,100000000 };<br><br><br>    /**<br><br><br>    /**<br>     * This method generates an TOTP value for the given<br>     * set of parameters.<br>     *<br>     * @param key   the shared secret, HEX encoded<br>     * @param time     a value that reflects a time<br>     * @param returnDigits     number of digits to return<br>     * @param crypto    the crypto function to use<br>     *<br>     * @return      A numeric String in base 10 that includes<br>     *              {@link truncationDigits} digits<br>     * @throws NoSuchAlgorithmException <br>     */<br>    public static long generateTOTP(byte[] key,<br>            long timeindex<br>            ) throws Exception {<br>        SecretKeySpec signkey = new SecretKeySpec(key , "HmacSHA1");<br>        <br>        ByteBuffer bufferobj = ByteBuffer.allocate(8);<br>        <br>        bufferobj.putLong(timeindex);<br>        <br>        byte[] timeBytesobj = bufferobj.array();<br>        <br>        Mac macobj = Mac.getInstance("HmacSHA1");<br>        <br>        macobj.init(signkey);<br>        <br>        byte[] hash = macobj.doFinal(timeBytesobj);<br>        <br>        int offset = hash[19] & 0xf;<br>        long trunc = hash[offset] & 0x7f;<br>        for ( int i = 1; i < 4 ; i++)<br>        {<br>            trunc <<= 8;<br>            trunc |= hash[offset + i]  & 0xff;<br>            <br>        }<br>        return ( trunc %= 1000000 );<br>    }<br><br>    <br><br>    public static void main(String[] args) {<br>        // Seed for HMAC-SHA1 - 20 bytes<br>        String seed = "3132333435363738393031323334353637383930";<br>  <br>        long T0 = 0;<br>        long X = 30;<br><br>        try{<br><br>            <br>                long T = (56344362 - T0)/X;<br>                <br>                System.out.println(generateTOTP(hexStr2Bytes(seed), T));<br>                <br>                <br>            <br>        }catch (final Exception e){<br>            System.out.println("Error : " + e);<br>        }<br>    }<br>}<br><br><br>

java-totp-class1.txt

Regards,

Nikhil

Attachments

image.png (58.3 kB)
abap.txt (3.8 kB)