You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP无外部库生成HS512 JWT签名无效问题求助

不使用外部库生成HS512 JWT的签名无效问题修复

问题描述

自行编写PHP代码生成HS512算法的JWT,但生成的签名无法通过jwt.io验证。当前代码及生成的JWT如下:

原始代码

function gen_jwt():String{

    $signing_key = "changeme";
    
    // header always eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9
    $header = [ 
        "alg" => "HS512", 
        "typ" => "JWT" 
    ];
    $header = base64_url_encode(json_encode($header));
    log_message('debug',__CLASS__.'('.__FUNCTION__.':'.__LINE__.') json base64_url_encode: ' . $header);
    
    // test case 0 generates (on jwt.io): 
    //      with secret base64 encoded: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjB9.ZW0gOCJV4e1KgGEsw0bL7oCF1AI1PBL8VVgSoss4tmr7682p6DpNc1uGbBpOEfkPjKJv0JBnLvjH2XUbo8PHUg
    //      without secret b64 encoded: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjB9.pqzfdCTmr-eWW9sEgV-COCdS4dI7MDpCIFWss6kXnAC9eLdGX1qOOr8BtJih59o_U_AdHtBh8JwUQ4dEPTk0rg
    $payload =  [
        // "exp" => time() + ...,
        "exp" => 0,
    ];
    $payload = base64_url_encode(json_encode($payload));
    
    $signature = hash_hmac('sha512', "$header.$payload", $signing_key, false);
    log_message('debug',__CLASS__.'('.__FUNCTION__.':'.__LINE__.') signature: ' . $signature);
    $signature = base64_url_encode($signature);
    log_message('debug',__CLASS__.'('.__FUNCTION__.':'.__LINE__.') signature: ' . $signature);
    
    // all three parts b64 url-encoded 
    $jwt = "$header.$payload.$signature";
    log_message('debug',__CLASS__.'('.__FUNCTION__.':'.__LINE__.') jwt: ' . $jwt);

    return $jwt;
    
}

/**
 * per https://stackoverflow.com/questions/2040240/php-function-to-generate-v4-uuid/15875555#15875555
 */
function base64_url_encode($text):String{
    return str_replace(
        ['+', '/', '='],
        ['-', '_', ''],
        base64_encode($text)
    );
}

/**
 * per https://www.uuidgenerator.net/dev-corner/php
 */
function guidv4($data = null): String {
    // Generate 16 bytes (128 bits) of random data or use the data passed into the function.
    $data = $data ?? random_bytes(16);
    assert(strlen($data) == 16);

    // Set version to 0100
    $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
    // Set bits 6-7 to 10
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80);

    // Output the 36 character UUID.
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}

生成的无效JWT

eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjB9.ZmIxNzIyN2Q2ZjFhYjg3ZTJjMTY0NDJkNGQ4NzFlYWFmMjFhYzg1NzI5NGRkOGVhZmY4MTYzNWI1YTMyYWEyN2UxOTFmN2E5MzA1ZTZjZmI0OGVlZmMwN2U2NTc1MzNhZDg0NmMxMTZhZDZlOGVlYjJmMGVmOWUxOTMyYzE5MmE

该JWT在jwt.io及本地解码时均被判定为签名无效。

错误原因

核心问题出在hash_hmac函数的第四个参数:

  • 你传入了false,函数返回十六进制字符串格式的哈希值
  • JWT规范要求签名必须是原始二进制哈希数据经过base64url编码,而非十六进制字符串的编码

修正后的代码

只需修改hash_hmac的第四个参数为true,获取二进制原始哈希值,其余逻辑保持不变:

function gen_jwt():String{

    $signing_key = "changeme";
    
    $header = [ 
        "alg" => "HS512", 
        "typ" => "JWT" 
    ];
    $header = base64_url_encode(json_encode($header));
    
    $payload =  [
        "exp" => 0,
    ];
    $payload = base64_url_encode(json_encode($payload));
    
    // 关键修改:第四个参数改为true,获取二进制原始哈希值
    $signature = hash_hmac('sha512', "$header.$payload", $signing_key, true);
    $signature = base64_url_encode($signature);
    
    $jwt = "$header.$payload.$signature";

    return $jwt;
    
}

function base64_url_encode($text):String{
    return str_replace(
        ['+', '/', '='],
        ['-', '_', ''],
        base64_encode($text)
    );
}

验证结果

修正后生成的JWT为:
eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjB9.pqzfdCTmr-eWW9sEgV-COCdS4dI7MDpCIFWss6kXnAC9eLdGX1qOOr8BtJih59o_U_AdHtBh8JwUQ4dEPTk0rg

这个JWT可以通过jwt.io的验证(注意不要勾选"secret base64 encoded"选项,因为你的密钥是明文字符串)。


内容的提问来源于stack exchange,提问作者cloudsurfin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 15:48:06