【问题标题】:JWT signature verificationJWT 签名验证
【发布时间】:2016-08-28 18:27:45
【问题描述】:

我正在尝试使用从身份验证服务器 (OpenId) 检索到的公钥来验证访问令牌签名。
客户端从同一服务器获取访问令牌,然后用它请求我的资源服务器 API。现在我必须使用 Spring Security 库检查它的签名。
访问令牌具有“alg”:“RS256”属性。
但是下面的代码仍然不成功,我总是得到 InvalidSignatureException...

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.InvalidSignatureException;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;

public class JWTValidation {

    private static final Logger logger = LoggerFactory.getLogger(JWTValidation.class);

    private static final String PUBLIC_KEY_MODULUS = "qOYyKKnoUpXd2qIj8A0tdumWwnDbVjXOVaPfiX5lxBvYEtgWPLknf1Nftdk371a7f1jD8SFFDxXnj-PPFx8qoNETOITvbR12uvWmS1J36B5Uo_ViHp7dC-GaZG_EdafyK0rxRPvK8b37NPXWhTggbxCZhYaqJUMb1t0xogDadEyM95lZweEXrwsJNzoyXiGnPfsRgy32TjOOXIMZnAMoj-osYd2WawymkRV6cteo3f8KMT72_kp8oG-kGm1s3ZooEfI3_9Z2jHVGWQLUWbmZKIrvjuUo2dhmqWWsNyTO3RsF4qyrRCpmZNawDf_GsioBTZ3vfPF_T58moH7cJ50Byw";
    private static final String PUBLIC_KEY_PUBLIC_EXPONENT = "AQAB";

    //Public key =
//  {
//      "keys":[
//          {
//            "kty":"RSA",
//            "use":"sig",
//            "kid":"DQr-GCc8rH3y5fkAuo0iau-ue-s",
//            "x5t":"DQr-GCc8rH3y5fkAuo0iau-ue-s",
//            "e":"AQAB",
//            "n":"qOYyKKnoUpXd2qIj8A0tdumWwnDbVjXOVaPfiX5lxBvYEtgWPLknf1Nftdk371a7f1jD8SFFDxXnj-PPFx8qoNETOITvbR12uvWmS1J36B5Uo_ViHp7dC-GaZG_EdafyK0rxRPvK8b37NPXWhTggbxCZhYaqJUMb1t0xogDadEyM95lZweEXrwsJNzoyXiGnPfsRgy32TjOOXIMZnAMoj-osYd2WawymkRV6cteo3f8KMT72_kp8oG-kGm1s3ZooEfI3_9Z2jHVGWQLUWbmZKIrvjuUo2dhmqWWsNyTO3RsF4qyrRCpmZNawDf_GsioBTZ3vfPF_T58moH7cJ50Byw",
//            "x5c":["MIIDBDCCAfCgAwIBAgIQt1HpvYkM6oxJ1ZjbpW1fPTAJBgUrDgMCHQUAMBkxFzAVBgNVBAMTDkRhdGFEb29ycyBUZXN0MCAXDTE1MDQwMjIyMjQwM1oYDzIwNTAwMTAxMDYwMDAwWjAZMRcwFQYDVQQDEw5EYXRhRG9vcnMgVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKjmMiip6FKV3dqiI/ANLXbplsJw21Y1zlWj34l+ZcQb2BLYFjy5J39TX7XZN+9Wu39Yw/EhRQ8V54/jzxcfKqDREziE720ddrr1pktSd+geVKP1Yh6e3QvhmmRvxHWn8itK8UT7yvG9+zT11oU4IG8QmYWGqiVDG9bdMaIA2nRMjPeZWcHhF68LCTc6Ml4hpz37EYMt9k4zjlyDGZwDKI/qLGHdlmsMppEVenLXqN3/CjE+9v5KfKBvpBptbN2aKBHyN//Wdox1RlkC1Fm5mSiK747lKNnYZqllrDckzt0bBeKsq0QqZmTWsA3/xrIqAU2d73zxf0+fJqB+3CedAcsCAwEAAaNOMEwwSgYDVR0BBEMwQYAQgtiIGHLzFEskZSe/65EOTqEbMBkxFzAVBgNVBAMTDkRhdGFEb29ycyBUZXN0ghC3Uem9iQzqjEnVmNulbV89MAkGBSsOAwIdBQADggEBADtIlf41MLeGwjTbhJS88stZEBhEexxXNJDlW92GKPVv0JJWD/5m8tfADzXOgP65rTyQ4lTGOFFRYQu0ajMYAzggqJTmU1rMrHxuVwLfJ3OpSOc9UZBs2gW/IUZFvSugMKNboTsfTPgpsHK1ag68NKvR/V209zYZd6A7zisGgUr2Oc5jNEj7lSQY6pME2ZXU0YppC6Dctj8XHkTO9Ji9vDj+iGoS4+RvHZ3cDr5YUbOKSooOAZ/kjqtm+VK2jdjdLrkduz/24NKIxEXQqhmM28f8kh5Wc2ilaMya9pxQZpTWk7sjOBkZCcw24tx6UqpSTsV/XnnTmJMIhgXFJXWnXFc="]
//          }
//      ]
//  }

    //Access Token = base64url encoded String
    public boolean verifySignature(String accessToken){

        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");          
            Base64 decoder = new Base64(true);//URL-safe Base64 decoder

            BigInteger modulus = new BigInteger(decoder.decode(PUBLIC_KEY_MODULUS.getBytes()));
            BigInteger publicExponent = new BigInteger(decoder.decode(PUBLIC_KEY_PUBLIC_EXPONENT.getBytes()));

            RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
            PublicKey newPublicKey = keyFactory.generatePublic(spec);

            RsaVerifier verif = new RsaVerifier((RSAPublicKey) newPublicKey, "SHA256withRSA");
            JwtHelper.decodeAndVerify(accessToken, verif);

        } catch (InvalidSignatureException e){
            logger.info(e.getMessage());
            return false;
        } catch (Exception e){
            logger.info(e.getMessage());
            return false;
        }

        return true;
    }
}

我也尝试使用在线工具jwt.io,但我无法使其工作(签名仍然无效)
而对于另一个(tool_jwt),获得有效签名的唯一方法是选择“默认 X.509 证书 RSA”,并在我的公钥“x5c”值周围使用 cmets:

-----BEGIN CERTIFICATE-----
    MIIDBDCCAfCgAwIBAgIQt1HpvYkM6oxJ1ZjbpW1fPTAJBgUrDgMCHQUAMBkxFzAVBgNVBAMTDkRhdGFEb29ycyBUZXN0MCAXDTE1MDQwMjIyMjQwM1oYDzIwNTAwMTAxMDYwMDAwWjAZMRcwFQYDVQQDEw5EYXRhRG9vcnMgVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKjmMiip6FKV3dqiI/ANLXbplsJw21Y1zlWj34l+ZcQb2BLYFjy5J39TX7XZN+9Wu39Yw/EhRQ8V54/jzxcfKqDREziE720ddrr1pktSd+geVKP1Yh6e3QvhmmRvxHWn8itK8UT7yvG9+zT11oU4IG8QmYWGqiVDG9bdMaIA2nRMjPeZWcHhF68LCTc6Ml4hpz37EYMt9k4zjlyDGZwDKI/qLGHdlmsMppEVenLXqN3/CjE+9v5KfKBvpBptbN2aKBHyN//Wdox1RlkC1Fm5mSiK747lKNnYZqllrDckzt0bBeKsq0QqZmTWsA3/xrIqAU2d73zxf0+fJqB+3CedAcsCAwEAAaNOMEwwSgYDVR0BBEMwQYAQgtiIGHLzFEskZSe/65EOTqEbMBkxFzAVBgNVBAMTDkRhdGFEb29ycyBUZXN0ghC3Uem9iQzqjEnVmNulbV89MAkGBSsOAwIdBQADggEBADtIlf41MLeGwjTbhJS88stZEBhEexxXNJDlW92GKPVv0JJWD/5m8tfADzXOgP65rTyQ4lTGOFFRYQu0ajMYAzggqJTmU1rMrHxuVwLfJ3OpSOc9UZBs2gW/IUZFvSugMKNboTsfTPgpsHK1ag68NKvR/V209zYZd6A7zisGgUr2Oc5jNEj7lSQY6pME2ZXU0YppC6Dctj8XHkTO9Ji9vDj+iGoS4+RvHZ3cDr5YUbOKSooOAZ/kjqtm+VK2jdjdLrkduz/24NKIxEXQqhmM28f8kh5Wc2ilaMya9pxQZpTWk7sjOBkZCcw24tx6UqpSTsV/XnnTmJMIhgXFJXWnXFc=
-----END CERTIFICATE-----

所以我现在不知道该做什么,我应该使用哪个公钥属性,以及如何使其工作?

非常感谢您的帮助:)

【问题讨论】:

    标签: java spring spring-security oauth-2.0 jwt


    【解决方案1】:

    除了 RSA 密钥规范外,我还使用了 x509 密钥规范

    RSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(modulusBytes), new BigInteger(exponentBytes));
    KeyFactory factory = KeyFactory.getInstance("RSA"); 
    PublicKey key = factory.generatePublic(spec);
    
    
    X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(key.getEncoded());
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PublicKey pubKey64 = kf.generatePublic(X509publicKey);
    

    这对 auth0 和 jwt.io 库都有效

    【讨论】:

      【解决方案2】:

      要使用 jwt.io 在线验证签名,您只需将以下 json 作为公钥放入:

      {
           "kty":"RSA",
           "kid":"DQr-GCc8rH3y5fkAuo0iau-ue-s",
           "e":"AQAB",
           "n":"qOYyKKnoUpXd2qIj8A0tdumWwnDbVjXOVaPfiX5lxBvYEtgWPLknf1Nftdk371a7f1jD8SFFDxXnj-PPFx8qoNETOITvbR12uvWmS1J36B5Uo_ViHp7dC-GaZG_EdafyK0rxRPvK8b37NPXWhTggbxCZhYaqJUMb1t0xogDadEyM95lZweEXrwsJNzoyXiGnPfsRgy32TjOOXIMZnAMoj-osYd2WawymkRV6cteo3f8KMT72_kp8oG-kGm1s3ZooEfI3_9Z2jHVGWQLUWbmZKIrvjuUo2dhmqWWsNyTO3RsF4qyrRCpmZNawDf_GsioBTZ3vfPF_T58moH7cJ50Byw"
      }
      

      【讨论】:

      • 谢谢!但这是为什么呢?你能详细说明一下吗?
      • 这就是的答案“我也尝试使用在线工具jwt.io,但我无法使其工作(签名仍然无效)”在原帖中。帖子中有所有必要的信息,唯一需要的操作是删除外部数组定义("keys":[])并将其余部分放入站点上的相应字段
      • jwt.io 的问题是缺少键的预期格式定义。第一次需要时,我花了一些时间找到合适的,而且我知道不仅我有这样的问题,这就是我把它放在这里的原因。希望它可以帮助某人使其工作。
      猜你喜欢
      • 2019-11-14
      • 2018-09-16
      • 2014-05-29
      • 2017-12-30
      • 2019-11-01
      • 2016-07-10
      • 2018-05-29
      • 2013-07-12
      相关资源
      最近更新 更多