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
相关产品推荐
相关产品推荐

