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

在PowerShell中生成Ghost博客JWT令牌遇签名无效问题,及C#十六进制转字节数组的优化方案咨询

解决Ghost Admin API的JWT签名无效问题(C# & PowerShell)

问题核心分析

你遇到的「Invalid token: invalid signature」错误,根源完全在密钥处理逻辑上。Ghost提供的$id:$secret格式里,secret是一个十六进制字符串,而你的PowerShell代码错误地把它当成ASCII字符串来处理,这和Ruby的[secret].pack('H*')(十六进制转字节数组)完全不是一回事。

先明确Ghost要求的JWT签名逻辑:

Ghost的JWT签名需要将十六进制格式的secret转换为原始字节数组,再用这个字节数组作为HMAC-SHA256的密钥来签名JWT内容。


PowerShell实现修正与优化

首先,你需要替换错误的密钥处理代码,正确将十六进制secret转为字节数组。另外,New-Jwt模块的-Secret参数如果接受字节数组的话,直接传入转换后的字节数组即可,不需要再做Base64相关操作。

修正后的完整PowerShell代码

# 拆分Ghost提供的id:secret令牌
$parts = $adminToken -split ":"
$id = $parts[0]
$hexSecret = $parts[1]

# 关键:将十六进制字符串转为字节数组(替代你之前错误的Base64处理)
function Convert-HexToByteArray {
    param([string]$Hex)
    if ($Hex.Length % 2 -ne 0) {
        throw "Hex string must have an even length."
    }
    $bytes = New-Object byte[] ($Hex.Length / 2)
    for ($i = 0; $i -lt $Hex.Length; $i += 2) {
        $bytes[$i/2] = [Convert]::ToByte($Hex.Substring($i, 2), 16)
    }
    return $bytes
}
$secretBytes = Convert-HexToByteArray -Hex $hexSecret

# 安装并导入JWT模块(如果未安装)
if (-not (Get-Module -ListAvailable -Name JWT)) {
    Install-Module -Name JWT -Force -Scope CurrentUser
}
Import-Module JWT

# 构建JWT Header和Payload(用-Compress减少冗余)
$header = @{
    alg = "HS256"
    kid = $id
    typ = "JWT"
} | ConvertTo-Json -Compress

$payload = @{
    exp = ([DateTimeOffset](Get-Date).AddMinutes(5)).ToUnixTimeSeconds()
    iat = ([DateTimeOffset](Get-Date)).ToUnixTimeSeconds()
    aud = "/admin/"
} | ConvertTo-Json -Compress

# 生成JWT令牌(注意这里传入字节数组作为Secret)
$jwtToken = New-Jwt -Header $header -PayloadJson $payload -Secret $secretBytes

# 调用Ghost Admin API,记得添加Content-Type头
$headers = @{
    "Authorization" = "Ghost $jwtToken"
    "Content-Type"  = "application/json"
}
Invoke-RestMethod -Uri "https://scrumbug.ghost.io/ghost/api/admin/pages/$($trainingPage.id)" -Method PUT -Body ($trainingPage | ConvertTo-Json -Compress) -Headers $headers

为什么之前的代码错了?

你的原代码$secret= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($secret)) | ConvertFrom-Base64UrlString | ConvertTo-Base64UrlString做了完全无关的操作:把十六进制字符串当成ASCII字符转成字节,再转Base64,这和Ghost要求的「十六进制直接转原始字节」逻辑完全不符,导致签名密钥错误,自然会触发INVALID_JWT。


C#实现优化

你自己写的ByteConvertor类是可行的,但在**.NET 5及以上版本**中,已经有内置的Convert.FromHexString方法可以直接完成十六进制字符串到字节数组的转换,不需要自己实现循环:

优化后的C#代码(使用官方JWT库)

首先,安装System.IdentityModel.Tokens.Jwt NuGet包,然后实现JWT生成:

using System;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;

public class GhostJwtGenerator
{
    public static string GenerateGhostAdminToken(string adminToken)
    {
        // 拆分id和secret
        var parts = adminToken.Split(':');
        var clientId = parts[0];
        var hexSecret = parts[1];
        
        // .NET 5+ 直接用内置方法转十六进制到字节数组
        var secretBytes = Convert.FromHexString(hexSecret);
        
        // 构建JWT签名凭据
        var securityKey = new SymmetricSecurityKey(secretBytes);
        var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
        
        // 配置JWT的Header和Payload
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            IssuedAt = DateTime.UtcNow,
            Expires = DateTime.UtcNow.AddMinutes(5),
            Audience = "/admin/",
            SigningCredentials = signingCredentials,
            Header = new JwtHeader
            {
                {"alg", SecurityAlgorithms.HmacSha256},
                {"kid", clientId},
                {"typ", "JWT"}
            }
        };
        
        // 生成并返回JWT令牌
        var tokenHandler = new JwtSecurityTokenHandler();
        var jwtToken = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(jwtToken);
    }
}

兼容旧.NET版本的实现

如果你用的是.NET Framework或.NET Core 3.1及以下,没有Convert.FromHexString,可以简化你原有的ByteConvertor类,增加参数校验让它更健壮:

public static class ByteConvertor
{
    public static byte[] StringToByteArray(string hex)
    {
        if (string.IsNullOrWhiteSpace(hex))
            throw new ArgumentNullException(nameof(hex), "Hex string cannot be null or empty.");
        if (hex.Length % 2 != 0)
            throw new ArgumentException("Hex string must have an even length.", nameof(hex));
            
        byte[] bytes = new byte[hex.Length / 2];
        for (int i = 0; i < hex.Length; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }
}

验证要点

不管用PowerShell还是C#,生成JWT后可以用在线JWT验证工具检查:

  1. 粘贴生成的JWT令牌
  2. 在验证签名区域,使用原始的十六进制secret(注意选择「非Base64编码密钥」选项)
  3. 确认Header的alg为HS256、kid为你的client id,Payload的aud为/admin/,且签名验证通过

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 15:42:32