SHA-1 hash for LoadRunner

One of my colleagues at work was testing a solution that obfuscated passwords using the SHA1 algorithm, and was looking for a suitable solution to replicate this with LoadRunner. As I’ve used SHA1 before for other things, I promised to look around and see if I could help out.

As it turns out a suitable source was found and I made the necessary changes to the code to make it work in LoadRunner. A big thank you to Paul E. Jones for providing a free SHA1 implementation.

I created an include file names “lr_sha1.c” that can easily be included in any C based LoadRunner script or VUser type.

Example of usage (vuser_init.c):

#include "lr_sha1.c"
vuser_init()
{
	//
	// HASH the string "The quick brown fox jumps over the lazy dog",
	// The result should be "2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12"
	//
	sha1_hash("The quick brown fox jumps over the lazy dog", "_sha1");

	lr_output_message( "SHA1 Hash: %s", lr_eval_string("{_sha1}") );

	return 0;
}

And here is the lr_sha1.c file:

/*
 *****************************************************************************
 *
 *  March 2010
 *
 *    Small changes by Kim Sandell to make the source work in LoadRunner
 *    - Changed "const unsigned char" to "const char" in function params
 *    - Combined sha1.h and sha1.c into one file (for ease of use in LR)
 *    - Added sha1_hash() function to ease use in LR
 *    - Included Paul's license in comments
 *
 *****************************************************************************
 *
 *  Freeware Public License (FPL)
 *
 *  This software is licensed as "freeware."  Permission to distribute
 *  this software in source and binary forms, including incorporation
 *  into other products, is hereby granted without a fee.  THIS SOFTWARE
 *  IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES,
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 *  AND FITNESS FOR A PARTICULAR PURPOSE.  THE AUTHOR SHALL NOT BE HELD
 *  LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER
 *  DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA
 *  OR DATA BEING RENDERED INACCURATE.
 *
 *****************************************************************************
 *
 *  sha1.h
 *
 *  Copyright (C) 1998, 2009
 *  Paul E. Jones <paulej@packetizer.com>
 *  All Rights Reserved
 *
 *****************************************************************************
 *  $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $
 *****************************************************************************
 *
 *  Description:
 *      This class implements the Secure Hashing Standard as defined
 *      in FIPS PUB 180-1 published April 17, 1995.
 *
 *      Many of the variable names in the SHA1Context, especially the
 *      single character names, were used because those were the names
 *      used in the publication.
 *
*/

#ifndef _SHA1_H_
#define _SHA1_H_

/*
 *  This structure will hold context information for the hashing
 *  operation
 */
typedef struct SHA1Context
{
 unsigned Message_Digest[5];       /* Message Digest (output)          */
 unsigned Length_Low;              /* Message length in bits           */
 unsigned Length_High;             /* Message length in bits           */
 unsigned char Message_Block[64];  /* 512-bit message blocks      */
 int Message_Block_Index;          /* Index into message block array   */
 int Computed;                     /* Is the digest computed?          */
 int Corrupted;                    /* Is the message digest corruped?  */
} SHA1Context;

/*
 *  Function Prototypes
 */
void SHA1Reset(SHA1Context *);
int SHA1Result(SHA1Context *);
void SHA1Input( SHA1Context *,
 const char *,
 unsigned);
#endif

/*
 *****************************************************************************
 *
 *  sha1.c
 *
 *  Copyright (C) 1998, 2009
 *  Paul E. Jones <paulej@packetizer.com>
 *  All Rights Reserved
 *
 *****************************************************************************
 *  $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $
 *****************************************************************************
 *
 *  Description:
 *      This file implements the Secure Hashing Standard as defined
 *      in FIPS PUB 180-1 published April 17, 1995.
 *
 *      The Secure Hashing Standard, which uses the Secure Hashing
 *      Algorithm (SHA), produces a 160-bit message digest for a
 *      given data stream.  In theory, it is highly improbable that
 *      two messages will produce the same message digest.  Therefore,
 *      this algorithm can serve as a means of providing a "fingerprint"
 *      for a message.
 *
 *  Portability Issues:
 *      SHA-1 is defined in terms of 32-bit "words".  This code was
 *      written with the expectation that the processor has at least
 *      a 32-bit machine word size.  If the machine word size is larger,
 *      the code should still function properly.  One caveat to that
 *      is that the input functions taking characters and character
 *      arrays assume that only 8 bits of information are stored in each
 *      character.
 *
 *  Caveats:
 *      SHA-1 is designed to work with messages less than 2^64 bits
 *      long. Although SHA-1 allows a message digest to be generated for
 *      messages of any number of bits less than 2^64, this
 *      implementation only works with messages with a length that is a
 *      multiple of the size of an 8-bit character.
 *
 *****************************************************************************
 */

/*
 *  Define the circular shift macro
 */
#define SHA1CircularShift(bits,word) \
 ((((word) << (bits)) & 0xFFFFFFFF) | \
 ((word) >> (32-(bits))))

/* Function prototypes */
void SHA1ProcessMessageBlock(SHA1Context *);
void SHA1PadMessage(SHA1Context *);

/*
 *  SHA1Reset
 *
 *  Description:
 *      This function will initialize the SHA1Context in preparation
 *      for computing a new message digest.
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to reset.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1Reset(SHA1Context *context)
{
 context->Length_Low             = 0;
 context->Length_High            = 0;
 context->Message_Block_Index    = 0;

 context->Message_Digest[0]      = 0x67452301;
 context->Message_Digest[1]      = 0xEFCDAB89;
 context->Message_Digest[2]      = 0x98BADCFE;
 context->Message_Digest[3]      = 0x10325476;
 context->Message_Digest[4]      = 0xC3D2E1F0;

 context->Computed   = 0;
 context->Corrupted  = 0;
}

/*
 *  SHA1Result
 *
 *  Description:
 *      This function will return the 160-bit message digest into the
 *      Message_Digest array within the SHA1Context provided
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to use to calculate the SHA-1 hash.
 *
 *  Returns:
 *      1 if successful, 0 if it failed.
 *
 *  Comments:
 *
 */
int SHA1Result(SHA1Context *context)
{

 if (context->Corrupted)
 {
 return 0;
 }

 if (!context->Computed)
 {
 SHA1PadMessage(context);
 context->Computed = 1;
 }

 return 1;
}

/*
 *  SHA1Input
 *
 *  Description:
 *      This function accepts an array of octets as the next portion of
 *      the message.
 *
 *  Parameters:
 *      context: [in/out]
 *          The SHA-1 context to update
 *      message_array: [in]
 *          An array of characters representing the next portion of the
 *          message.
 *      length: [in]
 *          The length of the message in message_array
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1Input(     SHA1Context  *context,
 const char   *message_array,
 unsigned     length)
{
 if (!length)
 {
 return;
 }

 if (context->Computed || context->Corrupted)
 {
 context->Corrupted = 1;
 return;
 }

 while(length-- && !context->Corrupted)
 {
 context->Message_Block[context->Message_Block_Index++] =
 (*message_array & 0xFF);

 context->Length_Low += 8;
 /* Force it to 32 bits */
 context->Length_Low &= 0xFFFFFFFF;
 if (context->Length_Low == 0)
 {
 context->Length_High++;
 /* Force it to 32 bits */
 context->Length_High &= 0xFFFFFFFF;
 if (context->Length_High == 0)
 {
 /* Message is too long */
 context->Corrupted = 1;
 }
 }

 if (context->Message_Block_Index == 64)
 {
 SHA1ProcessMessageBlock(context);
 }

 message_array++;
 }
}

/*
 *  SHA1ProcessMessageBlock
 *
 *  Description:
 *      This function will process the next 512 bits of the message
 *      stored in the Message_Block array.
 *
 *  Parameters:
 *      None.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *      Many of the variable names in the SHAContext, especially the
 *      single character names, were used because those were the names
 *      used in the publication.
 *
 *
 */
void SHA1ProcessMessageBlock(SHA1Context *context)
{
 const unsigned K[] =            /* Constants defined in SHA-1   */
 {
 0x5A827999,
 0x6ED9EBA1,
 0x8F1BBCDC,
 0xCA62C1D6
 };
 int         t;                  /* Loop counter                 */
 unsigned    temp;               /* Temporary word value         */
 unsigned    W[80];              /* Word sequence                */
 unsigned    A, B, C, D, E;      /* Word buffers                 */

 /*
 *  Initialize the first 16 words in the array W
 */
 for(t = 0; t < 16; t++)
 {
 W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
 W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
 W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
 W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
 }

 for(t = 16; t < 80; t++)
 {
 W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
 }

 A = context->Message_Digest[0];
 B = context->Message_Digest[1];
 C = context->Message_Digest[2];
 D = context->Message_Digest[3];
 E = context->Message_Digest[4];

 for(t = 0; t < 20; t++)
 {
 temp =  SHA1CircularShift(5,A) +
 ((B & C) | ((~B) & D)) + E + W[t] + K[0];
 temp &= 0xFFFFFFFF;
 E = D;
 D = C;
 C = SHA1CircularShift(30,B);
 B = A;
 A = temp;
 }

 for(t = 20; t < 40; t++)
 {
 temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
 temp &= 0xFFFFFFFF;
 E = D;
 D = C;
 C = SHA1CircularShift(30,B);
 B = A;
 A = temp;
 }

 for(t = 40; t < 60; t++)
 {
 temp = SHA1CircularShift(5,A) +
 ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
 temp &= 0xFFFFFFFF;
 E = D;
 D = C;
 C = SHA1CircularShift(30,B);
 B = A;
 A = temp;
 }

 for(t = 60; t < 80; t++)
 {
 temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
 temp &= 0xFFFFFFFF;
 E = D;
 D = C;
 C = SHA1CircularShift(30,B);
 B = A;
 A = temp;
 }

 context->Message_Digest[0] =
 (context->Message_Digest[0] + A) & 0xFFFFFFFF;
 context->Message_Digest[1] =
 (context->Message_Digest[1] + B) & 0xFFFFFFFF;
 context->Message_Digest[2] =
 (context->Message_Digest[2] + C) & 0xFFFFFFFF;
 context->Message_Digest[3] =
 (context->Message_Digest[3] + D) & 0xFFFFFFFF;
 context->Message_Digest[4] =
 (context->Message_Digest[4] + E) & 0xFFFFFFFF;

 context->Message_Block_Index = 0;
}

/*
 *  SHA1PadMessage
 *
 *  Description:
 *      According to the standard, the message must be padded to an even
 *      512 bits.  The first padding bit must be a '1'.  The last 64
 *      bits represent the length of the original message.  All bits in
 *      between should be 0.  This function will pad the message
 *      according to those rules by filling the Message_Block array
 *      accordingly.  It will also call SHA1ProcessMessageBlock()
 *      appropriately.  When it returns, it can be assumed that the
 *      message digest has been computed.
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to pad
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *
 */
void SHA1PadMessage(SHA1Context *context)
{
 /*
 *  Check to see if the current message block is too small to hold
 *  the initial padding bits and length.  If so, we will pad the
 *  block, process it, and then continue padding into a second
 *  block.
 */
 if (context->Message_Block_Index > 55)
 {
 context->Message_Block[context->Message_Block_Index++] = 0x80;
 while(context->Message_Block_Index < 64)
 {
 context->Message_Block[context->Message_Block_Index++] = 0;
 }

 SHA1ProcessMessageBlock(context);

 while(context->Message_Block_Index < 56)
 {
 context->Message_Block[context->Message_Block_Index++] = 0;
 }
 }
 else
 {
 context->Message_Block[context->Message_Block_Index++] = 0x80;
 while(context->Message_Block_Index < 56)
 {
 context->Message_Block[context->Message_Block_Index++] = 0;
 }
 }

 /*
 *  Store the message length as the last 8 octets
 */
 context->Message_Block[56] = (context->Length_High >> 24 ) & 0xFF;
 context->Message_Block[57] = (context->Length_High >> 16 ) & 0xFF;
 context->Message_Block[58] = (context->Length_High >> 8 ) & 0xFF;
 context->Message_Block[59] = (context->Length_High) & 0xFF;
 context->Message_Block[60] = (context->Length_Low >> 24 ) & 0xFF;
 context->Message_Block[61] = (context->Length_Low >> 16 ) & 0xFF;
 context->Message_Block[62] = (context->Length_Low >> 8 ) & 0xFF;
 context->Message_Block[63] = (context->Length_Low) & 0xFF;

 SHA1ProcessMessageBlock(context);
}

int sha1_hash(const char *source, char *lrvar)
// ----------------------------------------------------------------------------
// HASH:es a string with SHA1 and stores resulting hash in lrvar variable
//
// Parameters:
//        source    Pointer to source string to HASH
//        lrvar     LR variable where base64 encoded string is stored
//
// Result
//        -1        Error
//        >0        Success. Actual value is length of HASH string
//
// Example:
//        sha1_hash( "abc", "sha1" )  // sha1=A9993E364706816ABA3E25717850C26C9CD0D89D
// ----------------------------------------------------------------------------
{
 SHA1Context sha;
 char buf[128];

 SHA1Reset(&sha);
 SHA1Input(&sha, source, strlen(source));

 if (!SHA1Result(&sha))
 {
 lr_error_message("SHA1 ERROR: Could not compute message digest");

 return -1;
 }
 else
 {
 // Clear Buffer
 memset(buf,0,sizeof(buf));

 // Store HASH in buffer
 sprintf(buf, "%08X%08X%08X%08X%08X", sha.Message_Digest[0],sha.Message_Digest[1],
 sha.Message_Digest[2],sha.Message_Digest[3],sha.Message_Digest[4]);

 // Save to LR variable
 lr_save_string(buf, lrvar);

 // Return length of string (>0 = success)
 return strlen(buf);
 }
}

28 thoughts on “SHA-1 hash for LoadRunner

  1. Do you knwo if this has any memory issues, I am using it in a checksum system for html calls and I eventually hit 1.8gb of ram on vugen and it crashes. It seems to burn through about 1-2 MB of memusage per call (sometimes more depending on the variable size passed (i have one that is 20K characters in lenght)

    • During my tests this function did not eat memory in the way you describe. I’ve not especially checked the memusage on the Controller/LoadGen during tests, but endurance tests (72h or more) have been successful with millions of calls to the SHA1() functions.

      I admit that I’ve not hashed 20K worth of data so can’t really say how it’ll behave with such large char arrays..

      You also mention VuGen as the program that crashes. This implies you are running test using vugen? If so then I’m pretty sure it’s VuGen’s bugs/faults that causes the memleak & crash. VuGen is not meant for prolonged “tests” and has proven to fail in most cases when enough iterations/time has passed in a script… (this is from experience with it, no definite proof here).

  2. thanks, I haven’t tried it in controller yet, (just playing back to prove code)

    It may be a vugen problem (I am generating about 700 _sha1’s per iteration)

  3. Hello Kim,
    I’m working on a WS-Security Password Digest and need a SHA1 Base64 encoded string. I used the code that you have listed but getting a different result. Doing a Base64 encoding of that didn’t give expected result either. Could you please help?

    Example Text to encode : test
    Expected Result: qUqP5cyxm6YcTAhz05Hph5gvu9M= (Similar result from http://webcodertools.com/hashstring)
    Result using your code: A94A8FE5CCB19BA61C4C0873D391E987982FBBD3

    Please advise. Thanks !

    • It may be due to the fact that the other implementation does the encoding on binary data. The code I have only supports text. It does pass SHA1 self-tests so there is probably something else wrong

  4. Hi Kim,

    There is a groovy script that is been used in our application.

    Client uses encrypted random seed value to generate HMAC code and send along random seed & HMAC code to server in Http Authorization header. Server retrieve the request from Cache to see if the seed has been issued by the server and then compares HMAC code generated by client with server generated to validate request. Handshake is implemented using interceptor using HandshakeSecurityInterceptor (BenSoft.Security). The format of authorozation Header is handshakeseed:seed,handshakehmac:hmaccode handshakeend

    Below Groovy script is used in SOAP UI tool for our project:
    /*
    * soapUI Pro, copyright (C) 2007-2011 eviware software ab
    */
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.security.InvalidKeyException;
    /*
    class Greet2
    {
    def name
    def log

    Greet2(who, log)
    {
    name = who;
    this.log = log
    }

    def salute() { log.info “huj tebe, $name” }
    def HMAC() {return “huj tebe, $name”}

    def static salute( who, log ) { log.info “huj tebe again $who!” }
    }
    */

    /**
    * @param secretKey
    * @param data
    * @return HMAC/SHA256 representation of the given string
    */
    def hmac_sha256(String secretKey, String data) {
    try { SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(“UTF-8”), “HmacSHA256”)
    Mac mac = Mac.getInstance(“HmacSHA256”)
    mac.init(secretKeySpec)
    byte[] digest = mac.doFinal(data.getBytes(“UTF-8”))
    return byteArrayToString(digest)
    } catch (InvalidKeyException e) { throw new RuntimeException(“Invalid key exception while converting to HMac SHA256”)
    }
    }

    private def byteArrayToString(byte[] data) {
    BigInteger bigInteger = new BigInteger(1, data)
    String hash = bigInteger.toString(16)
    //Zero pad it
    while (hash.length() < 64) {
    hash = "0" + hash
    }
    return hash
    }
    def key = testRunner.testCase.getPropertyValue("secretKey")
    log.info "key: $key"
    def response = context.expand( '${GetSeed#Response}' )
    log.info "seed: $response"
    log.info "key: $key"
    def hmac = hmac_sha256("$key", "$response")

    log.info "hmac: $hmac"
    context.x = hmac.toUpperCase()

    Similar script/logic needs to be created for Loadrunner. Can you please help?

    WIth the REST scripting in LR(web http/html), we are able to generate the GetSeedResponse. But this response has to generate HMAC/SHA256 64bit secret key which will be then feeded for the next requests.

    Please advice. Thanks!!

    • In almost all these cases there is an option to turn OFF this validation on the server side. It is common to do this since many tools (like LR) lack the proper ciphers/hashes to handle these situations.

      Unfortunately I don’t have a complete HMAC working for LR at the moment either.

  5. Getting this error whicle including and compilling my vugen script.

    Error: CCI compilation error -In file included from c:\users\bkushwah\desktop\qpass\telenore\scripts\qpass_bundle3_sell\\combined_QPass_Bundle3_Sell.c:2:
    .
    Error: CCI compilation error -globals.h:9: C:\Users\bkushwah\Desktop\QPass\Telenore\Scripts\QPass_Bundle3_Sell\lr_sha1.c: Invalid argument
    .
    Error: Vuser compilation failed. Please set CCIDebug to Off in CCI section of mdrv.dat file.
    Warning: Extension cciext.dll reports error -19797 on call to function ExtPerProcessInitialize
    Error: Thread Context: Call to service of the driver failed, reason – thread context wasn’t initialized on this thread.

  6. Hi Kim,

    currently the output is encoded in hexadecimal string , so we obtain 40 chars. How should we proceed to obtain only 28 chars like with SOPA UI. I mean that how should be the source update to store a binary string not an hexa one and obtain exactly 28 chars in output.

    • The function
      int sha1_hash(const char *source, char *lrvar)

      does not output the BINARY version of the hash, but inside that function you can see how I convert the binary data to a string:

      // Store HASH in buffer
      sprintf(buf, “%08X%08X%08X%08X%08X”, sha.Message_Digest[0],sha.Message_Digest[1],
      sha.Message_Digest[2],sha.Message_Digest[3],sha.Message_Digest[4]);

      So by using the sha.Message_Digest array you can obtain the binary version.

  7. Hi Kim, as per your code
    // HASH the string “The quick brown fox jumps over the lazy dog”,
    // The result should be “2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12”

    But on http://www.cafewebmaster.com/online_tools/utf8_encode if you use sha-1 converter for “The quick brown fox jumps over the lazy dog” you get the sha-1 hash: “9e107d9d372bb6826bd81d3542a419d6”. It is 32 characters long and your function returns 40 characters long hexadecimal string. Is there a way to use sprintf() with different formatting or other options to have 32. What is the format of that 32 char. long string?

    • The hash produced by the SHA-1 is 160 bits long (20 bytes), and when written in HEX chars it becomes 40 characters long.

      I suspect the online version you reference is not correctly implemented. It seems the output is 32 chars, which corresponds to a MD5 hash of the string. And Indeed after trying MD5 on the string I get the exact output you say you get. So the conclusion is that the webmaster.com site has a bug 🙂

      A working online SHA-1 hasher can be found here http://www.sha1-online.com/

      Please have a look at https://en.wikipedia.org/wiki/SHA-1 for details of the SHA-1 algorithm. On the wiki page there is a section called “Examples and pseudocode” where the exact same phrase is hashed, and produces the 40 character string.

      • Hi Kim, thank you so much your response and explanation/clarification. I looked at wiki/SHA-1. Thanks – and I will need to revisit it few more times to make complete sense to me :). Wow, you are checking and replying! Fantastic! Good man!
        I would like to ask for your comments on this one.
        From my SoapUI Project with successfully submitted request we can see:
        kzJdC6gk6LY2XfelUm8yovGwDsw=
        XHTKRgpfZV/sp3XV8jYrBQ==

        And as per respective specs from: https://www.oasis-open.org/committees/download.php/13392/wss-v1.1-spec-pr-UsernameTokenProfile-01.htm#_Toc104276211
        we can conclude: Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )

        Also, in specs there is a note: “Note that the nonce is hashed using the octet sequence of its decoded value while the timestamp is hashed using the octet sequence of its UTF8 encoding as specified in the contents of the element.”

        In your function: SHA1Input Description: This function accepts an array of octets as the next portion of the message. So, looks to me you are using octet sequence but as an input you are passing the string “The quick brown fox jumps over the lazy dog”.

        Ok, here are my steps of my example script:
        //first generate nonce (random 16 digits number), Current DT, my password
        “sNonce” = “1000021265121134”
        “DateTime” = “2015-06-26T11:08:03.300Z”
        “sPassword = mywebservicpassword”
        // concatenate above 3 strings to pass it as input to lr_sha1()
        “s_Nonce_CT_Pwd = 10000212651211342015-06-26T11:08:03.300Zmywebservicpassword”
        //Next, use Kim’s lr_sha1 function to hash strings (I played with all combinations here)
        “_sha1_sNonce = D403A88EFB50E2037E5AF96F32B9BBB414E89669”
        “_sha1_DateTime = E172FDE10828627AE891380D3DFEBD8B146A310E”
        “_sha1_password = 04E44F87FF718C141A17CD06A3505111F5217DF8”
        “s_sha1_Nonce_CT_Pwd = 894275F4EBDE1B908EAF18065206ABE4D1210541”

        //Next, use “Base64 Encode/Decode for LoadRunner” posted by Kim to encode sha-1 strings
        // finally, decode to double-check
        “b64_sNonce = MTAwMDAyMTI2NTEyMTEzNA==”
        “b64_sha1_sNonce = RDQwM0E4OEVGQjUwRTIwMzdFNUFGOTZGMzJCOUJCQjQxNEU4OTY2OQ==”
        “b64_password = bXl3ZWJzZXJ2aWNwYXNzd29yZA==”
        “b64_sha1_password = MDRFNDRGODdGRjcxOEMxNDFBMTdDRDA2QTM1MDUxMTFGNTIxN0RGOA==”
        “b64_DateTime = MjAxNS0wNi0yNlQxMTowODowMy4zMDBa”
        “b64_sha1_DateTime = RTE3MkZERTEwODI4NjI3QUU4OTEzODBEM0RGRUJEOEIxNDZBMzEwRQ==”
        “b64_s_sha1_Nonce_CT_Pwd = ODk0Mjc1RjRFQkRFMUI5MDhFQUYxODA2NTIwNkFCRTREMTIxMDU0MQ==”
        “b64_sNonce_Decoded = 1000021265121134”
        “b64_sha1_sNonce_Decoded = D403A88EFB50E2037E5AF96F32B9BBB414E89669”
        “b64_DateTime_Decoded = 2015-06-26T11:08:03.300Z”
        “b64_sha1_DateTime” = “RTE3MkZERTEwODI4NjI3QUU4OTEzODBEM0RGRUJEOEIxNDZBMzEwRQ==”
        “b64_s_sha1_Nonce_CT_Pwd_decoded = 894275F4EBDE1B908EAF18065206ABE4D1210541”
        //?????? strange thing what happen with decoded values of the SoauUI strings.
        “Decoded sSoapUI_NonceBase64Binary = \tÊF\n_e_ì§uÕò6+\x05” (here is from SoapUI: “sSoapUI_NonceBase64Binary = XHTKRgpfZV/sp3XV8jYrBQ==” but decoded is blah…)
        “Decoded sSoapUI_PasswordDigest = “2]\x0b¨$è¶6]÷¥Ro2¢ñ°\x0eÌ” (here is from SoapUI: “sSoapUI_PasswordDigest = kzJdC6gk6LY2XfelUm8yovGwDsw” but decoded is blah….)

        Conclusion:
        My script generate strings with different (too many – wrong format) number of characters than valid request sent by SoauUI Project. (I played with all possible combinations)
        Format in SoapUI of the valid request
        Password equivalent: PasswordDigest”>kzJdC6gk6LY2XfelUm8yovGwDsw= (28 character long string)
        Nonce: Base64Binary”>XHTKRgpfZV/sp3XV8jYrBQ== (24 characters long string)

        Is it possible that instead sha-1 my project using MD5 or similar? I was advised by my developer that SHA-1 with above specs is in use.
        Thanks Kim in advance.

      • I think you have a situation where SoapUI is using the HASH/Digest results in binary format, and not as HEX strings (like I produce in my SHA1 function).

        The fact that Base64 is used, indicates that the HASH is stored as binary (for SHA1 this is 20 bytes) and when that is Base64 encoded it becomes 1.4 times bigger (approx) to 28 characters.

        If you really want to manipulate the binary HASH/Digest have a look at my code in the function sha1_hash() around lines 483-484. There I take the binary hash/digest and create a string of hex chars out of it. It is that binary stuff that you need to encode with Base64 to get the same results as in SoapUI.

        The MD5 Hash algorithm I have not implemented yet in code for LR. I do however have a DLL that covers that algorithm, and many others as well..

        As for the Octet thing: My function accepts an Octet Buffer, that just happens to be an ASCII string 🙂 The problem with C code is that a binary octet buffer may contain 0 characters that effectively terminates the strings. Binary buffers are seldomly used in LR and are an advanced topic as you need to manipulate memory buffers with pointers and sizes etc. You could easily make a new function sha1_hash_buffer() that takes a buffer pointer and its size. This is what I do internally in the sha1_hash() function using the strlen() for the supplied string.

        References:
        Base64 – https://en.wikipedia.org/wiki/Base64

  8. Hi – Any support code for LR with SHA512? trying to create a HASH code based on Entery point, CompanyID, SSOPassword, etc which Application is using

Leave a reply to Kim Cancel reply