I'm struggling with implementing WS-Security in Progress ABL.
It seems possible, this knowledgebase: http://knowledgebase.progress.com/articles/Article/P88147 states:
- For outgoing WS-Security, create SOAP Headers containing the WS-Security content manually using the OpenEdge Web Service client.
First of all I'm struggling with the creation of a base64 encoded and SHA1 digested password hash.
Password digest according to the WS-Security specification. Source.
Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
Nonce is a random string of a specific length, could basically be a UUID. The nonce is Base64-encoded (it's sent encoded in the soap header).
Created is a date in the format "YYYY-MM-DDTHH:MM:SS.SSSZ". Milliseconds are optional. Z for GMT should always be set.
Password is a password given out by the web service provider.
Trying to mimic this answer on SO: Working algorithm for PasswordDigest in WS-Security
PROCEDURE generatePassHash:
DEFINE INPUT PARAMETER pcNonce AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcCreated AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcPassword AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAMETER pcHash AS CHARACTER NO-UNDO.
DEFINE VARIABLE mBytes AS MEMPTR NO-UNDO.
DEFINE VARIABLE cNonceDecoded AS CHARACTER NO-UNDO.
/* Base64-decode the nonce since it's in encoded format */
ASSIGN
cNonceDecoded = STRING(BASE64-DECODE(pcNonce)).
/* Set size of mempointer */
SET-SIZE(mBytes) = LENGTH(cNonceDecoded) + LENGTH(pcCreated) + LENGTH(pcPassword) + 1.
/* Put the decoded nonce first */
PUT-STRING(mBytes, 1) = cNonceDecoded.
/* Add create time */
PUT-STRING(mBytes, 1 + LENGTH(cNonceDecoded)) = pcCreated.
/* Add password */
PUT-STRING(mBytes, 1 + LENGTH(cNonceDecoded) + LENGTH(pcCreated)) = pcPassword.
/* Create out-data */
pcHash = STRING(BASE64-ENCODE(SHA1-DIGEST(mBytes))).
/* Clean up mempointer */
SET-SIZE(mBytes) = 0.
END PROCEDURE.
DEFINE VARIABLE cNonce AS CHARACTER NO-UNDO.
DEFINE VARIABLE cTimeStamp AS CHARACTER NO-UNDO.
DEFINE VARIABLE cClearPass AS CHARACTER NO-UNDO.
DEFINE VARIABLE cRightAnswer AS CHARACTER NO-UNDO.
ASSIGN
cNonce = "UIYifr1SPoNlrmmKGSVOug=="
cTimeStamp = "2009-12-03T16:14:49Z"
cClearPass = "test8"
cRightAnswer = "yf2yatQzoaNaC8BflCMatVch/B8=".
RUN generatePassHash(cNonce, cTimeStamp, cClearPass, OUTPUT cHash).
MESSAGE "Is:" SKIP cHash SKIP(2)
"Should be:" SKIP
cRightAnswer
VIEW-AS ALERT-BOX INFORMATION TITLE "OK?".
I'm guessing that this might have to do with either me messing up the mempointer handling or the fact that everything should be UTF-8?
NB
I can easily produce the same erroneous hash as mentioned in the referenced question above with this simple code:
MESSAGE STRING(BASE64-ENCODE(SHA1-DIGEST("UIYifr1SPoNlrmmKGSVOug==" + "2009-12-03T16:14:49Z" + "test8"))) VIEW-AS ALERT-BOX
PUT-STRING will, if no length is specified, put a NULL terminated string into your MEMPTR - which is why you needed to set the length to + 1 - which was the wrong solution to 'Can't PUT past the end of the MEMPTR. (4791)'
Remove + 1 from SET-SIZE and change password to:
PUT-STRING(mBytes, 1 + LENGTH(cNonceDecoded) + LENGTH(pcCreated), LENGTH(pcPassword)) = pcPassword.