Skip to content

Commit ce79ef2

Browse files
committed
Single-key Key Selector
Fixes: gh-7049 Fixes: gh-7056
1 parent 3b5a418 commit ce79ef2

File tree

5 files changed

+208
-52
lines changed

5 files changed

+208
-52
lines changed

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java

+4-16
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@
2929

3030
import com.nimbusds.jose.JWSAlgorithm;
3131
import com.nimbusds.jose.RemoteKeySourceException;
32-
import com.nimbusds.jose.jwk.JWKSet;
33-
import com.nimbusds.jose.jwk.RSAKey;
34-
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
35-
import com.nimbusds.jose.jwk.source.ImmutableSecret;
3632
import com.nimbusds.jose.jwk.source.JWKSource;
3733
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
3834
import com.nimbusds.jose.proc.JWSKeySelector;
@@ -316,17 +312,12 @@ public Resource retrieveResource(URL url) throws IOException {
316312
*/
317313
public static final class PublicKeyJwtDecoderBuilder {
318314
private JWSAlgorithm jwsAlgorithm;
319-
private RSAKey key;
315+
private RSAPublicKey key;
320316

321317
private PublicKeyJwtDecoderBuilder(RSAPublicKey key) {
322318
Assert.notNull(key, "key cannot be null");
323319
this.jwsAlgorithm = JWSAlgorithm.RS256;
324-
this.key = rsaKey(key);
325-
}
326-
327-
private static RSAKey rsaKey(RSAPublicKey publicKey) {
328-
return new RSAKey.Builder(publicKey)
329-
.build();
320+
this.key = key;
330321
}
331322

332323
/**
@@ -352,10 +343,8 @@ JWTProcessor<SecurityContext> processor() {
352343
this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512.");
353344
}
354345

355-
JWKSet jwkSet = new JWKSet(this.key);
356-
JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(jwkSet);
357346
JWSKeySelector<SecurityContext> jwsKeySelector =
358-
new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
347+
new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.key);
359348
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
360349
jwtProcessor.setJWSKeySelector(jwsKeySelector);
361350

@@ -414,9 +403,8 @@ public NimbusJwtDecoder build() {
414403
}
415404

416405
JWTProcessor<SecurityContext> processor() {
417-
JWKSource<SecurityContext> jwkSource = new ImmutableSecret<>(this.secretKey);
418406
JWSKeySelector<SecurityContext> jwsKeySelector =
419-
new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
407+
new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.secretKey);
420408
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
421409
jwtProcessor.setJWSKeySelector(jwsKeySelector);
422410

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java

+4-17
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,7 @@
3030
import com.nimbusds.jose.jwk.JWK;
3131
import com.nimbusds.jose.jwk.JWKMatcher;
3232
import com.nimbusds.jose.jwk.JWKSelector;
33-
import com.nimbusds.jose.jwk.JWKSet;
34-
import com.nimbusds.jose.jwk.RSAKey;
35-
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
36-
import com.nimbusds.jose.jwk.source.ImmutableSecret;
3733
import com.nimbusds.jose.jwk.source.JWKSecurityContextJWKSet;
38-
import com.nimbusds.jose.jwk.source.JWKSource;
3934
import com.nimbusds.jose.proc.BadJOSEException;
4035
import com.nimbusds.jose.proc.JWKSecurityContext;
4136
import com.nimbusds.jose.proc.JWSKeySelector;
@@ -318,20 +313,15 @@ private JWKSelector createSelector(Header header) {
318313
* @since 5.2
319314
*/
320315
public static final class PublicKeyReactiveJwtDecoderBuilder {
321-
private final RSAKey key;
316+
private final RSAPublicKey key;
322317
private JWSAlgorithm jwsAlgorithm;
323318

324319
private PublicKeyReactiveJwtDecoderBuilder(RSAPublicKey key) {
325320
Assert.notNull(key, "key cannot be null");
326-
this.key = rsaKey(key);
321+
this.key = key;
327322
this.jwsAlgorithm = JWSAlgorithm.RS256;
328323
}
329324

330-
private static RSAKey rsaKey(RSAPublicKey publicKey) {
331-
return new RSAKey.Builder(publicKey)
332-
.build();
333-
}
334-
335325
/**
336326
* Use the given signing
337327
* <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target="_blank">algorithm</a>.
@@ -363,10 +353,8 @@ Converter<JWT, Mono<JWTClaimsSet>> processor() {
363353
this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512.");
364354
}
365355

366-
JWKSet jwkSet = new JWKSet(this.key);
367-
JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(jwkSet);
368356
JWSKeySelector<SecurityContext> jwsKeySelector =
369-
new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
357+
new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.key);
370358
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
371359
jwtProcessor.setJWSKeySelector(jwsKeySelector);
372360

@@ -418,9 +406,8 @@ public NimbusReactiveJwtDecoder build() {
418406
}
419407

420408
Converter<JWT, Mono<JWTClaimsSet>> processor() {
421-
JWKSource<SecurityContext> jwkSource = new ImmutableSecret<>(this.secretKey);
422409
JWSKeySelector<SecurityContext> jwsKeySelector =
423-
new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
410+
new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.secretKey);
424411
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
425412
jwtProcessor.setJWSKeySelector(jwsKeySelector);
426413

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.jwt;
18+
19+
import java.security.Key;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
23+
import com.nimbusds.jose.JWSAlgorithm;
24+
import com.nimbusds.jose.JWSHeader;
25+
import com.nimbusds.jose.proc.JWSKeySelector;
26+
import com.nimbusds.jose.proc.SecurityContext;
27+
28+
import org.springframework.util.Assert;
29+
30+
/**
31+
* An internal implementation of {@link JWSKeySelector} that always returns the same key
32+
*
33+
* @author Josh Cummings
34+
* @since 5.2
35+
*/
36+
final class SingleKeyJWSKeySelector<C extends SecurityContext> implements JWSKeySelector<C> {
37+
private final List<Key> keySet;
38+
private final JWSAlgorithm expectedJwsAlgorithm;
39+
40+
SingleKeyJWSKeySelector(JWSAlgorithm expectedJwsAlgorithm, Key key) {
41+
Assert.notNull(expectedJwsAlgorithm, "expectedJwsAlgorithm cannot be null");
42+
Assert.notNull(key, "key cannot be null");
43+
this.keySet = Arrays.asList(key);
44+
this.expectedJwsAlgorithm = expectedJwsAlgorithm;
45+
}
46+
47+
@Override
48+
public List<? extends Key> selectJWSKeys(JWSHeader header, C context) {
49+
if (!this.expectedJwsAlgorithm.equals(header.getAlgorithm())) {
50+
throw new IllegalArgumentException("Unsupported algorithm of " + header.getAlgorithm());
51+
}
52+
return this.keySet;
53+
}
54+
}

oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestKeys.java

+76-1
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,93 @@
1515
*/
1616
package org.springframework.security.oauth2.jose;
1717

18+
import java.security.KeyFactory;
19+
import java.security.NoSuchAlgorithmException;
20+
import java.security.interfaces.RSAPrivateKey;
21+
import java.security.interfaces.RSAPublicKey;
22+
import java.security.spec.InvalidKeySpecException;
23+
import java.security.spec.PKCS8EncodedKeySpec;
24+
import java.security.spec.X509EncodedKeySpec;
25+
import java.util.Base64;
1826
import javax.crypto.SecretKey;
1927
import javax.crypto.spec.SecretKeySpec;
20-
import java.util.Base64;
2128

2229
/**
2330
* @author Joe Grandja
2431
* @since 5.2
2532
*/
2633
public class TestKeys {
34+
public static final KeyFactory kf;
35+
36+
static {
37+
try {
38+
kf = KeyFactory.getInstance("RSA");
39+
} catch (NoSuchAlgorithmException e) {
40+
throw new IllegalStateException(e);
41+
}
42+
}
43+
2744
public static final String DEFAULT_ENCODED_SECRET_KEY = "bCzY/M48bbkwBEWjmNSIEPfwApcvXOnkCxORBEbPr+4=";
2845

2946
public static final SecretKey DEFAULT_SECRET_KEY =
3047
new SecretKeySpec(Base64.getDecoder().decode(DEFAULT_ENCODED_SECRET_KEY), "AES");
3148

49+
public static final String DEFAULT_RSA_PUBLIC_KEY =
50+
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd" +
51+
"7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv" +
52+
"c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6" +
53+
"iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2" +
54+
"kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o" +
55+
"RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj" +
56+
"KwIDAQAB";
57+
58+
public static final RSAPublicKey DEFAULT_PUBLIC_KEY = publicKey();
59+
60+
private static RSAPublicKey publicKey() {
61+
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(DEFAULT_RSA_PUBLIC_KEY));
62+
try {
63+
return (RSAPublicKey) kf.generatePublic(spec);
64+
} catch (InvalidKeySpecException e) {
65+
throw new IllegalArgumentException(e);
66+
}
67+
}
68+
69+
public static final String DEFAULT_RSA_PRIVATE_KEY =
70+
"MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcWWomvlNGyQhA" +
71+
"iB0TcN3sP2VuhZ1xNRPxr58lHswC9Cbtdc2hiSbe/sxAvU1i0O8vaXwICdzRZ1JM" +
72+
"g1TohG9zkqqjZDhyw1f1Ic6YR/OhE6NCpqERy97WMFeW6gJd1i5inHj/W19GAbqK" +
73+
"LhSHGHqIjyo0wlBf58t+qFt9h/EFBVE/LAGQBsg/jHUQCxsLoVI2aSELGIw2oSDF" +
74+
"oiljwLaQl0n9khX5ZbiegN3OkqodzCYHwWyu6aVVj8M1W9RIMiKmKr09s/gf31Nc" +
75+
"3WjvjqhFo1rTuurWGgKAxJLL7zlJqAKjGWbIT4P6h/1Kwxjw6X23St3OmhsG6HIn" +
76+
"+jl1++MrAgMBAAECggEBAMf820wop3pyUOwI3aLcaH7YFx5VZMzvqJdNlvpg1jbE" +
77+
"E2Sn66b1zPLNfOIxLcBG8x8r9Ody1Bi2Vsqc0/5o3KKfdgHvnxAB3Z3dPh2WCDek" +
78+
"lCOVClEVoLzziTuuTdGO5/CWJXdWHcVzIjPxmK34eJXioiLaTYqN3XKqKMdpD0ZG" +
79+
"mtNTGvGf+9fQ4i94t0WqIxpMpGt7NM4RHy3+Onggev0zLiDANC23mWrTsUgect/7" +
80+
"62TYg8g1bKwLAb9wCBT+BiOuCc2wrArRLOJgUkj/F4/gtrR9ima34SvWUyoUaKA0" +
81+
"bi4YBX9l8oJwFGHbU9uFGEMnH0T/V0KtIB7qetReywkCgYEA9cFyfBIQrYISV/OA" +
82+
"+Z0bo3vh2aL0QgKrSXZ924cLt7itQAHNZ2ya+e3JRlTczi5mnWfjPWZ6eJB/8MlH" +
83+
"Gpn12o/POEkU+XjZZSPe1RWGt5g0S3lWqyx9toCS9ACXcN9tGbaqcFSVI73zVTRA" +
84+
"8J9grR0fbGn7jaTlTX2tnlOTQ60CgYEA5YjYpEq4L8UUMFkuj+BsS3u0oEBnzuHd" +
85+
"I9LEHmN+CMPosvabQu5wkJXLuqo2TxRnAznsA8R3pCLkdPGoWMCiWRAsCn979TdY" +
86+
"QbqO2qvBAD2Q19GtY7lIu6C35/enQWzJUMQE3WW0OvjLzZ0l/9mA2FBRR+3F9A1d" +
87+
"rBdnmv0c3TcCgYEAi2i+ggVZcqPbtgrLOk5WVGo9F1GqUBvlgNn30WWNTx4zIaEk" +
88+
"HSxtyaOLTxtq2odV7Kr3LGiKxwPpn/T+Ief+oIp92YcTn+VfJVGw4Z3BezqbR8lA" +
89+
"Uf/+HF5ZfpMrVXtZD4Igs3I33Duv4sCuqhEvLWTc44pHifVloozNxYfRfU0CgYBN" +
90+
"HXa7a6cJ1Yp829l62QlJKtx6Ymj95oAnQu5Ez2ROiZMqXRO4nucOjGUP55Orac1a" +
91+
"FiGm+mC/skFS0MWgW8evaHGDbWU180wheQ35hW6oKAb7myRHtr4q20ouEtQMdQIF" +
92+
"snV39G1iyqeeAsf7dxWElydXpRi2b68i3BIgzhzebQKBgQCdUQuTsqV9y/JFpu6H" +
93+
"c5TVvhG/ubfBspI5DhQqIGijnVBzFT//UfIYMSKJo75qqBEyP2EJSmCsunWsAFsM" +
94+
"TszuiGTkrKcZy9G0wJqPztZZl2F2+bJgnA6nBEV7g5PA4Af+QSmaIhRwqGDAuROR" +
95+
"47jndeyIaMTNETEmOnms+as17g==";
96+
97+
public static final RSAPrivateKey DEFAULT_PRIVATE_KEY = privateKey();
98+
99+
private static RSAPrivateKey privateKey() {
100+
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(DEFAULT_RSA_PRIVATE_KEY));
101+
try {
102+
return (RSAPrivateKey) kf.generatePrivate(spec);
103+
} catch (InvalidKeySpecException e) {
104+
throw new IllegalArgumentException(e);
105+
}
106+
}
32107
}

0 commit comments

Comments
 (0)