如何为spring oauth2 jwt服务器生成正确的jwks端点?

mjqavswn  于 5个月前  发布在  Spring
关注(0)|答案(2)|浏览(75)

下发目标

为我的spring oauth2 jwt服务器配置有效的jwks

第一次尝试

遵循spring文档,我可以使用JWK Set URI的现成端点。它需要:

@Import(AuthorizationServerEndpointsConfiguration.class)

字符串
我已经添加。通过执行器检查Map的端点没有过滤jw

第二次尝试

按照相同的配置,我尝试使用下一个代码:

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
...

@FrameworkEndpoint
class JwkSetEndpoint {
    KeyPair keyPair;

    public JwkSetEndpoint(KeyPair keyPair) {
        this.keyPair = keyPair;
    }

    @GetMapping("/.well-known/jwks.json")
    @ResponseBody
    public Map<String, Object> getKey(Principal principal) {
        RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic();
        RSAKey key = new RSAKey.Builder(publicKey).build();
        return new JWKSet(key).toJSONObject();
    }
}


它产生

{
  "keys" : [ {
    "kty" : "RSA",
    "e" : "AQAB",
    "n" : "mWI2jtKwvf0W1hdMdajch-mFx9FZe3CZnKNvT_d0-2O6V1Pgkz7L2FcQx2uoV7gHgk5mmb2MZUsy_rDKj0dMfLzyXqBcCRxD6avALwu8AAiGRxe2dl8HqIHyo7P4R1nUaea1WCZB_i7AxZNAQtcCcSvMvF2t33p3vYXY6SqMucMD4yHOTXexoWhzwRqjyyC8I8uCYJ-xIfQvaK9Q1RzKRj99IRa1qyNgdeHjkwW9v2Fd4O_Ln1Tzfnk_dMLqxaNsXPw37nw-OUhycFDPPQF_H4Q4-UDJ3ATf5Z2yQKkUQlD45OO2mIXjkWprAmOCi76dLB2yzhCX_plGJwcgb8XHEQ"
  } ]
}


使用access_token ping资源服务器失败:

{"error":"invalid_token","error_description":"Invalid JWT/JWS: kid is a required JOSE Header"}

第三次尝试

修改"/.well-known/jwks.json"jwt.io helps detect algorithm used for jwt)的响应:

RSAKey key = new RSAKey.Builder(publicKey)
                .keyID("1")
                .keyUse(KeyUse.SIGNATURE)
                .algorithm(JWSAlgorithm.RS256)
                .build();


导致下一个响应:

{
  "keys" : [ {
    "kty" : "RSA",
    "e" : "AQAB",
    "use" : "sig",
    "kid" : "1",
    "alg" : "RS256",
    "n" : "mWI2jtKwvf0W1hdMdajch-mFx9FZe3CZnKNvT_d0-2O6V1Pgkz7L2FcQx2uoV7gHgk5mmb2MZUsy_rDKj0dMfLzyXqBcCRxD6avALwu8AAiGRxe2dl8HqIHyo7P4R1nUaea1WCZB_i7AxZNAQtcCcSvMvF2t33p3vYXY6SqMucMD4yHOTXexoWhzwRqjyyC8I8uCYJ-xIfQvaK9Q1RzKRj99IRa1qyNgdeHjkwW9v2Fd4O_Ln1Tzfnk_dMLqxaNsXPw37nw-OUhycFDPPQF_H4Q4-UDJ3ATf5Z2yQKkUQlD45OO2mIXjkWprAmOCi76dLB2yzhCX_plGJwcgb8XHEQ"
  } ]
}


使用access_token ping资源服务器会得到相同的结果:

{"error":"invalid_token","error_description":"Invalid JWT/JWS: kid is a required JOSE Header"}

提问

有任何想法或例子如何配置/.well-known/jwks.json产生正确的jwks

附言

  • 如果我使用公钥作为资源服务器上的本地资源-它工作。
  • 我会很高兴任何工作解决方案(可能有人知道不同的jwks库,可以在spring-boot应用程序中使用)。
oxosxuxt

oxosxuxt1#

有什么新进展吗?
我们正在研究基于签名的身份验证,https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12
其中一个要求是实现/. well-known/jwks.json端点,这样我们就不必有单独的公钥分发机制。
我还没有这样做,但它看起来像:

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    return JWKSet.load(keyStore, (name) -> null).toPublicJWKSet();

字符串

uqcuzwp8

uqcuzwp82#

要生成JWKS端点,您可以使用一些好的库,如nimbus-jose-jwt,但也可以完全不使用外部库。
为此,您需要生成密钥文件:

ssh-keygen -t rsa -b 4096 -m PEM -f rs256.key

字符串
并转换为Java可以使用的内容:

openssl pkcs8 -topk8 -inform PEM -in rs256.key -out rs256.pem -nocrypt


然后可以使用以下代码生成JWKS端点响应:

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;

class JwksEndpoint {
    public static void main(String[] args) {

        // *******************************
        // Load private key data from file
        // *******************************
        String filePath = "rs256.pem";

        String privateKeyContent;
        try {
            privateKeyContent = new String(Files.readAllBytes(Paths.get(filePath))) 
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replace("\n", "");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // ******************
        // Create private key
        // ******************
        KeyFactory keyFactory;
        try {
            keyFactory = KeyFactory.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
            Base64.getDecoder().decode(privateKeyContent)
        );
        PrivateKey privateKey; // Use this key also to sign JWT!
        try {
            privateKey = keyFactory.generatePrivate(privateKeySpec);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }

        // *****************
        // Create public key
        // *****************
        RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(
            ((RSAPrivateCrtKey) privateKey).getModulus(),
            ((RSAPrivateCrtKey) privateKey).getPublicExponent()
        );

        PublicKey publicKey;
        try {
            publicKey = keyFactory.generatePublic(publicKeySpec);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
            
        // *********************
        // Prepare JWKS response
        // *********************
        RSAPublicKey rsa = (RSAPublicKey) publicKey;

        Map<String, Object> keyObject = new HashMap<>();
        keyObject.put("kty", rsa.getAlgorithm());
        keyObject.put("kid", "1");
        keyObject.put(
            "n",
            Base64.getUrlEncoder().encodeToString(rsa.getModulus().toByteArray())
        );
        keyObject.put(
            "e",
            Base64.getUrlEncoder().encodeToString(rsa.getPublicExponent().toByteArray())
        );
        keyObject.put("alg", "RS256");
        keyObject.put("use", "sig");

        List<Map<String, Object>> keyList = new ArrayList<>();
        keyList.add(keyObject);

        Map<String, List<Map<String, Object>>> jwksResponse = new HashMap<>();
        jwksResponse.put("keys", keyList);

        System.out.println(jwksResponse);
    }
}


你可能需要一些像org.json或Jackson这样的库来将JWKS数据序列化为JSON,除非你想使用StringBuilder。

相关问题