No announcement yet.

AES Encryption with BASE64 encoder

  • Filter
  • Time
  • Show
Clear All
new posts

  • AES Encryption with BASE64 encoder

    Hi All,

    I’ve been working on this program (rpgle #2) on password encryption using APIs. The result of encrypted password (with base64 encoded) is done, however when I call another program (rpg#3) to pass the encrypted password, the return code from web service is 404. To test if rpg #3 is working, i hard coded the encrypted password provided by the client and it works. Below is the code of my password encryption (rpg #2). And i really cannot find what’s wrong with this code. Please help.

    Requirement: AES/CBC/NoPadding algo requies a password with multiple of 16 chars. If the password consists of less than 16, pad or add spaces to the right until the number of chars and spaces amount to 32.
    Attached Files

  • #2
    It seems clear that you're trying to match something else (maybe written in Java) but I don't know exactly what that is? So for that reason, I can't be sure this code is right... I would need to test it against the other method and see how that method works, exactly...

    Having said that, here is my best guess at what you need. Where I made changes, I put "SK:" with an explanation of the change and I left the old code (commented-out) so you can see exactly what I did. Let me know if this helps.

         H DFTACTGRP(*NO) ACTGRP(*caller)
         h BNDDIR('BASE64')
          ** SK: I don't understand why you included QC2LE, when you don't
          *      seem to use any C routines?
          *h BNDDIR('QC2LE')
          * System includes
          * SK: I removed QUSEC, since you don't seem to be using it.
          ** SK: You may need to change these back?  On my system they are
          **     in different source files, so I had to remove the names.
         D**/Copy V15HTRPGLE,httpapi_h
         D**/Copy QTSTSORC,bASE64_h
         D/Copy httpapi_h
         D/Copy bASE64_h
          * prototyped program for encryption
         D QC3EncryptData  pr                  ExtPgm('QC3ENCDT')
         D  P@InpDta                  32767    Options (*VarSize) Const             Data to encrypt
         D  P@InpDtaLen                  10I 0 Const                                Length of data to en
         D  P@InpDtaFmt                   8    Const                                Format of data to en
         D  P@AlgoDesc                32767    Options (*VarSize)                   Algorithm and assoc
         D  P@AlgoDesFmt                  8    Const                                Algorithm desc fmt
         D  P@KDesc                   32767    Options (*VarSize)                   Key description
         D  P@KDFormat                    8    Const                                Key desc format
         D  P@CryptSPrv                   1    Const
         D  P@CryptDev                   10    Const
         D  P@OutEncrData             32767    Options (*VarSize)
         D  P@EncDataLen                 10i 0 Const
         D  P@EncDLRet                   10i 0
         D  errcde                             like(APIERR)
         D QC3GenPRNs      pr                  ExtPgm('QC3GENRN')
         D  PrnDta                    32767    Options (*VarSize)
         D  PrnDtaLen                    10I 0 const
         D  PrnType                       1    const
         D  PrnParity                     1    const
         D  errcde                             like(APIERR)
          * SK: I don't have something called 'BASE64ENCODE' on my system
          *     But, I do have 'base64_encode' that hase a BASE64_H copy
          *     book and BNDDIR('BASE64') with these same parameters,
          *     so I'm using that instead.
         D* base64_encod    PR            10U 0 ExtProc('BASE64ENCODE')
         D*   Input                         *   value
         D*   InputLen                    10U 0 value
         D*   Output                        *   value
         D*   OutputSize                  10U 0 value
          * Data Inputted
          *  SK:  DATA0100 is a simpler format.  DATA0200 is meant
          *       for when you have multiple pointers in an array,
          *       which you don't have here.   If you do use DATA0200
          *       the 2nd parameter to QC3ENCDT should be the number
          *       of array entries...  but your code defines DATA0200
          *       format, but passes the format name 'DATA0100' to the
          *       API, which will cause big problems.  So I'm removing
          *       All of the DATA0200 code and using the simpler DATA0100
         D* @DData0200      DS
         D*  D@DClrPtr                      *
         D*  D@DClrLen                    10i 0
         D*  D@DRserv                     12
          * Algorithm Description
         d @DAlgD0200      DS
         d  D@ABCip                      10i 0                                      block cipher algortm
         d  D@ABLen                      10i 0                                      block length
         d  D@AMode                       1                                         mode
         d  D@APOpt                       1                                         pad option
         D  D@APCha                       1                                         pad character
         D  D@ARserv                      1                                         reserved
         D  D@AMLen                      10i 0                                      MAC length
         D  D@AKSize                     10i 0                                      effective key size
         D  D@AIV                        32                                         initial vector
          * Key Description
         D @DKeyD0200      DS
         D  D@KType                      10i 0
         D  D@KLength                    10i 0
         D  D@KFmt                        1
         D  D@KRserv                      3
         D  D@KString                 32767
          * API error structure
          *  SK: It is not a good idea to hard-code the length of
          *      the data structure.  Instead of INZ(272) please
          *      consider using INZ(%SIZE(APIERR))
         D APIERR          ds
         D**  ERRPRV                       10I 0 INZ(272)
         D  ERRPRV                       10I 0 INZ(%SIZE(APIERR))
         D  ERRLEN                       10I 0
         D  EXCPID                        7A
         D  RSRVD2                        1A
         D  EXCPDT                      256A
         D #keyLength      s             10I 0
         D Input           s            512a
         D Output          s            512a
         D PseudoRandom    s             32    Inz
         D OutEncrData     S             50
         D OutEncrDLen     S             10i 0
         D pEncData        s            512a
         D keyFormat       s              1
         D keyForm         s              1
         D wwEncLen        S             10I 0
          * SK: Converted these to VARYING because they
          *     will need to be UTF-8, and using %LEN(%TRIM))
          *     is not a good idea in that case.
         d E@Pswrd         S            100    varying
         d E@KeyStr        S            100    varying
         d E@Encrypt       S            100
         d E@ErrFlg        S              1
         D P@OutEncrData   s             50
         D AES             c                   const(22)
          * SK: Quick & Dirty way to convert EBCDIC to UTF-8
         D Convert         ds                  qualified
         D   toUtf8                     100a   varying ccsid(*utf8)
         D   data                       100a   varying overlay(toUtf8)
         C                   ExSr      SrPrepEncrypt
         C                   ExSr      SrEncrypt
         C                   ExSr      SrEnd
          * Prepare Parameters for Encryption
         C     SrPrepEncrypt BegSr
          *    SK: Is it correct that the data to encrypt as well as the key
          *        are in EBCDIC?  Some of your comments show examples that
          *        look like either Java or JavaScript code, if you need to
          *        be compatible with those, you don't want these to be EBCDIC!
          * String Password
         C*                   Eval      E@Pswrd        = 'tpP6YE5osT9jYe2c'
         C*                   Eval      E@KeyStr       = '3rTS5pdgRFVCBNi5'
         C                   Eval      Convert.toUtf8  = 'tpP6YE5osT9jYe2c'
         C                   Eval      E@Pswrd         =
         C                   Eval      Convert.toUtf8  = '3rTS5pdgRFVCBNi5'
         C                   Eval      E@KeyStr        =
          * SK: As mentioned above, I'm using DATA0100 for simplicity
         C*                   Eval      @DData0200     = *ALLx'00'
         C*                   Eval      D@DClrPtr      = %Addr (E@Pswrd)
         C*                   Eval      D@DClrLen      = %Size (E@Pswrd)
          * String Keyspecs: AES
          * String Algorithm: AES/CBC/NoPadding
         C                   ExSr      SrAlgorithm
          * String key = "7rPZ4dzwBNRTRhq9
         C                   ExSr      SrKeyParm                                    @DKeyD0200
         C                   EndSr
          * Set Algorithm
         C     SrAlgorithm   BegSr
         C                   Eval      @DAlgD0200     = *ALLx'00'
          * String Algorithm: AES/CBC/NoPadding
         C                   Eval      D@ABCip        = AES
          * Block length
         C                   Eval      D@ABLen        = 16
          * Mode
         C                   Eval      D@AMode        = '1'
          * Pad option
          *  0 No padding is performed.
         C                   Eval      D@APOpt        = '0'
          * Pad character (null-character)
         C                   Eval      D@APCha        = ' '
          * Reserved - Must be null (binary 0s).
         C                   Eval      D@ARserv       = *AllX'00'
          * MAC Length
          * This field is not used on an encrypt operation and must be set to
          * null (binary 0s).
         C                   Eval      D@AMLen        = X'00000000'
          * Effective key size
          * This field must be set to 0.
         C                   Eval      D@AKSize       = 0
          * initial vector
          *  ** SK: You have assigned a 32-byte initialization vector,
          *         however your block length is 16 bytes. The API will
          *         therefore only use 16 bytes of your IV.
         C                   Callp     QC3GenPRNs(PseudoRandom                      binary stream
         C                                       :%Size (PseudoRandom)              data type (AES)
         C                                       :'0'                               type
         C                                       :'0'                               parity
         C                                       :APIERR)                           error ret.
         C                   Eval      D@AIV = PseudoRandom
         C                   EndSr
          * Get Key Parameters
         C     SrKeyParm     BegSr
          * initialize key parameters
         C                   Eval      @DKeyD0200     = *ALLx'00'
          * key type
         C                   Eval      D@KType        = AES
          * key format
         C                   Eval      D@KFmt         = '0'
          * key length
         C                   ExSr      SrGetKLen
          * key string
          * SK: The %Trim here is pointless since D@KString is a fixed-length string
          *     the blanks would simply be added back in:
         C*                   eval      D@KString      = %Trim (E@KeyStr)
         C                   Eval      D@KString      = E@KeyStr
         C                   EndSr
          * Encrypt
         C     SrEncrypt     BegSr
         C                   CallP     QC3EncryptData (
     1   C*                                @DData0200                                data to encrypt
     2   C*                               :%Size (@DData0200)
     1   C                                E@Pswrd                                    data to encrypt
     2   C                               :%Len(E@Pswrd)
     3   C                               :'DATA0100'
     4   C                               :@DAlgD0200
     5   C                               :'ALGD0200'
     6   C                               :@DKeyD0200
     7   C                               :'KEYD0200'
     8   C                               :'0'
     9   C                               :' '
    10   C                               :OutEncrData
         C                               :%Size (OutEncrData)
         C                               :OutEncrDLen
         C                               :APIERR)
         C                   If        ERRLEN = 0
         C*  SK: I don't see the point of making a copy of OutEncrData, here
         C*      You also need to keep track of the length (OutEncrDLen) you
         C*      cannot reliably use %SIZE here.
         c*                   Eval      pEncData    = %subst(OutEncrData:
         c*                                              1:%Size (OutEncrData))
         c                   ExSr      SrBase64
         C                   Endif
         C                   EndSr
          * Use BASE64 encoded to prevent encoding problem
         C     SrBase64      BegSr
          * SK:  The %Trimr here is pointless since 'Input' is a fixed length
          *      string the blanks will simply be re-added.  Also the encrypted
          *      string is BINARY data, not text.. you should never use a text
          *      function like %TRIMR on it, you can corrupt the data.
         C*                   Eval      Input   =  %Trimr (pEncData)
          * String encrypted = Base64.getEncoder().encodeToString(combine)
          * SK: Again, it is important with binary data (and more efficient
          *     all any type of data) to keep track of the length and never
          *     use %TRIM.  Since we have the data and length in OutEncrData
          *     and OutEncrDLen, use that instead of copying the data to
          *     Input and trimming it to get the length.
         C*                   Eval      wwEncLen = base64_encod(%addr(Input)
         C*                                      : %len(%trimr(Input))
         C                   Eval      wwEncLen = base64_encode(%addr(OutEncrData)
         C                                      : OutEncrDLen
         C                                      : %addr(Output)
         C                                      : %size(Output))
         C                   Eval      E@Encrypt = %subst(Output:1:wwEncLen)
         C                   EndSr
          * SrGetKLen - Get Key Length
         C     SrGetKLen     BegSr
          * SK:  Again, avoid the use of %TRIM, especially since E@KeyStr
          *      is no longer EBCDIC, so %TRIM is not reliable.
         C*                   Eval      #KeyLength     = %Len (%Trim (E@KeyStr))
         C                   Eval      #KeyLength     = %Len(E@KeyStr)
         C                   Select
         C                   When      #KeyLength    <= 16
         C                   Eval      #KeyLength     = 16
         C                   When      #KeyLength    <= 24
         C                   Eval      #KeyLength     = 24
         C                   When      #KeyLength    <= 32
         C                   Eval      #KeyLength     = 32
         C                   EndSl
         C                   Eval      D@KLength      = #keyLength
         C                   EndSr
          * End routine
         C     srEnd         BegSr
         c                   Eval      *inlr = *on
         C                   EndSr


    • #3
      Also, since your question is about how to do something in RPG, it really should be posted in the RPG forum...


      • #4
        Hi Scott,

        Thank you

        i tried to include keyword ccsid *utf8 but the parameter says invalid. So i asked to check the PTFs with SysAdm. Is it possible that these PTFS were not included upon upgrade to V7R3?

        Anyways, I used instead the /copy of QTQICONV and ICONV.

        I’ll send update later.


        • #5
          Also, just to test, I tried to run pgm w/o utf-8conversion, rc is now 403 Access Denied.


          • #6
            Originally posted by CaiAndrade View Post
            i tried to include keyword ccsid *utf8 but the parameter says invalid. So i asked to check the PTFs with SysAdm. Is it possible that these PTFS were not included upon upgrade to V7R3?
            First of all, make sure you're not trying to do this in SEU. SEU has not received any new RPG enhancements since March 2008, so you'll receive errors on anything added in the past 12 years. (If that's the case, you can ignore the error and save the member, the compiler will still work.) RDi does not have this problem (provided you've kept it up-to-date.)

            Support for CCSID(*UTF8) was added in the V7R2 release. (At the time, PTFs were available for V7R1) So I can't imagine that you wouldn't have this already, unless you're compiling back to an old release.

            Using iconv() would also work, but obviously that's much more complex/advanced...


            • #7
              Also, I suggest comparing your output to what you'd get from a known working program (looks like you have comments referring to Java in there, maybe use a working Java program?) This would allow you to test different situations and make sure you're getting the same result. Otherwise, just trial and error by getting a '404' or '403' error is going to make it really hard.


              • #8
                Hi Scott,

                Done with CCSID (*UTF8). Got 404 again

                Also, it seems that the size value of E@Encrypt became shorter, 24, compare to the provided encrypted password (which is working) is 44.

                Tested/given encrypted password by client =>>

                my value =>> V6mv7pgL5ebDdcws8E2j3Q==


                • #9
                  Here is good example on AES, but the code is in C#

                  Just for your reference, not sure might help