Skip to content

Commit 2df1e47

Browse files
committed
Introduce RSA keys configuration properties for OAuth2 Authorization Server
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
1 parent 0c572ed commit 2df1e47

File tree

5 files changed

+140
-9
lines changed

5 files changed

+140
-9
lines changed

module/spring-boot-security-oauth2-authorization-server/src/main/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerJwtAutoConfiguration.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@
3535
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3636
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
3737
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
38+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3839
import org.springframework.boot.security.autoconfigure.servlet.UserDetailsServiceAutoConfiguration;
3940
import org.springframework.context.annotation.Bean;
4041
import org.springframework.context.annotation.Configuration;
4142
import org.springframework.context.annotation.Role;
43+
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
4244
import org.springframework.security.oauth2.jwt.JwtDecoder;
4345
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
4446
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
@@ -48,29 +50,44 @@
4850
* OAuth2 authorization server that require it (e.g. User Info, Client Registration).
4951
*
5052
* @author Steve Riesenberg
53+
* @author Yanming Zhou
5154
* @since 4.0.0
5255
*/
5356
@AutoConfiguration(after = UserDetailsServiceAutoConfiguration.class)
5457
@ConditionalOnClass({ OAuth2Authorization.class, JWKSource.class })
5558
@ConditionalOnWebApplication(type = Type.SERVLET)
59+
@EnableConfigurationProperties(OAuth2AuthorizationServerProperties.class)
5660
public final class OAuth2AuthorizationServerJwtAutoConfiguration {
5761

5862
@Bean
5963
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
6064
@ConditionalOnMissingBean
61-
JWKSource<SecurityContext> jwkSource() {
62-
RSAKey rsaKey = getRsaKey();
65+
JWKSource<SecurityContext> jwkSource(OAuth2AuthorizationServerProperties properties) {
66+
RSAKey rsaKey = getRsaKey(properties.getRsa());
6367
JWKSet jwkSet = new JWKSet(rsaKey);
6468
return new ImmutableJWKSet<>(jwkSet);
6569
}
6670

67-
private static RSAKey getRsaKey() {
68-
KeyPair keyPair = generateRsaKey();
69-
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
70-
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
71-
RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey)
72-
.keyID(UUID.randomUUID().toString())
73-
.build();
71+
@Bean
72+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
73+
@ConditionalOnMissingBean
74+
static RsaKeyConversionServicePostProcessor rsaKeyConversionServicePostProcessor() {
75+
return new RsaKeyConversionServicePostProcessor();
76+
}
77+
78+
private static RSAKey getRsaKey(OAuth2AuthorizationServerProperties.Rsa rsa) {
79+
RSAKey rsaKey;
80+
if (rsa.getPublicKey() != null && rsa.getPrivateKey() != null) {
81+
rsaKey = new RSAKey.Builder(rsa.getPublicKey()).privateKey(rsa.getPrivateKey())
82+
.keyID(rsa.getKeyId() != null ? rsa.getKeyId() : UUID.randomUUID().toString())
83+
.build();
84+
}
85+
else {
86+
KeyPair keyPair = generateRsaKey();
87+
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
88+
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
89+
rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).keyID(UUID.randomUUID().toString()).build();
90+
}
7491
return rsaKey;
7592
}
7693

module/spring-boot-security-oauth2-authorization-server/src/main/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerProperties.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.security.oauth2.server.authorization.autoconfigure.servlet;
1818

19+
import java.security.interfaces.RSAPrivateKey;
20+
import java.security.interfaces.RSAPublicKey;
1921
import java.time.Duration;
2022
import java.util.HashMap;
2123
import java.util.HashSet;
@@ -35,6 +37,7 @@
3537
*
3638
* @author Steve Riesenberg
3739
* @author Florian Lemaire
40+
* @author Yanming Zhou
3841
* @since 4.0.0
3942
*/
4043
@ConfigurationProperties("spring.security.oauth2.authorizationserver")
@@ -62,6 +65,11 @@ public class OAuth2AuthorizationServerProperties implements InitializingBean {
6265
*/
6366
private final Endpoint endpoint = new Endpoint();
6467

68+
/**
69+
* Authorization Server endpoints.
70+
*/
71+
private final Rsa rsa = new Rsa();
72+
6573
public boolean isMultipleIssuersAllowed() {
6674
return this.multipleIssuersAllowed;
6775
}
@@ -86,6 +94,10 @@ public Endpoint getEndpoint() {
8694
return this.endpoint;
8795
}
8896

97+
public Rsa getRsa() {
98+
return this.rsa;
99+
}
100+
89101
@Override
90102
public void afterPropertiesSet() {
91103
validate();
@@ -567,4 +579,50 @@ public void setIdTokenSignatureAlgorithm(String idTokenSignatureAlgorithm) {
567579

568580
}
569581

582+
/**
583+
* RSA keys for JWK.
584+
*/
585+
public static class Rsa {
586+
587+
/**
588+
* RSA key ID.
589+
*/
590+
private @Nullable String keyId;
591+
592+
/**
593+
* RSA public key.
594+
*/
595+
private @Nullable RSAPublicKey publicKey;
596+
597+
/**
598+
* RSA private key.
599+
*/
600+
private @Nullable RSAPrivateKey privateKey;
601+
602+
public @Nullable String getKeyId() {
603+
return this.keyId;
604+
}
605+
606+
public void setKeyId(String keyId) {
607+
this.keyId = keyId;
608+
}
609+
610+
public @Nullable RSAPublicKey getPublicKey() {
611+
return this.publicKey;
612+
}
613+
614+
public void setPublicKey(RSAPublicKey publicKey) {
615+
this.publicKey = publicKey;
616+
}
617+
618+
public @Nullable RSAPrivateKey getPrivateKey() {
619+
return this.privateKey;
620+
}
621+
622+
public void setPrivateKey(RSAPrivateKey privateKey) {
623+
this.privateKey = privateKey;
624+
}
625+
626+
}
627+
570628
}

module/spring-boot-security-oauth2-authorization-server/src/test/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerJwtAutoConfigurationTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.security.oauth2.server.authorization.autoconfigure.servlet;
1818

19+
import com.nimbusds.jose.jwk.JWKSet;
20+
import com.nimbusds.jose.jwk.RSAKey;
1921
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
2022
import com.nimbusds.jose.jwk.source.JWKSource;
2123
import com.nimbusds.jose.proc.SecurityContext;
@@ -36,6 +38,7 @@
3638
* Tests for {@link OAuth2AuthorizationServerJwtAutoConfiguration}.
3739
*
3840
* @author Steve Riesenberg
41+
* @author Yanming Zhou
3942
*/
4043
class OAuth2AuthorizationServerJwtAutoConfigurationTests {
4144

@@ -91,6 +94,22 @@ void jwkSourceBacksOffWhenBeanPresent() {
9194
});
9295
}
9396

97+
@Test
98+
void jwkSetShouldUseConfiguredRsaKeys() {
99+
this.contextRunner
100+
.withPropertyValues("spring.security.oauth2.authorizationserver.rsa.key-id=test",
101+
"spring.security.oauth2.authorizationserver.rsa.public-key=classpath:rsa/public.pem",
102+
"spring.security.oauth2.authorizationserver.rsa.private-key=classpath:rsa/private.pem")
103+
.run((context) -> {
104+
OAuth2AuthorizationServerProperties.Rsa rsa = context.getBean(OAuth2AuthorizationServerProperties.class)
105+
.getRsa();
106+
JWKSet jwkSet = context.getBean(ImmutableJWKSet.class).getJWKSet();
107+
RSAKey rsaKey = jwkSet.getKeyByKeyId("test").toRSAKey();
108+
assertThat(rsaKey.toRSAPublicKey()).isEqualTo(rsa.getPublicKey());
109+
assertThat(rsaKey.toRSAPrivateKey()).isEqualTo(rsa.getPrivateKey());
110+
});
111+
}
112+
94113
@Configuration
95114
static class TestJwtDecoderConfiguration {
96115

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCo9lH30na5xR21
3+
XociQCQyXvMNwaIs9NnvfYnXp5WdDtSGQwO1eR+nUODO5znQihkXRQ33aP8zyLJD
4+
cD5PQeeWRvj2qRSpG6UsKZ/GRZdkysCiCHL5pBKZJtEtFXpoY0K4HujSuBVz39W8
5+
txYmLCXS1s3vmOFuBbvQIieFHkdYRlyybm2L6uSj6GpNJfNVtn27gTV9l0boB/Gf
6+
9ruEHc5AwfNcMoD13aOJyWbVr3QtqrV6SVNN9qdvDZCFTi4NQtleYl8xGWXJsti+
7+
N3yje0qDD96uyrk7nN8D4reIcL8OYOVPS6HlV5RtrIVFeDKQ1faKTitt3qq864uN
8+
0ZeVr4G3AgMBAAECggEAPtLXx33KB2jzkuyC8olG7DPBy+ujkXO5VQMorbbyOmO5
9+
QfLI/kD7NAsui8AODyxKCAz1FHlF6stE/S5O/MlUgtwA1jYoKHjPAYy4i9B0alW5
10+
KoZZudj30VpNjKXfzdCajjtv9mncECm6H2E0Kx1fMvYLvHrr1yzqmIkaiLSpcnic
11+
XO4THiVEnPgLJx6ecRYnHRX+KVMS4a3KwMIBr5vvCjOFa+i45WKBHpUE0ykCbAID
12+
1uu+HMAcBJrjV5YeH97tuei4Od5/BMFaounN14R82TXqQ4i7K8F5AsDBn9jozouU
13+
N1QH8a3DSIjkickrMI/w3mQ0VJaLVK6GPGmU30kuAQKBgQDXa684CMgV4yA2O/nm
14+
e7/9BmSNeYo+VKdqKDNWkvE6cS6wf3o0R8jpwcdqrltLCRvjZWSDGNEIquY6eHXU
15+
+apSNEN4HBth2hNXqOTLBcGWqPxATeEzKeUbY8fHe38E0BBJ6aNzSHrenHwjCwn8
16+
uYOLXjT+pzlltqpaQT2SN65h8QKBgQDIykBNFZ8qK43KhArg0Kty498EeNwozIU1
17+
yqpSxX70kNnc1oUmYCLG4X4qy+kygMrUglMG6l2rfdHc//iPpIUODCIS7RWfYmyU
18+
XmUs+tKIrVX6OWE3hj0tTybTeBMWHCWMEgeqGTAyzzZMLi8f4yOsvp5w7Ycut/1W
19+
gAP0dxH2JwKBgBYniPmmTY2Ssjlhqa2+hFwtUCIMod8PLbiJMd5xdkWgZkDYm2TN
20+
DSidOTkLfXAWG7wjLVceMkFF8i+JO/UPSCj0Hww3N8m0d9DIGd+XU/V+o5Kpb8On
21+
R1ytwloNpV6FV2eCk8DDb399cHbaJ8jJ+3FV2vVllU2Un6hwlTh4aYLBAoGAekAa
22+
ElTdybEm7Wyjqumh2ZvAB1sGwJh1aqDwPuEcQQ+IdhruisTxp6FXTftFCoi79dM7
23+
dfRv/5/ljOcUkXCbyke830UWaypj7ZnjhBVa5fiTZnxVIpdK3DFa9FohVM7iVXwM
24+
ypX3cJgU+SENdB65c83DbgJQ0jMXvfjHb6qndvUCgYEAk87No0D8UeIfJ/QmX/+W
25+
KD5YEbzeKdGyx9r5XMc1ZKvJESehp+13P3l/h7t/83Fh9/BtoQ0bkiMLaoxgGb7k
26+
GHG9x2prkzVIKOEDan+kvQgHb1rcpKdWlKBhjnk+uf6o2J8Tb8iHqcpHhn96yAnp
27+
a5ubNn7ip2NUFLwJGS0Vqzk=
28+
-----END PRIVATE KEY-----
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqPZR99J2ucUdtV6HIkAk
3+
Ml7zDcGiLPTZ732J16eVnQ7UhkMDtXkfp1Dgzuc50IoZF0UN92j/M8iyQ3A+T0Hn
4+
lkb49qkUqRulLCmfxkWXZMrAoghy+aQSmSbRLRV6aGNCuB7o0rgVc9/VvLcWJiwl
5+
0tbN75jhbgW70CInhR5HWEZcsm5ti+rko+hqTSXzVbZ9u4E1fZdG6Afxn/a7hB3O
6+
QMHzXDKA9d2jiclm1a90Laq1eklTTfanbw2QhU4uDULZXmJfMRllybLYvjd8o3tK
7+
gw/ersq5O5zfA+K3iHC/DmDlT0uh5VeUbayFRXgykNX2ik4rbd6qvOuLjdGXla+B
8+
twIDAQAB
9+
-----END PUBLIC KEY-----

0 commit comments

Comments
 (0)