oauth2.0 如何计算PCKE的code_verifier?

l0oc07j2  于 2023-05-16  发布在  其他
关注(0)|答案(4)|浏览(223)

我正在浏览Okta's PCKE Flow demo以更好地理解它的工作原理,但我在复制从code_verifier生成的相同code_challenge哈希时遇到了麻烦。以下是演示的屏幕截图:

使用Zg6klgrnixQJ629GsawRMV8MjWvwRAr-vyvP1MHnB6X8WKZN作为代码验证器,他们是如何生成
iF_7prUeJ6rr3jMG3LmhW3R1cZ2ecZavFqS0jtb6tzo作为代码挑战?
使用这个SHA256 hash calculatorBase64 Encoder,我得到了ODg1ZmZiYTZiNTFlMjdhYWViZGUzMzA2ZGNiOWExNWI3NDc1NzE5ZDllNzE5NmFmMTZhNGI0OGVkNmZhYjczYQ,它与iF_7prUeJ6rr3jMG3LmhW3R1cZ2ecZavFqS0jtb6tzo的期望值不匹配。我做错了什么,没有得到期望值?
这个SHA256 base 64 hash calculator from approsto给出了一个非常接近预期值的值。使用这个计算器,我得到了iF/7prUeJ6rr3jMG3LmhW3R1cZ2ecZavFqS0jtb6tzo,它与预期值相差一个字符(注意这里是/而不是_)。
我做了什么导致了这个差异?如何计算iF_7prUeJ6rr3jMG3LmhW3R1cZ2ecZavFqS0jtb6tzo的预期code_verifier值?谢谢

dsekswqp

dsekswqp1#

PKCE代码质询是验证器的Base64-URL编码的SHA 256散列。这意味着您需要获取原始字符串,计算它的SHA 256哈希,然后对哈希进行Base64-URL编码。这是一个很大的单词,所以让我们通过它。
你上面尝试做的事情有两个问题:
您找到的在线SHA 256哈希计算器将哈希输出为十六进制编码的字符串,而不是原始字节。这通常是有帮助的,但在这种情况下不是。因此,通过base64编码所做的下一件事是,您正在对哈希的十六进制表示进行base64编码,而不是原始字节。您需要使用一个哈希函数来输出原始字节,并将原始字节传递给base64-url-encoder。
下一个问题是你需要base64-url编码,而不是base64编码。Base64-URL编码是Base64编码的一个微小变化,其中唯一的区别是使用字符-而不是+_而不是/,并从末尾修剪=填充字符。这使得它是URL安全的,因为否则需要在URL中转义+/=字符。
因此,要计算PKCE代码挑战,您需要使用SHA 256函数,该函数可以给予原始字节,然后使用修改后的Base64编码函数对这些字节进行编码。
下面是一些PHP代码,可以做到这一点:

function pkce_code_challenge($verifier) {
    $hash = hash('sha256', $verifier, true);
    return rtrim(strtr(base64_encode($hash), '+/', '-_'), '=');
}

在浏览器中也可以使用纯JavaScript,但由于WebCrypto API的复杂性,代码稍长:

function sha256(plain) { 
    // returns promise ArrayBuffer
    const encoder = new TextEncoder();
    const data = encoder.encode(plain);
    return window.crypto.subtle.digest('SHA-256', data);
}

function base64urlencode(a) {
    // Convert the ArrayBuffer to string using Uint8 array.
    // btoa takes chars from 0-255 and base64 encodes.
    // Then convert the base64 encoded to base64url encoded.
    // (replace + with -, replace / with _, trim trailing =)
    return btoa(String.fromCharCode.apply(null, new Uint8Array(a)))
        .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

async function pkce_challenge_from_verifier(v) {
    hashed = await sha256(v);
    base64encoded = base64urlencode(hashed);
    return base64encoded;
}
ubbxdtey

ubbxdtey2#

基于Aaron的例子和破解pkce-challenge节点包,我使用的是:

class PkceChallenge {
    random(length, mask) {
        let result = "";
        let randomIndices = new Int8Array(length);
        window.crypto.getRandomValues(randomIndices);
        const byteLength = 256
        const maskLength = Math.min(mask.length, byteLength);
        const scalingFactor = byteLength / maskLength;

        for (var i = 0; i < length; i++) {
            result += mask[Math.floor(Math.abs(randomIndices[i]) / scalingFactor)];
        }
        return result;
    }

    base64UrlEncode(array) {
        return btoa(String.fromCharCode.apply(null, new Uint8Array(array)))
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=+$/, '');
    }

    generateVerifier(length) {
        const mask = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
        return this.random(length, mask);
    }

    generateChallenge(length = 43) {
        this.verifier = this.generateVerifier(length);

        const encoder = new TextEncoder();
        const data = encoder.encode(this.verifier);
        return window.crypto.subtle.digest('SHA-256', data).then(array => { return { code_challenge: this.base64UrlEncode(array), code_verifier: this.verifier }; });
    }
}
hpcdzsge

hpcdzsge3#

echo -n "qwe" | sha256sum -b | xxd -p -r | base64 | tr '/+' '_-' | tr -d '='

其中,qwe是code_verifier

x8goxv8g

x8goxv8g4#

我也遇到了同样的问题,想在Postman中创建code_challenge作为预请求脚本,并提出了以下内容。如果你想达到同样的目标,可能会对你有帮助。我的code_verifier设置在一个环境中,挑战值也将保存到环境中:

pm.environment.set(
    "code_challenge",
    CryptoJS.SHA256(pm.environment.get("code_verifier"))
        .toString(CryptoJS.enc.Base64)
        .replace("+", "-")
        .replace("/", "_")
        .replace(/=+$/, "")
)

相关问题