Skip to main content
February 24, 2022
Solved

How to generate base64 digest string in Appian.

  • February 24, 2022
  • 37 replies
  • 1 view

Hello All,

Below is the code snippet from Ruby.

Digest::SHA256.base64digest('')

I need to generate the same in Appian and pass it to the integration header parameter, please let me know if anyone has done this before.

Thank You,

Sneha.

    Best answer by mikes0011

    After some further research, I found that extended characters (anything past char(127)) are handled specially in standard UTF-8 base64 encoding.  I did a little tampering with my existing rules and came up with the following updates today:

    Encoder:

    /* encode */
    a!localVariables(
      local!text: ri!text,
    
      local!charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
      local!codeArray: code(local!text),
      local!binArray: a!forEach(
        local!codeArray,
        if(
          fv!item > 127,
          if(
            fv!item > 2047,
            if(
              fv!item > 65535,
              dec2bin(fv!item, 21),
              dec2bin(fv!item, 16)
            ),
            dec2bin(fv!item, 11)
          ),
          dec2bin(fv!item, 8)
        )
      ),
      
      local!preProcessBinArray: a!forEach(
        /* encoding UTF-8 self-synchronizing (grumble) ref: https://en.wikipedia.org/wiki/UTF-8#Description */
        items: local!binArray,
        expression: a!match(
          value: len(fv!item), default: fv!item,
          
          equals: 8,
          then: fv!item,
          
          equals: 11,
          then: {
            concat( "110", left(fv!item, 5) ),
            concat( "10", right(fv!item, 6) )
          },
          
          equals: 16,
          then: {
            concat( "1110", left(fv!item, 4) ),
            concat( "10", mid(fv!item, 5, 6) ),
            concat( "10", right(fv!item, 6) )
          },
          
          equals: 21,
          then: {
            concat( "11110", left(fv!item, 3) ),
            concat( "10", mid(fv!item, 4, 6) ),
            concat( "10", mid(fv!item, 10, 6) ),
            concat( "10", right(fv!item, 6) )
          }
        )
      ),
      
      local!bins: concat(local!preProcessBinArray),
      
      local!groupedBins: split(concat(
        a!forEach(
          items: code(local!bins), 
          expression: char(fv!item) & if(and(mod(fv!index, 6) = 0, not(fv!isLast)), "-=-=-", "")
        )
      ), "-=-=-" ),
      local!lastBin: index(local!groupedBins, length(local!groupedBins)),
      local!padding: a!match(
        value: len(local!lastBin),
        equals: 2, then: "==",
        equals: 4, then: "=",
        equals: 6, then: "",
        default: "error condition"
      ),
      local!bindexes: a!forEach(
        items: local!groupedBins,
        expression: if(
          fv!isLast,
          bin2dec(substitute(padright(fv!item, 6), " ", "0")),
          bin2dec(fv!item)
        )
      ) + 1,
    
      local!assembledOutput: concat(a!forEach(items: local!bindexes, expression: charat(local!charset, fv!item))) & local!padding,
    
      local!assembledOutput
    )

    Decoder:

    /* decode */
    a!localVariables(
      local!base64text: ri!base64text,
    
      local!charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
    
      local!charCodes: code(local!base64text),
      /*local!paddingCount: count(wherecontains(code("="), local!charCodes)),*/
      local!unpaddedChars: a!flatten(a!forEach(
        items: local!charCodes,
        expression: if(fv!item = code("="), {}, find(char(fv!item), local!charset)-1)
      )),
      local!rawBins: dec2bin(tointeger(local!unpaddedChars), 6),
    
      local!decodedBins: split(
        concat(
          a!forEach(
            items: code(concat(local!rawBins)),
            expression: if(
              mod(fv!index, 8) = 0,
              char(fv!item) & "-=-=-",
              char(fv!item)
            )
          )
        ),
        "-=-=-"
      ),
    
      
      local!intermediaryDecodedBinList: a!flatten(a!forEach(
        /* update to handle UTF-8 self-synchronizing for extended character-set items. ref: https://en.wikipedia.org/wiki/UTF-8#Description */
        items: local!decodedBins,
        expression: if(
          or(fv!item = "0000", fv!item = "00"),
          {},
          
          a!match(
            value: fv!item, default: fv!value,
    
            /* handle 2-byte items */
            whentrue: left(fv!value, 3) = "110",
            then: concat(right(fv!item, 5), right(local!decodedBins[fv!index+1], 6)),
            whenTrue: or(  /* manually check and skip re-using subsequent ones on following iterations (ugh) */
              and(fv!index >= 2, left(local!decodedBins[fv!index-1], 3) = "110"),
            ),
            then: {},
    
            /* handle 3-byte items */
            whentrue: left(fv!value, 4) = "1110",
            then: concat(right(fv!item, 4), right(local!decodedBins[fv!index+1], 6), right(local!decodedBins[fv!index+2], 6)),
            whenTrue: or(  /* manually check and skip re-using subsequent ones on following iterations (ugh) */
              and(fv!index >= 2, left(local!decodedBins[fv!index-1], 4) = "1110"),
              and(fv!index >= 3, left(local!decodedBins[fv!index-2], 4) = "1110")
            ),
            then: {},
    
            /* handle 4-byte items */
            whentrue: left(fv!value, 5) = "11110",
            then: concat(right(fv!item, 3), right(local!decodedBins[fv!index+1], 6), right(local!decodedBins[fv!index+2], 6), right(local!decodedBins[fv!index+3], 6)),
            whenTrue: or(  /* manually check and skip re-using subsequent ones on following iterations (ugh) */
              and(fv!index >= 2, left(local!decodedBins[fv!index-1], 5) = "11110"),
              and(fv!index >= 3, left(local!decodedBins[fv!index-2], 5) = "11110"),
              and(fv!index >= 4, left(local!decodedBins[fv!index-3], 5) = "11110")
            ),
            then: {}
            
          )
        )
      )),
      
      concat(
        a!forEach(
          local!intermediaryDecodedBinList,
          char(bin2dec(fv!item))
        )
      )
    )

    These work with text containing the euro symbol and other extended character set items to an extent.  Warning that I have *not* tested them very thoroughly for corner cases (like items that exist at character set boundaries) yet, but the results so far match results given by public online base64 encoder tools.

    37 replies

    stefanhelzle0001
    February 24, 2022
    February 24, 2022

    Hello Stefan,

    Yes I tried with this plug-in but facing some issue while invoking external api. So would like to know if there any other way we can generate base64 digest string.

    Thank You,

    Sneha.

    stefanhelzle0001
    February 24, 2022

    There are so many ways of how this can be done. If yours is not supported by the existing plugins, your might have to develop your own using Java.

    mikes0011
    Brainy
    February 24, 2022

    Can you clarify on what you mean exactly by "base64 digest string"?  What is your input and what is your desired output?  If, for example, it just requires translation of a string to Base64, this can be done OOtB.

    February 28, 2022

    Hello Mike,

    Thank You for the response.

    I need to get the equivalent hash value of the given string. First, need to get the SHA256 hash of the given string and convert the value to base64.

    I've done using Java code and exposed it as an Appian function.

    mikes0011
    Brainy
    February 28, 2022

    That sounds good.  I've previously written an OOB Appian expression to convert text to Base64, but I don't have any solution for the SHA256 hash portion so i'm glad you figured something out.  Did you develop this as a plug-in or use some other method to expose it to Appian, if I may ask?