【问题标题】:Creating jwt after signing with apple in php在php中用apple签名后创建jwt
【发布时间】:2021-09-13 01:20:31
【问题描述】:

我有一个关于与苹果更准确地签名的问题,如何在登录后在 php 中创建 jw 令牌,这就是我所做的:

$pem_content = 
"-----BEGIN PRIVATE KEY-----".
"XXXX".
"-----END PRIVATE KEY-----";
$header = json_encode(array(
  'kid' => $keyId,
  'alg'  => 'ES256'
));
$decodedTokenData =  json_decode(base64_decode(str_replace('_', '/', str_replace('-','+',explode('.', $_POST['id_token'])[1]))),true);

$payload = json_encode(array(
    'iss' => $teamid,
    'iat' => time(),
    'exp' => time() + 86400*180,
     'aud' => $decodedTokenData['aud'],
    'sub' => $decodedTokenData['sub'],
));
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));
$key = //I don't know how to use $pem_content here
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $key, true);
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
$jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;

为了测试我做的交易

curl -X POST https://appleid.apple.com/auth/token -d  '{"client_id":$CLIENTID,"client_secret":$jwt,"code": $_POST['code'],"grant_type":"authorization_code","redirect_uri":$URL}'

但我明白了

{"error":"invalid_client"}

这里有两个限制:我必须只使用 php 并避免使用外部库

提前致谢

编辑我像这样生成 jwt:

$signature = '';
openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature,openssl_get_privatekey($pem_content),'SHA256');
$signature = $this->signatureFromDER($signature, 256);

使用的函数来自这里https://github.com/firebase/php-jwt/blob/d2113d9b2e0e349796e72d2a63cf9319100382d2/src/JWT.php#L172

现在 curl 命令是

curl -X POST "https://appleid.apple.com/auth/token" -H 'content-type: application/x-www-form-urlencoded' -d"client_id=CLIENT_ID&client_secret=CLIENT_SECRET&code=CODE&grant_type=authorization_code&redireect_uri=https%3A%2F%2Fsite.com%2Findex.php"

但我仍然得到 invalid_client

编辑2: 我没有实现这个解决方案: Apple Sign In "invalid_client", signing JWT for authentication using PHP and openSSL

但我一直有 invalid_client 错误

EDIT3 即使使用https://github.com/firebase/php-jwt 我也会收到错误

【问题讨论】:

  • 看看这里的实现:github.com/firebase/php-jwt/blob/…。跳出来的一件事是使用openssl_sign 代替ES256 而不是hash_hmac
  • 哦,还有一件事可能会让您感到困惑,根据文档 (developer.apple.com/documentation/sign_in_with_apple/…),该 POST 到 https://appleid.apple.com/auth/token 的正文应该是 application/x-www-form-urlencoded 类型。
  • 感谢您的帮助 msbit,现在我做 $signature = ''; openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $pem_content,'ES256'); $signature = $this->signatureFromDER($signature, 256); jwt 是有效的(我已经在 jwt.io 上测试过),但我仍然有一个 'invalid_client'
  • 错字,openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature,$pem_content,'ES256');
  • 错字,alg 缺少尾引号。

标签: php curl jwt


【解决方案1】:

所以问题是子值

这里是生成jwt的代码:

function generateJWT() {
        $kid =  "...";
        $iss = '...';
        $sub = '...';
        $header = [
            'alg' => 'ES256',
            'kid' => $kid
        ];
        $body = [
            'iss' => $iss,
            'iat' => time(),
            'exp' => time() + 3600,
            'aud' => 'https://appleid.apple.com',
            'sub' => $sub
    ];

    $pem_content = <<<EOD
-----BEGIN PRIVATE KEY-----
XXXXX
-----END PRIVATE KEY-----
EOD;
        $privKey = openssl_pkey_get_private($pem_content);
        if (!$privKey){
           return false;
        }

        $payload = $this->encode(json_encode($header)).'.'.$this->encode(json_encode($body));

        $signature = '';
        $success = openssl_sign($payload, $signature, $privKey, OPENSSL_ALGO_SHA256);
        if (!$success) return false;

        $raw_signature = $this->fromDER($signature, 64);

        return $payload.'.'.$this->encode($raw_signature);
    }

    public function encode($data){
        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data)); 
    }      

    public static function fromDER(string $der, int $partLength)
    {
        $hex = unpack('H*', $der)[1];
        if ('30' !== mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE
            throw new \RuntimeException();
        }
        if ('81' === mb_substr($hex, 2, 2, '8bit')) { // LENGTH > 128
            $hex = mb_substr($hex, 6, null, '8bit');
        } else {
            $hex = mb_substr($hex, 4, null, '8bit');
        }
        if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER
            throw new \RuntimeException();
        }
        $Rl = hexdec(mb_substr($hex, 2, 2, '8bit'));
        $R = self::retrievePositiveInteger(mb_substr($hex, 4, $Rl * 2, '8bit'));
        $R = str_pad($R, $partLength, '0', STR_PAD_LEFT);
        $hex = mb_substr($hex, 4 + $Rl * 2, null, '8bit');
        if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER
            throw new \RuntimeException();
        }
        $Sl = hexdec(mb_substr($hex, 2, 2, '8bit'));
        $S = self::retrievePositiveInteger(mb_substr($hex, 4, $Sl * 2, '8bit'));
        $S = str_pad($S, $partLength, '0', STR_PAD_LEFT);
        return pack('H*', $R.$S);
    }
    /**
     * @param string $data
     *
     * @return string
     */
    private static function preparePositiveInteger(string $data)
    {
        if (mb_substr($data, 0, 2, '8bit') > '7f') {
            return '00'.$data;
        }
        while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') <= '7f') {
            $data = mb_substr($data, 2, null, '8bit');
        }
        return $data;
    }
    /**
     * @param string $data
     *
     * @return string
     */
    private static function retrievePositiveInteger(string $data)
    {
        while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') > '7f') {
            $data = mb_substr($data, 2, null, '8bit');
        }
        return $data;
    }

【讨论】:

    猜你喜欢
    • 2021-08-16
    • 2022-10-31
    • 2019-10-30
    • 1970-01-01
    • 2019-12-12
    • 2016-12-27
    • 2017-11-14
    • 1970-01-01
    • 2017-03-02
    相关资源
    最近更新 更多