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('\\=+$', ''); } }
验证步骤
- 使用转换后的PKCS#8格式私钥传入方法
- 生成JWT后,在JWT签名验证工具中粘贴令牌
- 上传对应的公钥(从crt文件提取)到验证工具的公钥区域,验证签名是否有效
内容的提问来源于stack exchange,提问作者user15754268
相关产品推荐
相关产品推荐

