【问题标题】:WS-Security in OpenEdge, continuedOpenEdge 中的 WS-Security,续
【发布时间】:2015-07-07 06:08:49
【问题描述】:

作为对这个问题的跟进:Implementing WS-Security in Progress ABL,我将继续努力实现 WS-Security in Progress OpenEdge。

我的问题:

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

  • “nonce” - 随机字符串
  • 时间戳 - 当前时间
  • 密码 - 我和网络服务提供商之间的共享秘密。

然后将随机数、时间戳和摘要添加到 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).

我所知道的:

这在 10 000 个请求中的 9 500 个这样的情况下运行良好。但是有5%的失败率。不幸的是,错误消息没有帮助,所以我真正能看到的是登录失败。 Web 服务提供商指出,由于摘要不正确,登录被拒绝。

我做了什么:

为了测试我的摘要程序,我创建了一个小型 Python 程序。当我使用来自失败登录的输入数据(随机数和时间戳)进行尝试时,这确实会创建不同的摘要。然而,我不是 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")

我的真实密码当然不是“SECRET”,这让我相信错误与随机数有关。将密码更改为“SECRET”会使摘要不同,但之后 Progress 和 Python 摘要之间的差异仍然存在(上面的第一个示例在更改前后生成了类似的摘要,但第二个没有)。

我有一个进度支持的未决案例,但他们似乎和我一样在这方面苦苦挣扎。

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

【问题讨论】:

  • 您是说他们的 python 代码和进度代码在访问此 Web 服务时都以 5% 的速度失败?对我来说,有两个完全不同的实现失败似乎问题更可能出在 Web 服务上。 Web 服务提供商是否有他们认为可以正常工作的参考实现或代码示例?
  • 不,也许我描述的不是很好。它们在 5% 的情况下有所不同 - 那些 Progress 登录实际失败的情况。这使我认为 4GL 存在问题,而不是服务中存在问题。不幸的是,所有示例代码都适用于不同的 java 和 .net 框架,如 Axis2 或 JAX-WS。这些框架似乎负责实际的摘要生成。一个想法是它必须与代码页转换有关?我们在 iso8859-1 中运行所有内容,我猜应该使用 UTF,也许它很少(约 5%)受其影响的字符是偶然创建的。 (一个疯狂的猜测)。
  • 好的,这更有意义。你能用不同的 -cpinternal 和 -cpstream 值进行实验吗?
  • 是的,不过要等到明天
  • 文档说,如果 sha1-digest() 的参数是 char 或 longchar,它将被转换为 UTF-8 以避免代码页问题,但不会为原始或 mempr 值。当您将它们传递给 sha1-digest() 时,您在 memprs 中拥有 nonce,然后是 nonce + 密码。

标签: python soap ws-security progress-4gl openedge


【解决方案1】:

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

正如@TomBascom 指出的那样,问题与代码页转换有关,但实际错误在“链”中确实比 SHA 消化更早。

cNonceB64      = BASE64-ENCODE(GENERATE-RANDOM-KEY)
cNonce         = STRING(BASE64-DECODE(cNonceB64))

在第二行中,只要生成的密钥包含 iso8859-1 和 UTF-8 之间不匹配的值,cNonce 的值就会被破坏。

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

/* 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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-16
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 2014-03-02
    • 2014-11-25
    • 2012-01-14
    • 2013-10-26
    相关资源
    最近更新 更多