Python OpenEdge中的WS-Security,续

Python OpenEdge中的WS-Security,续,python,soap,ws-security,progress-4gl,openedge,Python,Soap,Ws Security,Progress 4gl,Openedge,作为这个问题的后续:我将继续努力在OpenEdge中实现WS-Security 我的问题: 对特定web服务的每个请求,我都会根据以下内容生成密码摘要: “nonce”-随机字符串 时间戳—当前时间 密码-我和web服务提供商之间的共享秘密 然后将nonce、timestamp和digest添加到web服务调用的Soap头中 这在大多数情况下工作正常,但在100个请求中有5个请求失败(请参阅下面的更多信息) 以下是我生成摘要的方式: PROCEDURE generatePassHashNon

作为这个问题的后续:我将继续努力在OpenEdge中实现WS-Security

我的问题:

对特定web服务的每个请求,我都会根据以下内容生成密码摘要:

  • “nonce”-随机字符串
  • 时间戳—当前时间
  • 密码-我和web服务提供商之间的共享秘密
然后将nonce、timestamp和digest添加到web服务调用的Soap头中

这在大多数情况下工作正常,但在100个请求中有5个请求失败(请参阅下面的更多信息)

以下是我生成摘要的方式:

PROCEDURE generatePassHashNonceClear:

/*------------------------------------------------------------------------------
  Purpose: 
    Generates a password hash for WS-Security

  General algorithm:
    Digest = base64(sha1(Nonce +  Timestamp + sha1(Pwd))) 
------------------------------------------------------------------------------*/
    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 mSHA1         AS MEMPTR      NO-UNDO.

   /* 
    Set size of mempointer, add 20 since we are adding the 20 byte 
    SHA1-DIGEST of the clear password in the end.
    */
    SET-SIZE(mBytes) = LENGTH(pcNonce) + LENGTH(pcCreated) + 20.

    /* Put the decoded nonce first */
    PUT-STRING(mBytes, 1) = pcNonce.

    /* Add create time */
    PUT-STRING(mBytes, 1 + LENGTH(pcNonce)) = pcCreated.

    /* Set SHA1 returns a 20 byte raw string. */
    SET-SIZE(mSHA1) = 20.
    mSHA1 = SHA1-DIGEST(pcPassword).

    /* Add password, SHA1-digested (so we need to put bytes instead of a string */
    PUT-BYTES(mBytes, 1 + LENGTH(pcNonce) + LENGTH(pcCreated)) = mSHA1.

    /* Create out-data in B64-encoded format */
    pcHash = STRING(BASE64-ENCODE(SHA1-DIGEST(mBytes))).

    /* Clean up mempointers */
    SET-SIZE(mBytes) = 0.
    SET-SIZE(mSHA1)  = 0.

END PROCEDURE.
这就是过程的调用方式:

DEFINE VARIABLE cPasswordClear   AS CHARACTER   NO-UNDO.
DEFINE VARIABLE dtZuluNow        AS DATETIME    NO-UNDO.
DEFINE VARIABLE cCreated         AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cNonceB64        AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cNonce           AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cPasswordDigest  AS CHARACTER   NO-UNDO.

/* 
Get time in UTC/GMT/ZULU/Timezone 0 and store 
it with 000 as milliseconds + Z for timezone Zulu 

Nonce is a random generated string 
*/
ASSIGN 
    dtZuluNow      = DATETIME-TZ(NOW,0)
    cCreated       = STRING(dtZuluNow, "9999-99-99THH:MM:SS") + ":000Z"
    cPasswordClear = "SECRET"
    cNonceB64      = BASE64-ENCODE(GENERATE-RANDOM-KEY)
    cNonce         = STRING(BASE64-DECODE(cNonceB64)).


RUN generatePassHashNonceClear( cNonce, cCreated, cPasswordClear, OUTPUT cPasswordDigest).
我所知道的:

这在10000个请求中的9500个请求中工作得很好。但有5%的失败率。不幸的是,错误消息没有帮助,所以我真正能看到的是登录失败。web服务提供商声明,由于摘要不正确,登录被拒绝

我所做的:

为了测试我的摘要过程,我创建了一个小型python程序。当我尝试使用来自失败登录的in数据(nonce和timestamp)时,这确实会创建不同的摘要。然而,我不是Python程序员,所以这个程序很可能有问题(但这将是一个非常奇怪的巧合,它在所有情况下都应该在相同的95%下工作)

下面是python程序:

import hashlib

def createDigest(Nonce, Created, Password):
    "This function returns a digest"

    NonceB64 = Nonce.decode("base64","strict")

    pdgst = hashlib.sha1()
    pdgst.update(Password)
    PasswordDgst = pdgst.digest()


    FinalDgst = hashlib.sha1()
    FinalDgst.update(NonceB64)
    FinalDgst.update(Created)
    FinalDgst.update(PasswordDgst)

    FinalTxt = FinalDgst.digest().encode("base64","strict")
    print "Final digest : " + FinalTxt

    return

print "This digest is repeated in Progress OpenEdge"
createDigest("tGxF8+DAmJvQo93PNZt5Nw==", "2015-04-08T20:10:44:000Z", "SECRET")

print "This digest isn't repeated in Progress OpenEdge"
createDigest("XdcAW1TdTr+MLp4t0QkJ8g==", "2015-04-08T20:10:44:000Z", "SECRET")
我的真实密码当然不是“秘密”,这让我相信这个错误与nonce有关。将密码更改为“SECRET”会使摘要有所不同,但之后Progress和Python摘要之间仍然存在差异(上面的第一个示例在更改前后生成了类似的摘要,但第二个没有)

我有一个进展支持的公开案例,但他们似乎和我一样努力解决这个问题


我已经在RHEL和Windows 7上的OpenEdge 11.3.1和11.4中对此进行了测试,其行为保持不变。

回答我自己的问题以备将来参考:

正如@TomBascom所指出的,这个问题与代码页转换有关,但实际的错误在“链”中的出现时间要早于SHA

cNonceB64      = BASE64-ENCODE(GENERATE-RANDOM-KEY)
cNonce         = STRING(BASE64-DECODE(cNonceB64))
在第二行中,每当生成的键包含iso8859-1和UTF-8之间不匹配的值时,cNonce的值就会被销毁

简单的解决方案是将cNonce变量更改为mempointer,然后重写生成摘要的过程

/* Optimistic, should really be based on current symmetric encryption algorithm */
SET-SIZE(mNonce) = 16.

ASSIGN
  mNonce    = GENERATE-RANDOM-KEY
  cNonceB64 = BASE64-ENCODE(mNonce).
然后是生成密码摘要的新过程:

PROCEDURE generateDigest:

/*------------------------------------------------------------------------------
  Purpose:     Generates a password hash for WS-Security
  Parameters:  <none>
  Notes:       
------------------------------------------------------------------------------*/

    DEFINE INPUT  PARAMETER mNonce     AS MEMPTR      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 mSHA1         AS MEMPTR      NO-UNDO.

    /* 
    Set size of mempointer, add 20 since we are adding the 20 byte 
    SHA1-DIGEST of the clear password in the end.
    */
    SET-SIZE(mBytes) = LENGTH(pcCreated) + 36. /* 16 + 20 = 36 */

    /* Put the decoded nonce first */
    PUT-BYTES(mBytes, 1) = mNonce.

    /* Add create time */
    PUT-STRING(mBytes, 17) = pcCreated. /* 16 + 1 = 17 */

    /* Set SHA1 returns a 20 byte raw string. */
    SET-SIZE(mSHA1) = 20.
    mSHA1 = SHA1-DIGEST(pcPassword).

    /* Add password, SHA1-digested (so we need to put bytes instead of a string */
    PUT-BYTES(mBytes, 17 + LENGTH(pcCreated)) = mSHA1. /* 16 + 1 = 17 */

    /* Create out-data in B64-encoded format */
    pcHash = STRING(BASE64-ENCODE(SHA1-DIGEST(mBytes))).

    /* Clean up mempointers */
    SET-SIZE(mBytes) = 0.
    SET-SIZE(mSHA1)  = 0.
    SET-SIZE(mNonce) = 0.
END PROCEDURE.
过程生成最大值:
/*------------------------------------------------------------------------------
目的:为WS-Security生成密码哈希
参数:
笔记:
------------------------------------------------------------------------------*/
将输入参数mNonce定义为MEMPTR NO-UNDO。
将输入参数pcCreated定义为字符NO-UNDO。
将输入参数pcPassword定义为字符NO-UNDO。
将输出参数pcHash定义为字符NO-UNDO。
将变量MB定义为MEMPTR NO-UNDO。
将变量mSHA1定义为MEMPTR NO-UNDO。
/* 
设置mempointer的大小,添加20,因为我们正在添加20字节
SHA1-最后清除密码的摘要。
*/
设置大小(MB)=长度(pcCreated)+36./*16 + 20 = 36 */
/*将解码后的nonce放在第一位*/
PUT-BYTES(兆字节,1)=兆字节一次。
/*添加创建时间*/
PUT-STRING(MB,17)=pcCreated./*16 + 1 = 17 */
/*SETSHA1返回一个20字节的原始字符串*/
设置大小(mSHA1)=20。
mSHA1=SHA1-DIGEST(pcPassword)。
/*添加密码,SHA1摘要(因此我们需要放入字节而不是字符串)*/
PUT-BYTES(MB,17+长度(pcCreated))=mSHA1/*16+1=17*/
/*以B64编码格式创建数据*/
pcHash=STRING(BASE64-ENCODE(SHA1-DIGEST(mBytes)))。
/*清理内存指针*/
设置大小(MB)=0。
设置大小(mSHA1)=0。
设置大小(mNonce)=0。
结束程序。

你是说python代码和progress代码在访问此web服务时都以5%的速率失败?对我来说,两个完全不同的实现失败似乎更可能是web服务的问题。web服务提供商是否有一个参考实现或代码示例,他们说可以工作s正确吗?不,可能我描述的不是很好。它们在5%的情况下有所不同-那些进度登录实际失败的情况。这让我认为4GL中存在问题,而不是服务中的问题。所有示例代码都是针对不同的java和.net框架(如Axis2或JAX-WS)的。这些框架似乎很小心关于acual digest一代。一个想法是它必须与代码页转换有关?我们在iso8859-1中运行所有东西,我猜应该使用UTF,可能受此影响的角色很少(约5%)是偶然创建的。(胡乱猜测).好的,这更有意义。你能用不同的-cpinternal和-cpstream值运行一个实验吗?是的,但要等到明天,尽管文档说如果sha1-digest()的参数是将转换为UTF-8以避免代码页问题的char或longchar,但不会对raw或memptr值进行转换。当您将nonce和nonce+密码传递给sha1-digest()时,会在memptr中进行转换。