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

Salesforce生成JWT令牌遇Invalid Signature错误排查求助

JWT签名验证失败(Invalid Signature)问题排查与修复

可能的问题点及修复方案

1. 时间戳字段类型错误

JWT标准要求exp(过期时间)和iat(签发时间)必须是数字类型(Unix时间戳整数),但你的代码中将这两个字段转成了字符串类型,这会导致JWT的Payload结构不符合标准,进而引发签名验证失败。

修复代码:
将claimSet中的exp和iat改为直接使用数值,去掉String.valueOf():

Map<String, Object> claimSet = new Map<String, Object>{
    'iss' => iss,
    'sub' => sub,
    'aud' => aud,
    'exp' => DateTime.now().getTime()/1000 + 300, // 直接用数值
    'iat' => DateTime.now().getTime()/1000 // 直接用数值
};

2. 私钥格式不匹配

OpenSSL默认生成的私钥是PKCS#1格式,而Salesforce Crypto.sign()方法要求的是PKCS#8格式的私钥。如果私钥格式不对,签名会无效。

验证与修复:

  • 检查你的私钥开头:PKCS#1格式是-----BEGIN RSA PRIVATE KEY-----,PKCS#8是-----BEGIN PRIVATE KEY-----
  • 如果是PKCS#1格式,用以下OpenSSL命令转换为PKCS#8:
openssl pkcs8 -topk8 -inform PEM -in SSO_Self_Signed.key -outform PEM -nocrypt -out SSO_Self_Signed_PKCS8.key
  • 将转换后的PKCS#8格式私钥传入APEX方法

3. 私钥处理逻辑优化

你的代码中只删除了私钥的首尾标记和空白字符,建议明确清理所有非Base64字符(包括换行、制表符等),确保私钥是纯净的Base64字符串。

优化后的私钥处理代码:

privateKey = privateKey.replaceAll('-----BEGIN PRIVATE KEY-----', '')
                       .replaceAll('-----END PRIVATE KEY-----', '')
                       .replaceAll('\\s+', ''); // 匹配所有空白字符

4. Base64URL编码验证

你的base64UrlEncode方法逻辑是正确的,但可以添加日志验证编码后的Header和Payload是否符合JWT标准:

  • 编码后的Header和Payload不能包含+、/,末尾不能有=
  • 可以在JWT签名验证工具中分别粘贴编码后的Header和Payload,确认解析后的内容与预期一致

完整修复后的APEX代码

public class SSOJWTGenerator {
    public static String generateJWT(String iss, String sub, String aud, String privateKey) {
        String alg = 'RS256';
        String typ = 'JWT';

        // 创建JWT头部
        Map<String, Object> header = new Map<String, Object>{
            'alg' => alg,
            'typ' => typ
        };
        String encodedHeader = base64UrlEncode(JSON.serialize(header));

        // 创建JWT载荷,修正时间戳类型为数值
        Map<String, Object> claimSet = new Map<String, Object>{
            'iss' => iss,
            'sub' => sub,
            'aud' => aud,
            'exp' => DateTime.now().getTime()/1000 + 300,
            'iat' => DateTime.now().getTime()/1000
        };
        String encodedClaimSet = base64UrlEncode(JSON.serialize(claimSet));

        // 生成签名输入内容
        String input = encodedHeader + '.' + encodedClaimSet;
        
        // 清理私钥格式
        privateKey = privateKey.replaceAll('-----BEGIN PRIVATE KEY-----', '')
                               .replaceAll('-----END PRIVATE KEY-----', '')
                               .replaceAll('\\s+', '');
        
        Blob privateKeyBlob = EncodingUtil.base64Decode(privateKey);
        Blob signatureBlob = Crypto.sign('RSA-SHA256', Blob.valueOf(input), privateKeyBlob);
        String signature = base64UrlEncode(signatureBlob);

        // 组合生成JWT令牌
        String jwtToken = encodedHeader + '.' + encodedClaimSet + '.' + signature;
        System.debug('生成的JWT令牌: ' + jwtToken);
        return jwtToken;
    }

    private static String base64UrlEncode(String input) {
        String base64 = EncodingUtil.base64Encode(Blob.valueOf(input));
        return base64.replace('+', '-').replace('/', '_').replaceAll('\\=+$', '');
    }

    private static String base64UrlEncode(Blob input) {
        String base64 = EncodingUtil.base64Encode(input);
        return base64.replace('+', '-').replace('/', '_').replaceAll('\\=+$', '');
    }
}

验证步骤

  1. 使用转换后的PKCS#8格式私钥传入方法
  2. 生成JWT后,在JWT签名验证工具中粘贴令牌
  3. 上传对应的公钥(从crt文件提取)到验证工具的公钥区域,验证签名是否有效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:45:15